using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using Strobotnik.Klattersynth; using UnityEngine; using UnityEngine.AI; using UnityEngine.Audio; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BertramBuddyBot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Bertram Buddy Bot - autonomous bot player & NPC teammate for R.E.P.O.")] [assembly: AssemblyFileVersion("3.9.1.0")] [assembly: AssemblyInformationalVersion("3.9.1")] [assembly: AssemblyProduct("BertramBuddyBot")] [assembly: AssemblyTitle("BertramBuddyBot")] [assembly: AssemblyVersion("3.9.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BotSpike { public static class BotAggro { private const float Damping = 0.35f; private static bool _warnedStuckTimer; private const float CycleSeconds = 1f; private const float ReRegisterEvery = 3f; private static float _registerAt; private static int _lastCount = -1; public static void Reset() { _registerAt = 0f; _lastCount = -1; } public static void Tick(PlayerAvatar bot, float dt) { if (!((Object)(object)bot == (Object)null)) { KeepRegistered(bot); Damp(bot); } } private static void KeepRegistered(PlayerAvatar bot) { if (Time.unscaledTime < _registerAt) { return; } _registerAt = Time.unscaledTime + 3f; try { if ((Object)(object)EnemyDirector.instance == (Object)null) { return; } PhotonView component = ((Component)bot).GetComponent(); if ((Object)(object)component == (Object)null) { return; } int viewID = component.ViewID; int num = 0; foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if ((Object)(object)item == (Object)null) { continue; } object obj = Reflect.Get(item, "Enemy"); if (obj == null) { continue; } try { MethodInfo methodInfo = AccessTools.Method(obj.GetType(), "PlayerAdded", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(obj, new object[1] { viewID }); num++; } } catch { } } if (num != _lastCount) { Plugin.Log.LogInfo((object)("[Aggro] He is now known to " + num + " enemies (was " + ((_lastCount < 0) ? "none" : _lastCount.ToString()) + ").")); _lastCount = num; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Aggro] Re-register: " + ex.Message)); } } private static void Damp(PlayerAvatar bot) { try { if (Reflect.Get(bot, "isDisabled")) { return; } bool num = Time.time % 1f < 0.35f; float num2 = Reflect.Get(bot, "enemyVisionFreezeTimer"); if (num) { if (num2 < 0.05f) { Reflect.Set(bot, "enemyVisionFreezeTimer", 0.06f); } } else if (num2 > 0f) { if (num2 > 0.2f && !_warnedStuckTimer) { _warnedStuckTimer = true; Plugin.Log.LogWarning((object)("[Aggro] His enemyVisionFreezeTimer was stuck at " + num2.ToString("F2") + " — the game had stopped decaying it, so every enemy was skipping him entirely. Clearing it myself from now on. If you see this, his PlayerAvatar.Update is throwing somewhere.")); } Reflect.Set(bot, "enemyVisionFreezeTimer", 0f); } } catch { } } [HarmonyPatch(typeof(EnemyVision), "Awake")] [HarmonyPostfix] public static void VisionAwakePostfix(EnemyVision __instance) { try { PlayerAvatar botAvatar = BotSpawner.BotAvatar; if (!((Object)(object)botAvatar == (Object)null)) { PhotonView component = ((Component)botAvatar).GetComponent(); if (!((Object)(object)component == (Object)null)) { __instance.PlayerAdded(component.ViewID); Plugin.Log.LogInfo((object)"[Aggro] A new enemy woke up — introduced it to him immediately."); } } } catch { } } } public static class BotAssist { private static PlayerAvatar _who; private static PhysGrabObject _what; private static float _until; private static float _offerAt; private const float Reach = 6f; private const float MaxSeconds = 45f; public static bool Helping { get; private set; } public static void Reset() { Helping = false; _who = null; _what = null; } public static bool Help(PlayerAvatar asker, Vector3 botPos) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatar val = asker ?? BotBuddy.Current; if ((Object)(object)val == (Object)null) { BotPersona.SpeakExact(BotPersona.HelpNobody, botPos); return false; } PhysGrabObject val2 = HeldBy(val); if ((Object)(object)val2 == (Object)null || BotHands.IsCart(val2)) { BotPersona.SpeakExact(BotPersona.HelpNothing, botPos, SemiFunc.PlayerGetName(val)); return false; } return Begin(val, val2, botPos, announce: true); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Assist] " + ex.Message)); return false; } } private static bool Begin(PlayerAvatar who, PhysGrabObject what, Vector3 botPos, bool announce) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) _who = who; _what = what; Helping = true; _until = Time.unscaledTime + 45f; BotHands.DropEverything(); if (announce) { BotPersona.SpeakExact(BotPersona.HelpOnIt, botPos, SemiFunc.PlayerGetName(who)); } Plugin.Log.LogInfo((object)("[Assist] Helping " + SemiFunc.PlayerGetName(who) + " with '" + ((Object)((Component)what).gameObject).name + "'.")); return true; } public static void MaybeOffer(Vector3 botPos) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) try { if (Helping || Time.unscaledTime < _offerAt || BotHands.Current != BotHands.Job.Idle) { return; } PlayerAvatar current = BotBuddy.Current; if ((Object)(object)current == (Object)null || Reflect.Get(current, "isDisabled") || Vector3.Distance(botPos, ((Component)current).transform.position) > 6f) { return; } PhysGrabObject val = HeldBy(current); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.rb == (Object)null) && !BotHands.IsCart(val) && !(val.rb.mass < 2f)) { Vector3 velocity = val.rb.velocity; if (!(((Vector3)(ref velocity)).magnitude > 0.6f)) { _offerAt = Time.unscaledTime + 25f; Begin(current, val, botPos, announce: false); BotPersona.SpeakExact(BotPersona.HelpUnasked, botPos, SemiFunc.PlayerGetName(current)); } } } catch { } } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) if (!Helping) { return false; } try { Vector3 position = bot.transform.position; if ((Object)(object)_who == (Object)null || (Object)(object)_what == (Object)null || Time.unscaledTime > _until || Reflect.Get(_who, "isDisabled") || (Object)(object)HeldBy(_who) != (Object)(object)_what) { Stop(position, (Object)(object)HeldBy(_who) != (Object)(object)_what && (Object)(object)_what != (Object)null); return false; } Transform transform = ((Component)_what).transform; float num = Vector3.Distance(position, transform.position); PhysGrabber physGrabber = avatar.physGrabber; if ((Object)(object)physGrabber == (Object)null) { Stop(position, announce: false); return false; } if (!((Object)(object)Reflect.Get(physGrabber, "grabbedPhysGrabObject") == (Object)(object)_what)) { if (num > 2.2f) { BotMovement.SetDestination(transform.position); BotMovement.FaceTowards(transform.position); return true; } BotHands.GrabDirectly(_what); return true; } PhysGrabber physGrabber2 = _who.physGrabber; Vector3 aim = transform.position + Vector3.up * 0.6f; if ((Object)(object)physGrabber2 != (Object)null) { Vector3 val = Reflect.Get(physGrabber2, "physGrabPointPullerPosition"); if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { aim = val; } } BotMovement.FaceTowards(transform.position); if (num > 2.6f) { BotMovement.SetDestination(transform.position); } else { BotMovement.Stop(); } BotHands.HoldAt(physGrabber, _what, bot, dt, aim); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Assist] " + ex.Message)); return false; } } public static void Stop(Vector3 botPos, bool announce) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (Helping) { Helping = false; _who = null; _what = null; BotHands.DropEverything(); if (announce) { BotPersona.SpeakExact(BotPersona.HelpDone, botPos); } } } private static PhysGrabObject HeldBy(PlayerAvatar p) { try { PhysGrabber val = (((Object)(object)p != (Object)null) ? p.physGrabber : null); if ((Object)(object)val == (Object)null) { return null; } return Reflect.Get(val, "grabbedPhysGrabObject"); } catch { return null; } } } public static class BotBuddy { private static float _checkTimer; public static PlayerAvatar Current { get; private set; } public static string CurrentName { get { if (!Alive(Current)) { return null; } return SemiFunc.PlayerGetName(Current); } } public static void Reset() { Current = null; _checkTimer = 0f; } public static bool AnchorPosition(out Vector3 pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) pos = Vector3.zero; if (!Alive(Current)) { return false; } pos = ((Component)Current).transform.position; return true; } public static void Tick(float dt, Vector3 botPos) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) _checkTimer -= dt; if (_checkTimer > 0f) { return; } _checkTimer = 1.5f; if (!Alive(Current)) { PlayerAvatar val = PickRandom(null); if ((Object)(object)val == (Object)null) { Current = null; Announce(); return; } bool num = (Object)(object)Current != (Object)null; Current = val; Announce(); BotPersona.Speak(num ? "newbuddy" : "buddy", BotPersona.BuddyPicked, 1f, 20f, botPos, SemiFunc.PlayerGetName(val)); Plugin.Log.LogInfo((object)("[Buddy] Sticking with " + SemiFunc.PlayerGetName(val) + ".")); } } private static void Announce() { try { int num = -1; if ((Object)(object)Current != (Object)null) { PhotonView component = ((Component)Current).GetComponent(); if ((Object)(object)component != (Object)null && component.Owner != null) { num = component.Owner.ActorNumber; } } BotBuddyHud.SetBuddyLocal(num); BotNet.BroadcastBuddy(num); } catch { } } public static void Assign(PlayerAvatar p, Vector3 botPos) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (Alive(p)) { Current = p; _checkTimer = 1.5f; Announce(); BotPersona.Speak("buddy", BotPersona.BuddyPicked, 1f, 0f, botPos, SemiFunc.PlayerGetName(p)); Plugin.Log.LogInfo((object)("[Buddy] Reassigned to " + SemiFunc.PlayerGetName(p) + ".")); } } public static void Reroll(Vector3 botPos) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar val = PickRandom(Current); if ((Object)(object)val == (Object)null) { BotPersona.Speak("nobuddy", BotPersona.NoOneElse, 1f, 10f, botPos); return; } Current = val; _checkTimer = 1.5f; Announce(); BotPersona.Speak("newbuddy", BotPersona.BuddyPicked, 1f, 0f, botPos, SemiFunc.PlayerGetName(val)); Plugin.Log.LogInfo((object)("[Buddy] Swapped to " + SemiFunc.PlayerGetName(val) + ".")); } private static bool Alive(PlayerAvatar p) { try { if ((Object)(object)p == (Object)null) { return false; } if (BotSpawner.IsBot(p)) { return false; } if (Reflect.Get(p, "isDisabled")) { return false; } if ((Object)(object)GameDirector.instance != (Object)null && !GameDirector.instance.PlayerList.Contains(p)) { return false; } return true; } catch { return false; } } private static PlayerAvatar PickRandom(PlayerAvatar avoid) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } List list = new List(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (Alive(player) && player != avoid) { list.Add(player); } } if (list.Count == 0) { return null; } return list[Random.Range(0, list.Count)]; } catch { return null; } } } public static class BotBuddyHud { private sealed class Cmd { public readonly string Intent; public readonly string Label; public Cmd(string intent, string label) { Intent = intent; Label = label; } } private static readonly List Commands = new List { new Cmd("buddy.claim", "Follow me"), new Cmd("move.wait", "Wait here"), new Cmd("scout.go", "Scout ahead"), new Cmd("cart.fetch", "Fetch the cart"), new Cmd("medic.revive", "Revive them"), new Cmd("medic.heal", "Heal me"), new Cmd("work.drop", "Drop it"), new Cmd("scout.report", "Report"), new Cmd("social.quiet", "Be quiet"), new Cmd("social.unquiet", "Speak up"), new Cmd("work.resume", "Back to work"), new Cmd("work.stop", "Stop working"), new Cmd("move.hide", "Hide"), new Cmd("buddy.reroll", "Go find someone else"), new Cmd("buddy.who", "Who are you following?"), new Cmd("social.praise", "Good job"), new Cmd("social.howareyou", "How are you?"), new Cmd("social.greet", "Hey Bertram"), new Cmd("social.robot", "Are you a robot?"), new Cmd("social.name", "What's your name?") }; private static int _slot; private static float _drum; private static bool _wasArmed; private static float _flash; private static string _flashLabel; private static GUIStyle _title; private static GUIStyle _row; private static GUIStyle _rowHi; private static GUIStyle _num; private static GUIStyle _foot; private static Texture2D _panelTex; private static Texture2D _hiTex; private static Texture2D _lineTex; private static Texture2D _shadeTex; private static Texture2D _icon; private const float DrumHeight = 150f; private const float RowPitch = 26f; private const float DegPerRow = 21f; public static int BuddyActor { get; private set; } = -1; private static int SlotCount => Commands.Count + 1; private static Cmd Selected { get { if (_slot <= 0 || _slot > Commands.Count) { return null; } return Commands[_slot - 1]; } } public static bool Visible { get { try { if (Plugin.CfgBuddyHud != null && !Plugin.CfgBuddyHud.Value) { return false; } if (SemiFunc.MenuLevel() && !SemiFunc.RunIsLobbyMenu()) { return false; } PlayerAvatar val = LocalAvatar(); if ((Object)(object)val == (Object)null) { return false; } if (Reflect.Get(val, "isDisabled")) { return false; } if (!BotSpawner.HasBot) { return false; } if (BotIntercom.Open) { return true; } if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient) { return (Object)(object)BotBuddy.Current == (Object)(object)val; } return BuddyActor >= 0 && PhotonNetwork.LocalPlayer.ActorNumber == BuddyActor; } catch { return false; } } } public static void Reset() { BuddyActor = -1; _slot = 0; _drum = 0f; _wasArmed = false; _flash = 0f; } public static void SetBuddyLocal(int actorNumber) { if (BuddyActor != actorNumber) { BuddyActor = actorNumber; if (actorNumber < 0) { _slot = 0; _drum = 0f; } } } public static void OnBuddyBroadcast(int actorNumber) { BuddyActor = actorNumber; if (!Visible) { _slot = 0; _drum = 0f; } } private static PlayerAvatar LocalAvatar() { try { if ((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.playerAvatarScript != (Object)null) { return PlayerController.instance.playerAvatarScript; } } catch { } return null; } public static void Tick() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) try { if (_flash > 0f) { _flash -= Time.unscaledDeltaTime; } bool armed = BotInputGate.Armed; if (armed && !_wasArmed) { _slot = 0; _drum = 0f; } if (armed) { float y = Input.mouseScrollDelta.y; if (Mathf.Abs(y) > 0.01f) { int num = ((!(y > 0f)) ? 1 : (-1)); int slot = _slot; _slot = (_slot + num + SlotCount) % SlotCount; if (slot == SlotCount - 1 && _slot == 0) { _drum -= SlotCount; } else if (slot == 0 && _slot == SlotCount - 1) { _drum += SlotCount; } } for (int i = 0; i < 9 && i < Commands.Count; i++) { if (Input.GetKeyDown((KeyCode)(49 + i)) || Input.GetKeyDown((KeyCode)(257 + i))) { Send(i); _slot = 0; _drum = 0f; break; } } } if (!armed && _wasArmed) { if (_slot > 0) { Send(_slot - 1); } _slot = 0; } _drum = Mathf.Lerp(_drum, (float)_slot, 1f - Mathf.Exp(-18f * Time.unscaledDeltaTime)); if (Mathf.Abs(_drum - (float)_slot) < 0.001f) { _drum = _slot; } _wasArmed = armed; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hud] " + ex.Message)); } } private static void Send(int index) { if (index >= 0 && index < Commands.Count) { Cmd cmd = Commands[index]; _flash = 0.8f; _flashLabel = cmd.Label; if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient) { BotCommands.Run(cmd.Intent, LocalAvatar()); } else { BotNet.SendHudCommand(cmd.Intent); } Plugin.Log.LogInfo((object)("[Hud] Sent '" + cmd.Intent + "' (" + cmd.Label + ").")); } } public static void Draw() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) if (!Visible) { return; } try { EnsureSkin(); bool armed = BotInputGate.Armed; float num = (armed ? 150f : 40f); float num2 = 58f + num + 22f; float num3 = (float)Screen.width - 276f - 18f; float num4 = (float)Screen.height - num2 - 18f; GUI.DrawTexture(new Rect(num3, num4, 276f, num2), (Texture)(object)_panelTex, (ScaleMode)0); GUI.DrawTexture(new Rect(num3 + 12f, num4 + 10f, 64f, 32f), (Texture)(object)_icon, (ScaleMode)2); GUI.Label(new Rect(num3 + 84f, num4 + 9f, 182f, 18f), BotSpawner.ChosenName.ToUpperInvariant(), _title); GUI.Label(new Rect(num3 + 84f, num4 + 26f, 182f, 16f), (_flash > 0f) ? ("sent: " + _flashLabel) : "is sticking with you", _foot); GUI.DrawTexture(new Rect(num3 + 10f, num4 + 58f - 10f, 256f, 1f), (Texture)(object)_lineTex, (ScaleMode)0); Rect area = default(Rect); ((Rect)(ref area))..ctor(num3 + 8f, num4 + 58f, 260f, num); if (!armed) { GUI.Label(new Rect(((Rect)(ref area)).x + 6f, ((Rect)(ref area)).y + 2f, ((Rect)(ref area)).width - 12f, 18f), "Hold ALT", _row); GUI.Label(new Rect(((Rect)(ref area)).x + 6f, ((Rect)(ref area)).y + 19f, ((Rect)(ref area)).width - 12f, 16f), "wheel spins · release sends · 1-9 direct", _foot); } else { DrawDrum(area); GUI.Label(new Rect(num3 + 10f, num4 + num2 - 18f, 256f, 14f), (Selected == null) ? "spin to choose" : ("release ALT → " + Selected.Label), _foot); } } catch { } } private static void DrawDrum(Rect area) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) float num = ((Rect)(ref area)).x + ((Rect)(ref area)).width * 0.5f; float num2 = ((Rect)(ref area)).y + ((Rect)(ref area)).height * 0.5f; float num3 = ((Rect)(ref area)).height * 0.5f; GUI.DrawTexture(new Rect(((Rect)(ref area)).x, num2 - 13f, ((Rect)(ref area)).width, 26f), (Texture)(object)_hiTex, (ScaleMode)0); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, num2 - 13f, ((Rect)(ref area)).width, 1f), (Texture)(object)_lineTex, (ScaleMode)0); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, num2 + 13f, ((Rect)(ref area)).width, 1f), (Texture)(object)_lineTex, (ScaleMode)0); Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; int num4 = Mathf.RoundToInt(_drum); int num5 = Mathf.CeilToInt(4.285714f); Rect val2 = default(Rect); for (int i = -num5; i <= num5; i++) { int num6 = num4 + i; float num7 = ((float)num6 - _drum) * 21f; if (Mathf.Abs(num7) >= 88f) { continue; } float num8 = num7 * (MathF.PI / 180f); float num9 = num3 * Mathf.Sin(num8); float num10 = Mathf.Cos(num8); if (!(num10 <= 0.02f)) { int num11 = (num6 % SlotCount + SlotCount) % SlotCount; bool num12 = num11 == 0; string text = (num12 ? "— nothing —" : Commands[num11 - 1].Label); string text2 = ((num12 || num11 > 9) ? "" : num11.ToString()); float num13 = num2 + num9; float num14 = Mathf.Pow(num10, 1.35f); GUI.color = new Color(1f, 1f, 1f, Mathf.Clamp01(num14)); GUIUtility.ScaleAroundPivot(new Vector2(1f, num10), new Vector2(num, num13)); bool flag = Mathf.Abs(num7) < 10.5f; GUIStyle val = (num12 ? _foot : (flag ? _rowHi : _row)); ((Rect)(ref val2))..ctor(((Rect)(ref area)).x + 8f, num13 - 13f, ((Rect)(ref area)).width - 16f, 26f); if (text2.Length > 0) { GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y, 16f, ((Rect)(ref val2)).height), text2, _num); } GUI.Label(new Rect(((Rect)(ref val2)).x + 22f, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width - 28f, ((Rect)(ref val2)).height), text, val); GUI.matrix = matrix; } } GUI.color = color; GUI.matrix = matrix; for (int j = 0; j < 7; j++) { float num15 = (float)j / 7f; float num16 = ((Rect)(ref area)).height * 0.3f / 7f; GUI.color = new Color(1f, 1f, 1f, 0.55f * (1f - num15)); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).y + (float)j * num16, ((Rect)(ref area)).width, num16 + 1f), (Texture)(object)_shadeTex, (ScaleMode)0); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).yMax - (float)(j + 1) * num16, ((Rect)(ref area)).width, num16 + 1f), (Texture)(object)_shadeTex, (ScaleMode)0); } GUI.color = color; } private static void EnsureSkin() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_panelTex != (Object)null)) { _panelTex = Solid(new Color(0.06f, 0.07f, 0.08f, 0.92f)); _hiTex = Solid(new Color(0.85f, 0.72f, 0.18f, 0.16f)); _lineTex = Solid(new Color(1f, 1f, 1f, 0.18f)); _shadeTex = Solid(new Color(0.04f, 0.05f, 0.06f, 1f)); _icon = BuildIcon(); _title = new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; _title.normal.textColor = new Color(1f, 0.86f, 0.35f); _row = new GUIStyle(GUI.skin.label) { fontSize = 12, alignment = (TextAnchor)3 }; _row.normal.textColor = new Color(0.86f, 0.88f, 0.9f); _rowHi = new GUIStyle(_row) { fontStyle = (FontStyle)1 }; _rowHi.normal.textColor = new Color(1f, 0.93f, 0.62f); _num = new GUIStyle(_row) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1 }; _num.normal.textColor = new Color(0.55f, 0.6f, 0.66f); _foot = new GUIStyle(GUI.skin.label) { fontSize = 10, alignment = (TextAnchor)3 }; _foot.normal.textColor = new Color(0.6f, 0.64f, 0.7f); } } private static Texture2D Solid(Color c) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, c); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; return val; } private static Texture2D BuildIcon() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_00e4: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(96, 48, (TextureFormat)4, false); ((Object)val).hideFlags = (HideFlags)61; ((Texture)val).filterMode = (FilterMode)1; Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); Color shell = default(Color); ((Color)(ref shell))..ctor(0.91f, 0.91f, 0.88f, 1f); Color edge = default(Color); ((Color)(ref edge))..ctor(0.55f, 0.56f, 0.55f, 1f); Color visor = default(Color); ((Color)(ref visor))..ctor(0.09f, 0.1f, 0.12f, 1f); Color eye = default(Color); ((Color)(ref eye))..ctor(1f, 0.85f, 0.3f, 1f); for (int i = 0; i < 48; i++) { for (int j = 0; j < 96; j++) { val.SetPixel(j, i, val2); } } DrawBot(val, 30f, 22f, 15f, 0.34f, shell, edge, visor, eye); DrawBot(val, 66f, 22f, 15f, -0.34f, shell, edge, visor, eye); val.Apply(); return val; } private static void DrawBot(Texture2D t, float cx, float cy, float r, float lean, Color shell, Color edge, Color visor, Color eye) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Cos(lean); float num2 = Mathf.Sin(lean); for (int i = 0; i < ((Texture)t).height; i++) { for (int j = 0; j < ((Texture)t).width; j++) { float num3 = (float)j + 0.5f - cx; float num4 = (float)i + 0.5f - cy; float num5 = num3 * num + num4 * num2; float num6 = (0f - num3) * num2 + num4 * num; float num7 = Mathf.Pow(Mathf.Abs(num5 / r), 3f) + Mathf.Pow(Mathf.Abs(num6 / (r * 0.92f)), 3f); if (num7 > 1.18f) { continue; } Color val = ((num7 > 0.86f) ? edge : shell); if (num6 > (0f - r) * 0.05f && num6 < r * 0.46f && Mathf.Abs(num5) < r * 0.8f) { val = visor; float num8 = Mathf.Abs(num5) - r * 0.36f; float num9 = num6 - r * 0.21f; if (num8 * num8 + num9 * num9 < r * 0.15f * (r * 0.15f)) { val = eye; } } t.SetPixel(j, i, val); } } } } public static class BotCart { private static readonly HashSet Told = new HashSet(); public static Vector3 SteeringGrip(PhysGrabCart cart, Vector3 fallback) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)cart == (Object)null) { return fallback; } Collider val = HandleCollider(cart); if ((Object)(object)val != (Object)null) { Bounds bounds = val.bounds; return ((Bounds)(ref bounds)).center; } if ((Object)(object)cart.handlePoint != (Object)null) { return cart.handlePoint.position; } } catch { } return fallback; } public static Collider HandleCollider(PhysGrabCart cart) { try { if ((Object)(object)cart == (Object)null) { return null; } PhysGrabObjectGrabArea val = ((Component)cart).GetComponent() ?? ((Component)cart).GetComponentInChildren(true); if ((Object)(object)val == (Object)null || val.grabAreas == null) { return null; } foreach (GrabArea grabArea in val.grabAreas) { if (grabArea == null) { continue; } List list = Reflect.Get>(grabArea, "grabAreaColliders"); if (list != null) { foreach (Collider item in list) { if ((Object)(object)item != (Object)null) { return item; } } } if ((Object)(object)grabArea.grabAreaTransform != (Object)null) { Collider val2 = ((Component)grabArea.grabAreaTransform).GetComponent() ?? ((Component)grabArea.grabAreaTransform).GetComponentInChildren(true); if ((Object)(object)val2 != (Object)null) { return val2; } } } } catch { } return null; } public static bool IsSteering(PhysGrabCart cart, PhysGrabber grabber) { try { if ((Object)(object)cart == (Object)null || (Object)(object)grabber == (Object)null) { return false; } PhysGrabObjectGrabArea val = ((Component)cart).GetComponent() ?? ((Component)cart).GetComponentInChildren(true); if ((Object)(object)val == (Object)null || val.listOfAllGrabbers == null) { return false; } return val.listOfAllGrabbers.Contains(grabber) && Reflect.Get(cart, "cartActive"); } catch { return false; } } public static Vector3 StandingSpot(PhysGrabCart cart, Vector3 grip, Vector3 cartCentre) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Vector3 val = grip - cartCentre; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { return grip; } return grip + ((Vector3)(ref val)).normalized * 1f; } public static Vector3 Maintain(PhysGrabCart cart, PhysGrabber grabber, Vector3 fallback) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Vector3 result = SteeringGrip(cart, fallback); try { if ((Object)(object)cart == (Object)null || (Object)(object)grabber == (Object)null) { return result; } if (Reflect.Get(grabber, "initialPressTimer") <= 0.02f) { Reflect.Set(grabber, "initialPressTimer", 0.15f); } int instanceID = ((Object)cart).GetInstanceID(); if (IsSteering(cart, grabber)) { if (Told.Add(instanceID)) { Plugin.Log.LogInfo((object)"[Cart] He's on the handle — the cart is in Handled mode, so it steers instead of being dragged."); } } else { Told.Remove(instanceID); } } catch { } return result; } } public static class BotChatter { private static float _pollTimer; private static readonly Dictionary _lastHealth = new Dictionary(); private static readonly HashSet _knownDead = new HashSet(); private const float EarshotRange = 18f; public static void SayFetching() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("fetch", BotPersona.Fetch, 0.2f, 240f, BotSpawner.BotPosition); } public static void SayHauling() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("haul", BotPersona.Haul, 0.2f, 240f, BotSpawner.BotPosition); } public static void SayDelivered() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("delivered", BotPersona.Delivered, 0.35f, 180f, BotSpawner.BotPosition); } public static void SayDanger() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("danger", BotPersona.Danger, 0.55f, 90f, BotSpawner.BotPosition); } public static void SayOops() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("oops", BotPersona.Oops, 0.6f, 120f, BotSpawner.BotPosition); } public static void SayYielding() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("yield", BotPersona.Yield, 0.45f, 150f, BotSpawner.BotPosition); } public static void SayAssisting() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("assist", BotPersona.Assist, 0.45f, 180f, BotSpawner.BotPosition); } public static void Reset() { BotPersona.Reset(); _lastHealth.Clear(); _knownDead.Clear(); } public static void Tick(GameObject bot, float dt) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null) { return; } _pollTimer -= dt; if (_pollTimer > 0f) { return; } _pollTimer = 0.25f; try { PollCrew(bot.transform.position); } catch { } } private static void PollCrew(Vector3 botPos) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } PhotonView component = ((Component)player).GetComponent(); if ((Object)(object)component == (Object)null) { continue; } int viewID = component.ViewID; if (Reflect.Get(player, "deadSet")) { if (_knownDead.Add(viewID) && InEarshot(botPos, player)) { BotOpinions.Eulogise(player, botPos); } continue; } _knownDead.Remove(viewID); PlayerHealth playerHealth = player.playerHealth; if ((Object)(object)playerHealth == (Object)null) { continue; } int num = Reflect.Get(playerHealth, "health"); if (_lastHealth.TryGetValue(viewID, out var value)) { int num2 = value - num; if (num2 > 0 && InEarshot(botPos, player)) { bool flag = num2 >= 25 || num <= 30; BotPersona.Speak(flag ? "badhurt" : "hurt", flag ? BotPersona.BadHurt : BotPersona.Hurt, flag ? 0.55f : 0.3f, flag ? 90f : 150f, botPos); } } _lastHealth[viewID] = num; } } private static bool InEarshot(Vector3 botPos, PlayerAvatar p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return Vector3.Distance(botPos, ((Component)p).transform.position) <= 18f; } public static void MaybeIdleChatter() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Vector3 botPosition = BotSpawner.BotPosition; string text = BotComposer.Ambient1(); if (text != null && BotPersona.SpeakLiteral(text, botPosition)) { BotComposer.MaybeSecondThought(botPosition); } else { BotPersona.Speak("idle", BotPersona.Idle, 0.5f, 150f, botPosition); } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")] [HarmonyPostfix] public static void OnValuableBroke(PhysGrabObjectImpactDetector __instance, float valueLost, int breakLevel, bool _loseValue) { //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) try { if (!BotSpawner.HasBot || valueLost <= 0f || !_loseValue) { return; } Vector3 botPosition = BotSpawner.BotPosition; Transform transform = ((Component)__instance).transform; if (Vector3.Distance(botPosition, transform.position) > 18f) { return; } PhysGrabObject val = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent(); PlayerAvatar val2 = (((Object)(object)val != (Object)null) ? Reflect.Get(val, "lastPlayerGrabbing") : null); if (((Object)(object)val != (Object)null && BotHands.IsCarrying(val)) || ((Object)(object)val2 != (Object)null && BotSpawner.IsBot(val2))) { BotPersona.Speak("oops", BotPersona.Oops, 0.6f, 120f, botPosition); return; } if ((Object)(object)val2 != (Object)null && !BotSpawner.IsBot(val2)) { BotFacts.NoteBreakage(SemiFunc.PlayerGetName(val2)); string text = BotComposer.OnBreakage(); if (text != null && BotPersona.SpeakLiteral(text, botPosition)) { BotComposer.MaybeSecondThought(botPosition, 0.25f); return; } } try { ValuableObject val3 = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent(); if ((Object)(object)val3 != (Object)null && (Object)(object)val2 != (Object)null) { float num = Reflect.Get(val3, "dollarValueOriginal"); float num2 = Reflect.Get(val3, "dollarValueCurrent"); if (num > 0f && num2 < num * 0.15f) { BotTattle.Witnessed((Component)(object)__instance, val2, Mathf.RoundToInt(num)); } } } catch { } if ((Object)(object)val2 != (Object)null && !Reflect.Get(val2, "isDisabled")) { string aboutName = (BotPersona.CanSee(botPosition, val2) ? SemiFunc.PlayerGetName(val2) : null); bool flag = breakLevel <= 1 || valueLost >= 40f; BotPersona.Speak(flag ? "blamebig" : "blame", flag ? BotPersona.BlameBig : BotPersona.Blame, flag ? 0.75f : 0.5f, flag ? 90f : 45f, botPosition, aboutName); } else { BotPersona.Speak("break", BotPersona.Break, 0.5f, 120f, botPosition); } } catch { } } } public static class BotChute { public static Vector3 StandingSpot(ExtractionPoint ep) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; try { if ((Object)(object)ep == (Object)null) { return val; } val = (((Object)(object)ep.safetySpawn != (Object)null) ? ep.safetySpawn : ((Component)ep).transform).position; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val + Vector3.up * 3f, Vector3.down, ref val2, 30f, LayerMask.GetMask(new string[2] { "Default", "StaticGrabObject" }), (QueryTriggerInteraction)1)) { return new Vector3(val.x, ((RaycastHit)(ref val2)).point.y + 0.15f, val.z); } NavMeshHit val3 = default(NavMeshHit); if (NavMesh.SamplePosition(val, ref val3, 3f, -1)) { Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(((NavMeshHit)(ref val3)).position.x - val.x, ((NavMeshHit)(ref val3)).position.z - val.z); if (((Vector2)(ref val4)).magnitude < 1.5f) { return ((NavMeshHit)(ref val3)).position + Vector3.up * 0.15f; } } Plugin.Log.LogWarning((object)("[Chute] No floor found under '" + ((Object)ep).name + "' — using the raw anchor, which is how he ends up wedged in the top.")); return val + Vector3.up * 0.1f; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Chute] " + ex.Message)); return val; } } public static bool LooksStandable(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) try { RaycastHit val = default(RaycastHit); if (!Physics.Raycast(p + Vector3.up * 0.5f, Vector3.down, ref val, 4f, LayerMask.GetMask(new string[2] { "Default", "StaticGrabObject" }), (QueryTriggerInteraction)1)) { return false; } return !Physics.CheckSphere(p + Vector3.up * 0.6f, 0.25f, LayerMask.GetMask(new string[2] { "Default", "StaticGrabObject" }), (QueryTriggerInteraction)1); } catch { return true; } } } public static class BotCommands { private sealed class Ctx { public PlayerAvatar Speaker; public Vector3 BotPos; public string Text; } private sealed class Intent { public string Name; public string[] Phrases; public Action Run; public Intent(string name, string[] phrases, Action run) { Name = name; Phrases = phrases; Run = run; } } private struct PendingChat { public string Text; public PlayerAvatar Who; public float At; } private static readonly List Table = new List { new Intent("cart.fetch", new string[14] { "bring the cart", "get the cart", "fetch the cart", "cart over here", "bring a cart", "cart please", "i need the cart", "we need the cart", "push the cart", "cart here", "bringing the cart", "getting the cart", "fetching the cart", "the cart" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotHands.FetchCart(c.Speaker, c.BotPos); }), new Intent("buddy.reroll", new string[7] { "someone else", "somebody else", "new buddy", "leave me alone", "go away", "get lost", "not me" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) BotScout.Stop(c.BotPos, announce: false); if (!BotGrudge.NoteDismissal(c.Speaker, c.BotPos)) { BotBuddy.Reroll(c.BotPos); } }), new Intent("buddy.claim", new string[8] { "follow me", "come here", "with me", "over here", "stay close", "on me", "stick with me", "come with" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_002d: Unknown result type (might be due to invalid IL or missing references) BotScout.Stop(c.BotPos, announce: false); if ((Object)(object)c.Speaker != (Object)null) { if (Reflect.Get(c.Speaker, "isDisabled")) { BotMedic.OrderRevive(c.BotPos); } else { BotBuddy.Assign(c.Speaker, c.BotPos); if (BotDanger.Level == BotDanger.Threat.Hiding) { BotDanger.ForceWary(); } } } else { BotPersona.Speak("confused", BotPersona.Confused, 1f, 5f, c.BotPos); } }), new Intent("buddy.who", new string[4] { "who are you with", "whose buddy", "who's your buddy", "who are you following" }, delegate(Ctx c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) string currentName = BotBuddy.CurrentName; if (string.IsNullOrEmpty(currentName)) { BotPersona.Speak("nobuddy", BotPersona.NoOneElse, 1f, 5f, c.BotPos); } else { BotPersona.Speak("buddywho", BotPersona.BuddyWho, 1f, 5f, c.BotPos, currentName); } }), new Intent("medic.revive", new string[20] { "revive", "resurrect", "res him", "res her", "res them", "rez", "raise him", "raise her", "bring him back", "bring her back", "bring them back", "jump start", "jumpstart", "jumper cables", "shock him", "shock her", "get him up", "get her up", "wake him", "wake her" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotMedic.OrderRevive(c.BotPos); }), new Intent("medic.heal", new string[11] { "heal me", "heal him", "heal her", "heal them", "patch me up", "patch him up", "i need health", "give me health", "top me up", "i'm hurt", "im hurt" }, delegate(Ctx c) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) BotMedic.OrderHeal(((c.Text.Contains("heal me") || c.Text.Contains("patch me") || c.Text.Contains("top me") || c.Text.Contains("i need") || c.Text.Contains("give me") || c.Text.Contains("hurt")) ? c.Speaker : NearestHurtOther(c.BotPos, c.Speaker)) ?? c.Speaker, c.BotPos); }), new Intent("scout.go", new string[11] { "look around", "take a look", "have a look", "go and look", "scout", "explore", "recon", "check it out", "scope it out", "go ahead", "see what's out there" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotScout.Begin(c.BotPos, c.Speaker); }), new Intent("scout.report", new string[7] { "report", "what did you see", "anything out there", "find anything", "see anything", "what's out there", "sitrep" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotScout.Report(c.BotPos); }), new Intent("work.drop", new string[4] { "drop it", "put it down", "let go", "drop that" }, delegate(Ctx c) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) BotHands.DropEverything(); BotPersona.Speak("acknowledge", BotPersona.Acknowledge, 1f, 3f, c.BotPos); }), new Intent("work.stop", new string[6] { "stop working", "stop looting", "leave the loot", "forget the loot", "don't touch anything", "hands off" }, delegate(Ctx c) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) BotHands.PauseWork(paused: true); BotPersona.Speak("toldoff", BotPersona.ToldOff, 1f, 10f, c.BotPos); }), new Intent("work.resume", new string[8] { "get to work", "back to work", "start working", "grab the loot", "get the loot", "fill the cart", "load the cart", "do your job" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) BotHands.PauseWork(paused: false); BotScout.Stop(c.BotPos, announce: false); BotPersona.Speak("acknowledge", BotPersona.Acknowledge, 1f, 3f, c.BotPos); }), new Intent("move.hide", new string[4] { "hide", "get down", "take cover", "get out of sight" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) BotScout.Stop(c.BotPos, announce: false); BotDanger.ForceHide(12f); BotPersona.Speak("acknowledge", BotPersona.Acknowledge, 1f, 3f, c.BotPos); }), new Intent("social.unquiet", new string[8] { "you can talk", "talk again", "all clear", "at ease", "it's gone", "its gone", "coast is clear", "you can speak" }, delegate(Ctx c) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (BotHush.Release()) { BotPersona.SpeakExact(BotPersona.Unhushed, c.BotPos); } else { BotPersona.Speak("confused", BotPersona.Confused, 1f, 8f, c.BotPos); } }), new Intent("social.quiet", new string[8] { "shut up", "be quiet", "stop talking", "zip it", "pipe down", "quiet", "hush", "silence" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) BotScout.Stop(c.BotPos, announce: false); BotHush.Start(30f); BotGrudge.NoteHush(c.Speaker, c.BotPos); }), new Intent("move.wait", new string[6] { "wait", "stay", "hold on", "hold up", "stand still", "stop" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) BotScout.Stop(c.BotPos, announce: false); BotMovement.Stop(); BotPersona.Speak("acknowledge", BotPersona.Acknowledge, 1f, 3f, c.BotPos); }), new Intent("ask.haul", new string[9] { "how much do we have", "how much have we got", "what's the haul", "whats the haul", "how much money", "are we short", "how much more", "what's the total", "whats the total" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.ReportHaul(c.BotPos); }), new Intent("ask.extractions", new string[4] { "how many extractions", "how many left", "how many more", "extractions left" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.ReportExtractions(c.BotPos); }), new Intent("ask.alive", new string[8] { "who's alive", "whos alive", "who's left", "whos left", "who's down", "whos down", "is anyone alive", "how many are up" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.ReportAlive(c.BotPos); }), new Intent("ask.where", new string[4] { "where are you", "where'd you go", "where did you go", "how far are you" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.ReportPosition(c.BotPos); }), new Intent("ask.safe", new string[6] { "is it clear", "is it safe", "anything there", "anything near you", "see anything", "all clear over there" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.ReportSafety(c.BotPos); }), new Intent("point.go", new string[9] { "go over there", "go there", "over there", "go that way", "head over there", "get over there", "move over there", "stand there", "wait over there" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.GoThere(c.Speaker, c.BotPos); }), new Intent("point.grab", new string[22] { "grab that", "grab this", "take that", "take this", "get that", "get this", "pick that up", "pick this up", "bring that", "carry this", "carry that", "grabbing that", "grabbing this", "taking that", "taking this", "getting that", "getting this", "picking that up", "picking this up", "bringing that", "carrying that", "carrying this" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.GrabThat(c.Speaker, c.BotPos); }), new Intent("point.leave", new string[7] { "leave that", "leave it alone", "don't touch that", "dont touch that", "put that down", "not that one", "forget that one" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.LeaveThat(c.Speaker, c.BotPos); }), new Intent("point.follow", new string[9] { "follow him", "follow her", "follow them", "go with him", "go with her", "go with them", "stay with him", "stay with her", "stay with them" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.FollowThem(c.Speaker, c.BotPos); }), new Intent("go.truck", new string[6] { "go to the truck", "get to the truck", "back to the truck", "head to the truck", "get in the truck", "truck" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.GoToTruck(c.BotPos); }), new Intent("go.extraction", new string[5] { "go to extraction", "get to extraction", "head to extraction", "go to the extraction point", "get to the chute" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.GoToExtraction(c.BotPos); }), new Intent("body.crouch", new string[4] { "crouch", "get low", "duck", "keep your head down" }, delegate(Ctx c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) BotOrders.Crouch(down: true, c.BotPos); }), new Intent("body.stand", new string[4] { "stand up", "get up", "stand", "on your feet" }, delegate(Ctx c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) BotOrders.Crouch(down: false, c.BotPos); }), new Intent("body.jump", new string[3] { "jump", "hop", "get up there" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotOrders.Jump(c.BotPos); }), new Intent("body.lookat", new string[4] { "look at me", "face me", "eyes on me", "pay attention" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.LookAt(c.Speaker, c.BotPos); }), new Intent("body.backup", new string[8] { "back up", "move back", "get out of the way", "out of my way", "you're in the way", "youre in the way", "give me room", "move" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotOrders.BackUp(c.Speaker, c.BotPos); }), new Intent("social.praise", new string[16] { "good bot", "good boy", "thanks", "thank you", "thankyou", "cheers", "nice one", "well done", "good work", "legend", "you're the best", "appreciate it", "you saved me", "nice save", "much obliged", "ta" }, delegate(Ctx c) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) BotOpinions.Thank(c.Speaker, 1f, "said something nice"); BotPersona.Speak("praised", BotPersona.Praised, 1f, 6f, c.BotPos); }), new Intent("social.name", new string[3] { "what's your name", "who are you", "your name" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("whoami", BotPersona.WhoAmI, 1f, 10f, c.BotPos); }), new Intent("social.robot", new string[7] { "are you a robot", "are you a bot", "are you real", "are you human", "are you an ai", "are you the impostor", "are you a player" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("denial", BotPersona.Denial, 1f, 15f, c.BotPos); }), new Intent("fun.playsong", new string[4] { "play ", "put on ", "play some ", "dj " }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotDance.RequestSong(c.Text, c.BotPos); }), new Intent("fun.dance", new string[5] { "dance", "boogie", "bust a move", "show us your moves", "get down" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotDance.Start(c.BotPos); }), new Intent("fun.stopdance", new string[4] { "stop dancing", "turn it off", "stop the music", "enough dancing" }, delegate { BotDance.Stop(announce: true); }), new Intent("assist.lift", new string[11] { "help me lift", "help me carry", "help me with this", "give me a hand", "help me pick", "help me move", "grab this too", "help lift", "help carry", "lend a hand", "help me grab" }, delegate(Ctx c) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BotAssist.Help(c.Speaker, c.BotPos); }), new Intent("control.halt", new string[10] { "stop", "stop it", "freeze", "hold on", "hold up", "knock it off", "pack it in", "cut it out", "that's enough", "quit it" }, delegate(Ctx c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) BotSpawner.HaltEverything(c.BotPos, announce: true); }), new Intent("social.howareyou", new string[5] { "how are you", "you alright", "you ok", "you okay", "how's it going" }, delegate(Ctx c) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) string text = BotComposer.StatusReport(); if (text == null || !BotPersona.SpeakLiteral(text, c.BotPos)) { BotPersona.Speak("howami", BotPersona.HowAmI, 1f, 15f, c.BotPos); } }), new Intent("social.blame", new string[4] { "you broke it", "you broke that", "you broke the loot", "you smashed it" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("blame", BotPersona.BlameItem, 1f, 5f, c.BotPos); }), new Intent("social.frustration", new string[5] { "damn it", "dammit", "god damn it", "come on bertram", "seriously bertram" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("frust", BotPersona.Frustrated, 1f, 5f, c.BotPos); }), new Intent("emergency.run", new string[4] { "run", "save yourself", "get out of here", "flee" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("run", BotPersona.EmergencyRun, 1f, 5f, c.BotPos); }), new Intent("emergency.trapped", new string[4] { "i'm trapped", "im trapped", "door is locked", "open this door" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("trapped", BotPersona.Trapped, 1f, 5f, c.BotPos); }), new Intent("emergency.monster", new string[3] { "what is that thing", "monster", "it sees me" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("monster", BotPersona.MonsterPanic, 1f, 5f, c.BotPos); }), new Intent("emergency.taunt", new string[5] { "take the hit", "distract it", "bait it", "yell at it", "taunt it" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("taunt", BotPersona.TauntReply, 1f, 5f, c.BotPos); }), new Intent("emergency.elevator", new string[5] { "close the door", "close the elevator", "press the button", "get in the elevator", "get in" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("elevator", BotPersona.ElevatorPress, 1f, 5f, c.BotPos); }), new Intent("resource.light", new string[4] { "too dark", "turn on your light", "can't see", "cant see" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("light", BotPersona.TooDark, 1f, 5f, c.BotPos); }), new Intent("resource.stamina", new string[4] { "slow down", "out of stamina", "wait for me", "i'm tired" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("stamina", BotPersona.StaminaLow, 1f, 5f, c.BotPos); }), new Intent("resource.pass", new string[3] { "pass me that", "give me that", "hand it over" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("pass", BotPersona.ItemPass, 1f, 5f, c.BotPos); }), new Intent("resource.value", new string[5] { "is this expensive", "is this valuable", "worth keeping", "price check", "appraise" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("value", BotPersona.AppraiseValue, 1f, 5f, c.BotPos); }), new Intent("resource.explosive", new string[3] { "carry the explosive", "hold this bomb", "take the nuke" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("explosive", BotPersona.ExplosiveItem, 1f, 5f, c.BotPos); }), new Intent("tactical.stealth", new string[4] { "crouch walk", "go quiet", "don't make a sound", "tiptoe" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("stealth", BotPersona.StealthMode, 1f, 5f, c.BotPos); }), new Intent("tactical.rear", new string[3] { "watch our six", "watch my back", "cover behind" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("rear", BotPersona.WatchRear, 1f, 5f, c.BotPos); }), new Intent("tactical.peek", new string[3] { "peek the door", "check the corner", "check doorway" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("peek", BotPersona.PeekCorner, 1f, 5f, c.BotPos); }), new Intent("tactical.leave", new string[3] { "should we leave", "call it", "time to go" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("leave", BotPersona.LeaveLevel, 1f, 5f, c.BotPos); }), new Intent("tactical.throw", new string[3] { "throw it", "toss that", "chuck it" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("throw", BotPersona.ThrowItem, 1f, 5f, c.BotPos); }), new Intent("accusation.loud", new string[4] { "you're too loud", "youre too loud", "stop stomping", "shush your feet" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("loud", BotPersona.LoudNoise, 1f, 5f, c.BotPos); }), new Intent("accusation.hoard", new string[3] { "loot goblin", "stop stealing loot", "leave some for us" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("hoard", BotPersona.LootSteal, 1f, 5f, c.BotPos); }), new Intent("accusation.apologize", new string[2] { "say sorry", "apologize" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("apologize", BotPersona.ApologyReply, 1f, 5f, c.BotPos); }), new Intent("hazard.mine", new string[4] { "watch the laser", "watch out for mines", "mine", "trap ahead" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("mine", BotPersona.HazardMine, 1f, 5f, c.BotPos); }), new Intent("hazard.pit", new string[4] { "watch your step", "don't fall", "dont fall", "cliff" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("pit", BotPersona.HazardPit, 1f, 5f, c.BotPos); }), new Intent("hazard.deadend", new string[3] { "dead end", "is this a dead end", "wrong way" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("deadend", BotPersona.DeadEndRoom, 1f, 5f, c.BotPos); }), new Intent("hazard.fog", new string[3] { "it's so foggy", "can't see anything", "creepy in here" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("fog", BotPersona.FoggyWeather, 1f, 5f, c.BotPos); }), new Intent("social.favorite", new string[3] { "who is your favorite", "who's your favorite", "favorite player" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("favorite", BotPersona.Favourite, 1f, 5f, c.BotPos); }), new Intent("social.scared", new string[3] { "are you scared", "you nervous", "you shaking" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("scared", BotPersona.ScaredCheck, 1f, 5f, c.BotPos); }), new Intent("social.highfive", new string[3] { "high five", "fist bump", "slap it" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("highfive", BotPersona.HighFiveAct, 1f, 5f, c.BotPos); }), new Intent("social.sing", new string[3] { "sing something", "hum a tune", "sing a song" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("sing", BotPersona.SingSong, 1f, 5f, c.BotPos); }), new Intent("monster.weakness", new string[3] { "how do we beat it", "weakness", "how to kill it" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("weakness", BotPersona.WeaknessCheck, 1f, 5f, c.BotPos); }), new Intent("monster.dead", new string[3] { "is it dead", "did it die", "is it down" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("monsterdead", BotPersona.IsMonsterDead, 1f, 5f, c.BotPos); }), new Intent("monster.hear", new string[3] { "did you hear that", "footsteps", "hear that" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("hear", BotPersona.FootstepsHear, 1f, 5f, c.BotPos); }), new Intent("truck.time", new string[3] { "how much time left", "time left", "clock ticking" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("time", BotPersona.TimeRemaining, 1f, 5f, c.BotPos); }), new Intent("truck.pack", new string[3] { "pack the truck", "stack it", "organize the truck" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("pack", BotPersona.PackTruck, 1f, 5f, c.BotPos); }), new Intent("truck.quota", new string[3] { "did we make it", "quota done", "enough loot" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("quota", BotPersona.QuotaStatus, 1f, 5f, c.BotPos); }), new Intent("truck.cheer", new string[4] { "we made it", "we survived", "gg", "ez" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("cheer", BotPersona.CheerExtracted, 1f, 5f, c.BotPos); }), new Intent("fun.spin", new string[3] { "do a 360", "do a trick", "spin around" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("spin", BotPersona.SpinTrick, 1f, 5f, c.BotPos); }), new Intent("fun.math", new string[3] { "what's 2 plus 2", "whats 2 plus 2", "are you smart" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("math", BotPersona.MathJoke, 1f, 5f, c.BotPos); }), new Intent("fun.joke", new string[2] { "tell a joke", "say something funny" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("joke", BotPersona.TellJoke, 1f, 5f, c.BotPos); }), new Intent("fun.useless", new string[4] { "you suck", "you're useless", "youre useless", "bad bot" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("useless", BotPersona.UselessBot, 1f, 5f, c.BotPos); }), new Intent("fun.sleep", new string[3] { "go to sleep", "take a nap", "sleep" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("sleep", BotPersona.SleepMode, 1f, 5f, c.BotPos); }), new Intent("loot.guard", new string[3] { "guard the loot", "watch the pile", "protect the loot" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("guard", BotPersona.GuardLoot, 1f, 5f, c.BotPos); }), new Intent("loot.fragile", new string[3] { "is it fragile", "will it break", "careful with that" }, delegate(Ctx c) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) BotPersona.Speak("fragile", BotPersona.FragileItem, 1f, 5f, c.BotPos); }), new Intent("social.greet", new string[11] { "hey", "hi", "hello", "yo", "sup", "howdy", "morning", "evening", "you there", "you awake", "greetings" }, delegate(Ctx c) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) string aboutName = (((Object)(object)c.Speaker != (Object)null) ? SemiFunc.PlayerGetName(c.Speaker) : null); BotPersona.Speak("greet", BotPersona.Greeting, 1f, 25f, c.BotPos, aboutName); }) }; private static readonly List _pendingChat = new List(); public static bool Run(string intentName, PlayerAvatar speaker) { //IL_0052: 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_0085: Unknown result type (might be due to invalid IL or missing references) try { GameObject botObject = BotSpawner.BotObject; if ((Object)(object)botObject == (Object)null) { return false; } foreach (Intent item in Table) { if (!(item.Name != intentName)) { BotSocial.ExpectRealAnswer(); Interrupt(item.Name, botObject.transform.position); BotIdle.CommandHeard(item.Name); item.Run(new Ctx { Speaker = speaker, BotPos = botObject.transform.position, Text = intentName }); Plugin.Log.LogInfo((object)("[Cmd] " + intentName + " (from the buddy panel).")); return true; } } Plugin.Log.LogWarning((object)("[Cmd] No such intent: " + intentName)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Cmd] Run: " + ex.Message)); } return false; } private static void Interrupt(string intentName, Vector3 botPos) { try { if ((intentName == null || !intentName.StartsWith("fun.")) && BotDance.Dancing) { BotDance.Stop(announce: false); } } catch { } } public static bool Handle(string text, PlayerAvatar speaker, bool addressed = false) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrEmpty(text)) { return false; } if (!BotSpawner.HasBot) { return false; } if ((Object)(object)speaker != (Object)null && BotSpawner.IsBot(speaker)) { return false; } string text2 = text.ToLowerInvariant(); if (!addressed && !NameMatcher.Mentions(text2, BotSpawner.ChosenName)) { return BotReflex.Handle(text2, speaker, BotSpawner.BotPosition); } BotSocial.ExpectRealAnswer(); if ((Object)(object)speaker != (Object)null && Plugin.CfgBuddyOnlyCommands != null && Plugin.CfgBuddyOnlyCommands.Value) { PlayerAvatar current = BotBuddy.Current; if ((Object)(object)current != (Object)null && current != speaker) { Plugin.Log.LogInfo((object)("[Cmd] Ignoring " + SemiFunc.PlayerGetName(speaker) + " — he's attached to " + SemiFunc.PlayerGetName(current) + ".")); BotPersona.Speak("notmyboss", BotPersona.NotMyBoss, 1f, 15f, BotSpawner.BotPosition, SemiFunc.PlayerGetName(current)); return true; } } Ctx ctx = new Ctx { Speaker = speaker, BotPos = BotSpawner.BotPosition, Text = text2 }; if (BotGrudge.IsSulkingAt(speaker)) { Plugin.Log.LogInfo((object)("[Cmd] Sulking — ignoring " + SemiFunc.PlayerGetName(speaker) + ".")); BotPersona.Speak("snubbed", BotPersona.Snubbed, 1f, 6f, ctx.BotPos); return true; } if (BotGrudge.JudgeMessage(text2, speaker, ctx.BotPos)) { return true; } if (DangerNearby(ctx.BotPos)) { BotPersona.Speak("shush", BotPersona.Shush, 1f, 8f, ctx.BotPos, ((Object)(object)speaker != (Object)null) ? SemiFunc.PlayerGetName(speaker) : null); } for (int i = 0; i < Table.Count; i++) { Intent intent = Table[i]; for (int j = 0; j < intent.Phrases.Length; j++) { if (Matches(text2, intent.Phrases[j])) { Plugin.Log.LogInfo((object)("[Cmd] " + intent.Name + " <- \"" + text + "\"")); BotIdle.CommandHeard(intent.Name); intent.Run(ctx); return true; } } } Plugin.Log.LogInfo((object)("[Cmd] (unmatched) <- \"" + text + "\"")); if (BotMovement.InLobby()) { string aboutName = (((Object)(object)speaker != (Object)null) ? SemiFunc.PlayerGetName(speaker) : null); BotPersona.Speak("insult", BotPersona.Insult, 0.4f, 20f, ctx.BotPos, aboutName); } else { BotPersona.Speak("confused", BotPersona.Confused, 0.35f, 25f, ctx.BotPos); } return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Cmd] " + ex.Message)); return false; } } private static PlayerAvatar NearestHurtOther(Vector3 botPos, PlayerAvatar except) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar result = null; float num = 1f; try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player) || player == except || Reflect.Get(player, "isDisabled") || Vector3.Distance(botPos, ((Component)player).transform.position) > 15f) { continue; } PlayerHealth playerHealth = player.playerHealth; if ((Object)(object)playerHealth == (Object)null) { continue; } int num2 = Reflect.Get(playerHealth, "maxHealth"); if (num2 > 0) { float num3 = (float)Reflect.Get(playerHealth, "health") / (float)num2; if (num3 < num) { num = num3; result = player; } } } } catch { } return result; } private static bool DangerNearby(Vector3 botPos) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { if (BotDanger.Level != BotDanger.Threat.Clear) { return true; } if ((Object)(object)EnemyDirector.instance == (Object)null) { return false; } foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if (!((Object)(object)item == (Object)null)) { Enemy val = Reflect.Get(item, "Enemy"); if (!((Object)(object)val == (Object)null) && Vector3.Distance(botPos, ((Component)val).transform.position) <= 16f) { return true; } } } } catch { } return false; } private static bool Matches(string text, string phrase) { if (string.IsNullOrEmpty(phrase)) { return false; } if (phrase.IndexOf(' ') >= 0) { return text.IndexOf(phrase, StringComparison.Ordinal) >= 0; } int startIndex = 0; while (true) { int num = text.IndexOf(phrase, startIndex, StringComparison.Ordinal); if (num < 0) { return false; } bool num2 = num == 0 || !IsWordChar(text[num - 1]); int num3 = num + phrase.Length; bool flag = num3 >= text.Length || !IsWordChar(text[num3]); if (num2 && flag) { break; } startIndex = num + 1; } return true; } private static bool IsWordChar(char c) { if (!char.IsLetterOrDigit(c)) { return c == '\''; } return true; } public static bool LooksLikeCommand(string lowerText) { if (string.IsNullOrEmpty(lowerText)) { return false; } for (int i = 0; i < Table.Count; i++) { for (int j = 0; j < Table[i].Phrases.Length; j++) { if (Matches(lowerText, Table[i].Phrases[j])) { return true; } } } return false; } public static IEnumerable AllPhrases() { yield return "bertram"; foreach (Intent item in Table) { string[] phrases = item.Phrases; for (int i = 0; i < phrases.Length; i++) { yield return phrases[i]; } } } [HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSendRPC")] [HarmonyPostfix] public static void OnChatMessage(PlayerAvatar __instance, string _message) { try { if (!PhotonNetwork.IsMasterClient || string.IsNullOrEmpty(_message) || ((Object)(object)__instance != (Object)null && BotSpawner.IsBot(__instance))) { return; } int num = 1; for (int i = 0; i < _message.Length; i++) { if (_message[i] == ' ') { num++; } } float num2 = Mathf.Clamp((float)num * 0.32f + 0.35f, 0.35f, 7f); _pendingChat.Add(new PendingChat { Text = _message, Who = __instance, At = Time.unscaledTime + num2 }); } catch { } } public static void TickPendingChat() { try { if (_pendingChat.Count == 0) { return; } float unscaledTime = Time.unscaledTime; for (int num = _pendingChat.Count - 1; num >= 0; num--) { if (!(unscaledTime < _pendingChat[num].At)) { PendingChat pendingChat = _pendingChat[num]; _pendingChat.RemoveAt(num); Handle(pendingChat.Text, pendingChat.Who); } } } catch { } } } public static class BotComposer { private sealed class Core { public readonly string Text; public readonly string[] Needs; public Core(string text, params string[] needs) { Text = text; Needs = needs; } } private static readonly string[] Openers = new string[13] { "", "", "", "", "Right, ", "So. ", "Okay. ", "Look, ", "For what it's worth, ", "Not to be a bore, but ", "Genuine question: ", "Hm. ", "Anyway. " }; private static readonly string[] Hedges = new string[10] { "", "", "", "", "", "", "I think ", "probably ", "if I'm reading this right, ", "roughly " }; private static readonly string[] Riders = new string[13] { "", "", "", "", "", "", " Just so we're all aware.", " Not that anyone asked.", " Carry on.", " That's my contribution.", " Do with that what you will.", " I'll be over here.", " Anyway." }; private static readonly Core[] Ambient = new Core[18] { new Core("we're {short} short of quota.", "short"), new Core("{haul} banked, {goal} wanted. You do the maths.", "haul", "goal"), new Core("that's {haul} so far. Underwhelming.", "haul"), new Core("we've been down here {mins} minutes.", "mins"), new Core("{mins} minutes and {haul}. Not a great rate.", "mins", "haul"), new Core("{done} of {total} extractions done.", "done", "total"), new Core("{name} is the only one of you doing anything.", "name"), new Core("I'm on {hp} health, since nobody asked.", "hp"), new Core("{name} has broken {n} things tonight.", "name", "n"), new Core("that's {broke} broken between you. A record?", "broke"), new Core("I'm still following {buddy}. Lucky them.", "buddy"), new Core("{buddy} has wandered off again.", "buddy"), new Core("last thing I saw was a {enemy}. Sleep well.", "enemy"), new Core("no sign of that {enemy} for a bit.", "enemy"), new Core("{down} of you are down. That's not ideal.", "down"), new Core("this {item} is worth {value} and I'm the one carrying it.", "item", "value"), new Core("carrying {value} of {item}. No pressure.", "value", "item"), new Core("{name} is {dist} metres away and hasn't helped once.", "name", "dist") }; private static readonly Core[] Breakage = new Core[6] { new Core("{name} just destroyed a {item}. {value} gone.", "name", "item", "value"), new Core("that {item} was worth {value}. Was.", "item", "value"), new Core("{name}, that's {n} now.", "name", "n"), new Core("{value} lighter, thanks to {name}.", "value", "name"), new Core("we're {short} short and {name} is smashing the furniture.", "short", "name"), new Core("that's {broke} things broken tonight.", "broke") }; private static readonly Core[] Status = new Core[6] { new Core("{hp} health, {haul} banked, {short} to go.", "hp", "haul", "short"), new Core("{hp} out of {hpmax}. I've had better runs.", "hp", "hpmax"), new Core("alive, which is more than {down} of you can say.", "down"), new Core("I've died {deaths} times and I'm still here. Think about that.", "deaths"), new Core("following {buddy}, carrying nothing, quietly judging.", "buddy"), new Core("holding {value} of {item} and wishing I wasn't.", "value", "item") }; private static readonly Dictionary Slots = new Dictionary(); private static readonly List Usable = new List(); private static readonly Queue Recent = new Queue(); private static string _pending; private static float _pendingAt; private static readonly string[] SecondThoughts = new string[7] { "Actually, ignore me.", "...although I could be wrong.", "Don't quote me on that.", "Actually no, that's worse than I said.", "Forget it.", "Sorry, thinking out loud.", "That came out ruder than intended. Slightly." }; public static string Ambient1() { return Compose(Ambient); } public static string OnBreakage() { return Compose(Breakage); } public static string StatusReport() { return Compose(Status); } private static string Compose(Core[] pool) { try { BuildSlots(); Usable.Clear(); foreach (Core core in pool) { bool flag = true; string[] needs = core.Needs; foreach (string key in needs) { if (!Slots.ContainsKey(key)) { flag = false; break; } } if (flag && !Recent.Contains(core.Text)) { Usable.Add(core); } } if (Usable.Count == 0) { return null; } Core core2 = Usable[Random.Range(0, Usable.Count)]; Recent.Enqueue(core2.Text); while (Recent.Count > 8) { Recent.Dequeue(); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Openers[Random.Range(0, Openers.Length)]); string text = core2.Text; foreach (KeyValuePair slot in Slots) { text = text.Replace("{" + slot.Key + "}", slot.Value); } string text2 = Hedges[Random.Range(0, Hedges.Length)]; if (text2.Length > 0 && text.Length > 0) { text = text2 + text; } if (stringBuilder.Length == 0 && text.Length > 0) { text = char.ToUpperInvariant(text[0]) + text.Substring(1); } stringBuilder.Append(text); stringBuilder.Append(Riders[Random.Range(0, Riders.Length)]); return stringBuilder.ToString(); } catch { return null; } } private static void BuildSlots() { Slots.Clear(); if (BotFacts.HaveMoneyFacts) { Slots["haul"] = BotFacts.Money(BotFacts.Haul); Slots["goal"] = BotFacts.Money(BotFacts.Goal); if (BotFacts.Shortfall > 0) { Slots["short"] = BotFacts.Money(BotFacts.Shortfall); } } if (BotFacts.ExtractionsTotal > 0) { Slots["done"] = BotFacts.ExtractionsDone.ToString(); Slots["total"] = BotFacts.ExtractionsTotal.ToString(); } if (BotFacts.HpMax > 0) { Slots["hp"] = BotFacts.Hp.ToString(); Slots["hpmax"] = BotFacts.HpMax.ToString(); } if (BotFacts.Deaths > 0) { Slots["deaths"] = BotFacts.Deaths.ToString(); } if (BotFacts.MinutesHere >= 1f) { Slots["mins"] = Mathf.FloorToInt(BotFacts.MinutesHere).ToString(); } if (!string.IsNullOrEmpty(BotFacts.BuddyName)) { Slots["buddy"] = BotFacts.BuddyName; } if (!string.IsNullOrEmpty(BotFacts.NearestName)) { Slots["name"] = BotFacts.NearestName; Slots["dist"] = Mathf.RoundToInt(BotFacts.NearestDist).ToString(); int num = BotFacts.BreakagesBy(BotFacts.NearestName); if (num > 0) { Slots["n"] = num.ToString(); } } if (BotFacts.PlayersDown > 0) { Slots["down"] = BotFacts.PlayersDown.ToString(); } if (BotFacts.TotalBreakages > 0) { Slots["broke"] = BotFacts.TotalBreakages.ToString(); } if (!string.IsNullOrEmpty(BotFacts.CarryingName) && BotFacts.CarryingValue > 0) { Slots["item"] = BotFacts.CarryingName; Slots["value"] = BotFacts.Money(BotFacts.CarryingValue); } if (!string.IsNullOrEmpty(BotFacts.LastEnemy) && BotFacts.SinceEnemy < 120f) { Slots["enemy"] = BotFacts.LastEnemy; } } public static void Afterthought(string line, float delay = 2.2f) { _pending = line; _pendingAt = Time.unscaledTime + delay; } public static void MaybeSecondThought(Vector3 pos, float chance = 0.18f) { if (!(Random.value > chance)) { Afterthought(SecondThoughts[Random.Range(0, SecondThoughts.Length)]); } } public static void Tick(Vector3 pos) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (_pending != null && !(Time.unscaledTime < _pendingAt)) { string pending = _pending; _pending = null; BotPersona.SpeakLiteral(pending, pos); } } public static void Reset() { _pending = null; Recent.Clear(); } } public static class BotCustomVoice { private struct Formant { public float F1; public float F2; public float F3; public Formant(float f1, float f2, float f3) { F1 = f1; F2 = f2; F3 = f3; } } private const int SampleRate = 22050; private static readonly Dictionary Phonemes = new Dictionary { { "a", new Formant(730f, 1090f, 2440f) }, { "e", new Formant(530f, 1840f, 2480f) }, { "i", new Formant(390f, 1990f, 2550f) }, { "o", new Formant(570f, 840f, 2410f) }, { "u", new Formant(300f, 870f, 2240f) }, { "ea", new Formant(270f, 2290f, 3010f) }, { "ou", new Formant(450f, 950f, 2300f) }, { "ai", new Formant(600f, 1700f, 2500f) }, { "oo", new Formant(320f, 800f, 2200f) }, { "r", new Formant(400f, 1300f, 1690f) }, { "l", new Formant(420f, 1100f, 2800f) }, { "m", new Formant(280f, 900f, 2200f) }, { "n", new Formant(280f, 1700f, 2600f) } }; public static void Speak(string text, AudioSource targetSource) { if (string.IsNullOrEmpty(text) || (Object)(object)targetSource == (Object)null) { return; } try { float[] array = GenerateAudioBuffer(text); if (array != null && array.Length != 0) { AudioClip val = AudioClip.Create("BertramCustomVoice_" + Random.Range(100, 999), array.Length, 1, 22050, false); val.SetData(array, 0); targetSource.Stop(); targetSource.clip = val; targetSource.spatialBlend = 1f; targetSource.minDistance = 2f; targetSource.maxDistance = 25f; targetSource.volume = 1f; targetSource.Play(); Plugin.Log.LogInfo((object)("[CustomVoice] Spoke line (" + array.Length + " samples): \"" + text + "\"")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[CustomVoice] Synthesis error: " + ex.Message)); } } private static float[] GenerateAudioBuffer(string text) { string text2 = text.ToLowerInvariant(); List list = new List(66150); float num = 130f; Random random = new Random(text.GetHashCode()); float num2 = 0f; float num3 = 0f; float num4 = 0f; float num5 = 0f; int num6 = 0; int length = text2.Length; while (num6 < length) { char c = text2[num6]; if (c == ' ' || c == ',' || c == '.' || c == '!' || c == '?') { int num7 = ((c == ' ') ? 1543 : 4410); for (int i = 0; i < num7; i++) { list.Add(0f); } num6++; continue; } string text3 = c.ToString(); if (num6 + 1 < length) { string text4 = text2.Substring(num6, 2); if (Phonemes.ContainsKey(text4)) { text3 = text4; num6++; } } num6++; float num8 = 1f; if (text2.EndsWith("?")) { num8 = 1.15f; } else if (text2.EndsWith(".")) { num8 = 0.95f; } float num9 = num * num8; int num10 = 1874; if (Phonemes.TryGetValue(text3, out var value)) { float f = value.F1; float f2 = value.F2; float f3 = value.F3; for (int j = 0; j < num10; j++) { float num11 = 1f; float num12 = (float)j / (float)num10; if (num12 < 0.12f) { num11 = num12 / 0.12f; } else if (num12 > 0.88f) { num11 = (1f - num12) / 0.12f; } float num13 = ((num5 < 0.35f) ? (num5 / 0.35f) : ((1f - num5) / 0.65f)); num5 += num9 / 22050f; if (num5 >= 1f) { num5 -= 1f; } num2 += f / 22050f; if (num2 >= 1f) { num2 -= 1f; } num3 += f2 / 22050f; if (num3 >= 1f) { num3 -= 1f; } num4 += f3 / 22050f; if (num4 >= 1f) { num4 -= 1f; } float num14 = Mathf.Sin(num2 * 2f * MathF.PI) * 0.45f; float num15 = Mathf.Sin(num3 * 2f * MathF.PI) * 0.3f; float num16 = Mathf.Sin(num4 * 2f * MathF.PI) * 0.15f; float item = (num13 * 0.25f + num14 + num15 + num16) * num11 * 0.35f; list.Add(item); } continue; } int num17; switch (text3) { default: num17 = ((text3 == "ch") ? 1 : 0); break; case "s": case "z": case "f": case "v": case "h": case "th": case "sh": num17 = 1; break; } bool flag = (byte)num17 != 0; int num18; switch (text3) { default: num18 = ((text3 == "g") ? 1 : 0); break; case "t": case "k": case "p": case "b": case "d": num18 = 1; break; } bool flag2 = (byte)num18 != 0; int num19 = (flag2 ? 992 : 1543); for (int k = 0; k < num19; k++) { float num20 = 1f; float num21 = (float)k / (float)num19; if (num21 < 0.1f) { num20 = num21 / 0.1f; } else if (num21 > 0.8f) { num20 = (1f - num21) / 0.2f; } float num22 = (float)(random.NextDouble() * 2.0 - 1.0); if (flag) { float item2 = num22 * num20 * 0.22f; list.Add(item2); continue; } if (flag2) { float num23 = (((float)k < 264.6f) ? (num22 * 0.38f) : 0f); list.Add(num23 * num20); continue; } num2 += 0.009070295f; if (num2 >= 1f) { num2 -= 1f; } float num24 = Mathf.Sin(num2 * 2f * MathF.PI) * 0.22f + num22 * 0.04f; list.Add(num24 * num20); } } return list.ToArray(); } } public static class BotDance { private static GameObject _rig; private static Transform _ball; private static readonly List _beams = new List(); private static float _until; private static float _bob; private const float DefaultSeconds = 30f; private const float SpinDegPerSec = 38f; private static bool _remote; public static bool Dancing { get; private set; } public static void Reset() { _remote = false; Stop(announce: false); } public static void OnRemoteDance(bool on, string track) { try { GameObject val = (((Object)(object)BotSpawner.BotAvatar != (Object)null) ? ((Component)((Component)BotSpawner.BotAvatar).transform.root).gameObject : null); if (!on) { _remote = false; Stop(announce: false); } else { if ((Object)(object)val == (Object)null) { return; } _remote = true; Dancing = true; _until = Time.unscaledTime + 3600f; Build(val); if (!string.IsNullOrEmpty(track)) { string text = BotJukebox.Find(track); if (text != null) { BotJukebox.Play(text); } else { Plugin.Log.LogInfo((object)("[Dance] Host is playing '" + track + "' but this machine hasn't got it — dancing dry.")); } } Plugin.Log.LogInfo((object)"[Dance] Disco mirrored from the host."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Dance] Remote: " + ex.Message)); } } public static void ClientTick() { if (!_remote || !Dancing) { return; } try { PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botAvatar == (Object)null) { Stop(announce: false); } else { Spin(((Component)((Component)botAvatar).transform.root).gameObject, Time.deltaTime, drive: false); } } catch { } } public static void RequestSong(string spokenText, Vector3 botPos) { //IL_001f: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) try { string want = StripLeadIn(spokenText); if (string.IsNullOrEmpty(want)) { Start(botPos); return; } string text = BotJukebox.Find(want); if (text != null) { Start(botPos, 30f, text); return; } if (string.IsNullOrEmpty((Plugin.CfgMidiUrl != null) ? Plugin.CfgMidiUrl.Value : "")) { BotPersona.SpeakExact(BotPersona.SongNotFound, botPos, want); return; } Start(botPos, 30f, null, silent: true); ((MonoBehaviour)Plugin.Instance).StartCoroutine(BotJukebox.Fetch(want, delegate(string got) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) Vector3 botPosition = BotSpawner.BotPosition; if (got != null) { BotPersona.SpeakExact(BotPersona.DanceWithMusic, botPosition, got); } else { BotPersona.SpeakExact(BotPersona.SongNotFound, botPosition, want); } })); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Dance] RequestSong: " + ex.Message)); } } private static string StripLeadIn(string text) { if (string.IsNullOrEmpty(text)) { return null; } string text2 = text.ToLowerInvariant(); string[] array = new string[4] { "play some ", "put on ", "play ", "dj " }; foreach (string text3 in array) { int num = text2.IndexOf(text3, StringComparison.Ordinal); if (num >= 0) { return text2.Substring(num + text3.Length).Trim(); } } return null; } public static void Start(Vector3 botPos, float seconds = 30f, string specificTrack = null, bool silent = false) { //IL_0057: 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) try { GameObject botObject = BotSpawner.BotObject; if ((Object)(object)botObject == (Object)null) { return; } Dancing = true; _until = Time.unscaledTime + seconds; Build(botObject); string text = ((specificTrack != null) ? BotJukebox.Play(specificTrack) : BotJukebox.PlayRandom()); if (!silent) { if (text != null) { BotPersona.SpeakExact(BotPersona.DanceWithMusic, botPos, text); } else { BotPersona.SpeakExact(BotPersona.DanceNoMusic, botPos); } } BotNet.BroadcastDance(on: true, text); Plugin.Log.LogInfo((object)("[Dance] Disco engaged" + ((text != null) ? (" with '" + text + "'.") : " (no music found)."))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Dance] " + ex.Message)); } } public static void Stop(bool announce) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_rig != (Object)null) { Object.Destroy((Object)(object)_rig); } _rig = null; _ball = null; _beams.Clear(); BotJukebox.Stop(); if (Dancing && !_remote) { BotNet.BroadcastDance(on: false, null); } if (Dancing && announce) { BotPersona.SpeakExact(BotPersona.DanceOver, BotSpawner.BotPosition); } Dancing = false; } catch { } } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { if (!Dancing || (Object)(object)bot == (Object)null) { return false; } try { if (Time.unscaledTime > _until || Reflect.Get(avatar, "isDisabled")) { Stop(announce: true); return false; } BotMovement.Stop(); BotMovement.SpeedScale = 0f; Spin(bot, dt, drive: true); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Dance] " + ex.Message)); return false; } } private static void Spin(GameObject bot, float dt, bool drive) { //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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) if ((Object)(object)bot == (Object)null) { return; } if (drive) { BotMovement.Stop(); BotMovement.SpeedScale = 0f; BotMovement.SpinYaw(38f * dt); _bob += dt * 6f; BotMovement.ForceCrouch = Mathf.Sin(_bob) > 0.45f; } if ((Object)(object)_rig != (Object)null) { _rig.transform.position = bot.transform.position + Vector3.up * 2.6f; if ((Object)(object)_ball != (Object)null) { _ball.Rotate(0f, 90f * dt, 0f, (Space)1); } for (int i = 0; i < _beams.Count; i++) { Light val = _beams[i]; if (!((Object)(object)val == (Object)null)) { ((Component)val).transform.RotateAround(_rig.transform.position, Vector3.up, ((i % 2 == 0) ? 70f : (-95f)) * dt); val.intensity = 4f + Mathf.Sin(Time.time * 9f + (float)i) * 2.5f; } } } BotJukebox.Tick(bot.transform.position); } private static void Build(GameObject bot) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rig != (Object)null) { return; } _rig = new GameObject("BotSpike.Disco"); _rig.transform.position = bot.transform.position + Vector3.up * 2.6f; GameObject obj = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)obj).name = "Ball"; obj.transform.SetParent(_rig.transform, false); obj.transform.localScale = Vector3.one * 0.45f; Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = obj.GetComponent(); if ((Object)(object)component2 != (Object)null) { Material val = BorrowMaterial(); if ((Object)(object)val != (Object)null) { component2.material = val; TrySetColor(component2.material, new Color(0.75f, 0.78f, 0.85f)); TrySetFloat(component2.material, "_Metallic", 1f); TrySetFloat(component2.material, "_Glossiness", 0.95f); TrySetFloat(component2.material, "_Smoothness", 0.95f); } } _ball = obj.transform; Color[] array = (Color[])(object)new Color[4] { new Color(1f, 0.2f, 0.3f), new Color(0.2f, 0.6f, 1f), new Color(0.3f, 1f, 0.4f), new Color(1f, 0.85f, 0.2f) }; for (int i = 0; i < array.Length; i++) { GameObject val2 = new GameObject("Beam" + i); val2.transform.SetParent(_rig.transform, false); float num = (float)i * MathF.PI * 0.5f; val2.transform.localPosition = new Vector3(Mathf.Cos(num) * 0.9f, 0f, Mathf.Sin(num) * 0.9f); val2.transform.LookAt(_rig.transform.position + Vector3.down * 2f); Light val3 = val2.AddComponent(); val3.type = (LightType)0; val3.color = array[i]; val3.range = 14f; val3.spotAngle = 26f; val3.intensity = 5f; val3.shadows = (LightShadows)0; _beams.Add(val3); } } private static Material BorrowMaterial() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown try { MeshRenderer[] array = Object.FindObjectsOfType(); foreach (MeshRenderer val in array) { if (!((Object)(object)val == (Object)null) && ((Renderer)val).enabled) { Material sharedMaterial = ((Renderer)val).sharedMaterial; if ((Object)(object)sharedMaterial != (Object)null && (Object)(object)sharedMaterial.shader != (Object)null) { return new Material(sharedMaterial); } } } } catch { } try { return new Material(Shader.Find("Standard")); } catch { return null; } } private static void TrySetColor(Material m, Color c) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) try { if (m.HasProperty("_BaseColor")) { m.SetColor("_BaseColor", c); } if (m.HasProperty("_Color")) { m.SetColor("_Color", c); } } catch { } } private static void TrySetFloat(Material m, string prop, float v) { try { if (m.HasProperty(prop)) { m.SetFloat(prop, v); } } catch { } } } public static class BotDanger { public enum Threat { Clear, Wary, Hiding } private static float _pollTimer; private static float _hideHold; private static Vector3 _hideSpot; private static bool _haveHideSpot; private static Enemy _threatEnemy; private static float _hidingFor; private const float NoticeRange = 18f; private const float HuntedSense = 14f; private const float PanicRange = 8f; private const float MeleeSense = 3.5f; private const float HideHoldSeconds = 5f; private const float MaxHideSeconds = 12f; public static Threat Level { get; private set; } public static bool WantsCrouch { get; private set; } public static void Reset() { Level = Threat.Clear; WantsCrouch = false; _haveHideSpot = false; _hideHold = 0f; _hidingFor = 0f; _threatEnemy = null; } public static void ForceHide(float seconds) { Level = Threat.Hiding; WantsCrouch = true; _hideHold = seconds; _hidingFor = 0f; _haveHideSpot = false; } public static void ForceWary() { Level = Threat.Wary; WantsCrouch = true; _hideHold = 0f; _hidingFor = 0f; _haveHideSpot = false; } public static bool Tick(GameObject bot, float dt) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null) { return false; } Vector3 position = bot.transform.position; _pollTimer -= dt; if (_pollTimer <= 0f) { _pollTimer = 0.1f; Assess(position); } if (_hideHold > 0f) { _hideHold -= dt; } switch (Level) { case Threat.Clear: WantsCrouch = false; _hidingFor = 0f; return false; case Threat.Wary: WantsCrouch = true; return false; case Threat.Hiding: _hidingFor += dt; if (_hidingFor > 12f) { Plugin.Log.LogInfo((object)"[Danger] Hidden long enough — getting back to work."); _hidingFor = 0f; _hideHold = 0f; Level = Threat.Clear; _haveHideSpot = false; WantsCrouch = false; return false; } WantsCrouch = true; if (!_haveHideSpot) { _haveHideSpot = FindHideSpot(position, out _hideSpot); } if (_haveHideSpot) { if (Vector3.Distance(position, _hideSpot) > 1.2f) { BotMovement.SetDestination(_hideSpot); } else { BotMovement.Stop(); } } return true; default: return false; } } private static bool CanSee(Vector3 from, Vector3 target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = from + Vector3.up * 1.2f; _ = target + Vector3.up * 0.8f - val; int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Linecast(val, target + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1); } catch { return true; } } public static bool IsMinorThreat(EnemyParent ep, Enemy e) { try { if ((Object)(object)ep == (Object)null) { return false; } string text = BotScout.EnemyDisplayName(ep); if (string.IsNullOrEmpty(text) || text == "something") { text = ((Object)ep).name; if (string.IsNullOrEmpty(text) && (Object)(object)e != (Object)null) { text = ((Object)e).name; } } if (string.IsNullOrEmpty(text)) { return false; } string text2 = text.ToLowerInvariant(); if (text2.Contains("birthday") || text2.Contains("bike") || text2.Contains("trike") || text2.Contains("tricycle") || text2.Contains("duck") || text2.Contains("hugger") || text2.Contains("puker") || text2.Contains("spewer") || text2.Contains("face") || text2.Contains("sucker") || text2.Contains("gaggle") || text2.Contains("gnome") || text2.Contains("snare") || text2.Contains("crawler")) { return true; } } catch { } return false; } private static void Assess(Vector3 pos) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Invalid comparison between Unknown and I4 //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Invalid comparison between Unknown and I4 Threat level = Level; _threatEnemy = null; float num = float.MaxValue; bool flag = false; bool flag2 = false; try { if ((Object)(object)EnemyDirector.instance == (Object)null) { Level = Threat.Clear; return; } foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if ((Object)(object)item == (Object)null) { continue; } Enemy val = Reflect.Get(item, "Enemy"); if ((Object)(object)val == (Object)null) { continue; } bool flag3 = IsMinorThreat(item, val); Transform transform = ((Component)val).transform; float num2 = Vector3.Distance(pos, transform.position); if (!(num2 >= num)) { EnemyState currentState = val.CurrentState; bool flag4 = (int)currentState == 4 || (int)currentState == 3 || (int)currentState == 5; if ((CanSee(pos, transform.position + Vector3.up * 0.7f) || (flag4 && num2 < 14f) || num2 < 3.5f) && (!flag3 || !((Object)(object)_threatEnemy != (Object)null) || flag2)) { num = num2; _threatEnemy = val; flag2 = flag3; flag = flag4; } } } } catch { } if ((Object)(object)_threatEnemy == (Object)null || num > 18f) { Level = ((_hideHold > 0f) ? Threat.Wary : Threat.Clear); if (Level == Threat.Clear) { _haveHideSpot = false; } } else if (flag2) { Level = Threat.Wary; WantsCrouch = true; _hideHold = 0f; } else if (num <= 8f || flag) { Level = Threat.Hiding; _hideHold = 5f; } else { Level = Threat.Wary; } if (Level != level) { Plugin.Log.LogInfo((object)("[Danger] " + level.ToString() + " -> " + Level.ToString() + (((Object)(object)_threatEnemy != (Object)null) ? (" (nearest " + num.ToString("F1") + "m" + (flag2 ? " [minor]" : "") + ")") : ""))); if (Level == Threat.Hiding) { _haveHideSpot = false; BotChatter.SayDanger(); } } } private static bool FindHideSpot(Vector3 from, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) spot = from; if ((Object)(object)_threatEnemy == (Object)null) { return false; } Vector3 position = ((Component)_threatEnemy).transform.position; Vector3 val = position + Vector3.up * 1.2f; int blockers = BotSight.Blockers; float num = float.MinValue; bool result = false; NavMeshHit val3 = default(NavMeshHit); for (int i = 0; i < 14; i++) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(3f, 11f); if (!NavMesh.SamplePosition(from + new Vector3(val2.x, 0f, val2.y), ref val3, 3f, -1)) { continue; } Vector3 position2 = ((NavMeshHit)(ref val3)).position; if (Physics.Linecast(val, position2 + Vector3.up * 0.6f, blockers)) { float num2 = Vector3.Distance(position2, position); float num3 = Vector3.Distance(position2, from); float num4 = num2 - num3 * 0.5f; if (num4 > num) { num = num4; spot = position2; result = true; } } } return result; } } [HarmonyPatch] public static class BotDeath { private static bool _reportedDeath; private const float FuseSeconds = 1.3f; public static void ClearDeathReport() { _reportedDeath = false; } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")] [HarmonyPostfix] public static void DeathRpcPostfix(PlayerAvatar __instance) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)__instance == (Object)null) && BotSpawner.IsBot(__instance)) { Reflect.Set(__instance, "deadSet", true); Reflect.Set(__instance, "isDisabled", true); Reflect.Set(__instance, "deadTimer", 1.3f); if (Time.unscaledTime - BotMedic.SacrificedAt < 3f) { Plugin.Log.LogInfo((object)"[Death] Went out helping — leaving his sacrifice line alone."); return; } BotPersona.SpeakExact(BotPersona.DyingGasp, ((Component)__instance).transform.position); Plugin.Log.LogWarning((object)("[Death] He was killed. Fuse " + 1.3f + "s, then the bang.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Death] " + ex.Message)); } } [HarmonyPatch(typeof(PlayerDeathHead), "Trigger")] [HarmonyPrefix] public static void HeadTriggerPrefix(PlayerDeathHead __instance) { try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.playerAvatar : null); if (!((Object)(object)val == (Object)null) && BotSpawner.IsBot(val) && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null) { Plugin.Log.LogWarning((object)"[Death] No tumble body at death — rebuilding before the head trips over it."); BotSpawner.EnsureBodyParts(val); } } catch { } } [HarmonyPatch(typeof(PlayerDeathHead), "Trigger")] [HarmonyFinalizer] public static Exception HeadTriggerFinalizer(PlayerDeathHead __instance, Exception __exception) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (__exception == null) { return null; } try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.playerAvatar : null); if ((Object)(object)val == (Object)null || !BotSpawner.IsBot(val)) { return __exception; } Plugin.Log.LogWarning((object)("[Death] His death head threw (" + __exception.Message + ") — finishing it by hand so the explosion still happens.")); Reflect.Set(__instance, "triggeredPosition", ((Component)val).transform.position + Vector3.up * 0.5f); Reflect.Set(__instance, "triggeredRotation", ((Component)val).transform.rotation); Reflect.Set(__instance, "triggeredTimer", 0.1f); Reflect.Set(__instance, "triggered", true); PhysGrabObject val2 = Reflect.Get(__instance, "physGrabObject"); if ((Object)(object)val2 != (Object)null) { val2.DisableDeathPitEffect(5f); } return null; } catch { return __exception; } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] [HarmonyPostfix] public static void DeathDonePostfix(PlayerAvatar __instance) { try { if (!((Object)(object)__instance == (Object)null) && BotSpawner.IsBot(__instance)) { Reflect.Set(__instance, "deadSet", true); Reflect.Set(__instance, "isDisabled", true); if (!_reportedDeath) { _reportedDeath = true; Plugin.Log.LogWarning((object)("[Death] Death completed. isDisabled=" + Reflect.Get(__instance, "isDisabled") + " deadSet=" + Reflect.Get(__instance, "deadSet") + " active=" + ((Component)__instance).gameObject.activeSelf + " head=" + (Reflect.Get(__instance, "playerDeathHead") != null))); } } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")] [HarmonyPostfix] public static void RevivePostfix(PlayerAvatar __instance) { try { if (!((Object)(object)__instance == (Object)null) && BotSpawner.IsBot(__instance)) { Plugin.Log.LogWarning((object)("[Death] HE WAS REVIVED. Called from:" + Environment.NewLine + new StackTrace(1, fNeedFileInfo: false))); } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] [HarmonyFinalizer] public static Exception DeathDoneFinalizer(PlayerAvatar __instance, Exception __exception) { if (__exception == null) { return null; } try { if ((Object)(object)__instance == (Object)null || !BotSpawner.IsBot(__instance)) { return __exception; } Plugin.Log.LogWarning((object)("[Death] PlayerDeathDone threw (" + __exception.Message + ") — blowing him up manually.")); PlayerDeathEffects val = Reflect.Get(__instance, "playerDeathEffects"); if ((Object)(object)val != (Object)null && !Reflect.Get(val, "triggered")) { val.Trigger(); } Reflect.Set(__instance, "isDisabled", true); ((Component)__instance).gameObject.SetActive(false); return null; } catch { return __exception; } } } [HarmonyPatch] public static class BotDeathHead { private static float _checkAt; [HarmonyPatch(typeof(PlayerDeathHead), "SetupRPC")] [HarmonyPrefix] public static bool SetupGuard(PlayerDeathHead __instance, string _steamID) { try { if (!BotSpawner.HasBot || (Object)(object)GameDirector.instance == (Object)null) { return true; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !(Reflect.Get(player, "steamID") != _steamID)) { object obj = Reflect.Get(player, "playerDeathHead"); PlayerDeathHead val = (PlayerDeathHead)((obj is PlayerDeathHead) ? obj : null); if ((Object)(object)val == (Object)null || val == __instance) { return true; } Plugin.Log.LogWarning((object)("[Head] Refused a death head claiming to be '" + _steamID + "' — that player already has a different head. Letting it through would have handed them somebody else's face and left the original head unable to revive them.")); return false; } } } catch { } return true; } private static void HideHead(PlayerDeathHead head) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)head == (Object)null) { return; } int num = 0; Renderer[] componentsInChildren = ((Component)head).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled) { val.enabled = false; num++; } } ParticleSystem[] componentsInChildren2 = ((Component)head).GetComponentsInChildren(true); foreach (ParticleSystem val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { EmissionModule emission = val2.emission; if (((EmissionModule)(ref emission)).enabled) { ((EmissionModule)(ref emission)).enabled = false; num++; } } } Light[] componentsInChildren3 = ((Component)head).GetComponentsInChildren(true); foreach (Light val3 in componentsInChildren3) { if ((Object)(object)val3 != (Object)null && ((Behaviour)val3).enabled) { ((Behaviour)val3).enabled = false; num++; } } if (num > 0) { Plugin.Log.LogInfo((object)("[Head] Hid his death head (" + num + " visuals). He explodes and leaves nothing — the object stays because Revive() needs somewhere to put him, but there is no corpse to look at or carry.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Head] HideHead: " + ex.Message)); } } public static void Tick() { try { if (Time.unscaledTime < _checkAt) { return; } _checkAt = Time.unscaledTime + 3f; PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botAvatar == (Object)null || (Object)(object)GameDirector.instance == (Object)null) { return; } object obj = Reflect.Get(botAvatar, "playerDeathHead"); PlayerDeathHead val = (PlayerDeathHead)((obj is PlayerDeathHead) ? obj : null); if ((Object)(object)val == (Object)null) { return; } HideHead(val); if (val.playerAvatar != botAvatar) { Plugin.Log.LogWarning((object)("[Head] His death head was bound to '" + (((Object)(object)val.playerAvatar != (Object)null) ? SemiFunc.PlayerGetName(val.playerAvatar) : "nobody") + "' — rebinding it to him. This is what made his head wear somebody else's cosmetics.")); val.playerAvatar = botAvatar; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && (object)/*isinst with value type is only supported in some contexts*/ == val) { Plugin.Log.LogError((object)("[Head] " + SemiFunc.PlayerGetName(player) + " had HIS death head registered as theirs. Clearing it — a player pointing at the wrong head cannot be revived by it.")); Reflect.Set(player, "playerDeathHead", null); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Head] " + ex.Message)); } } } public static class BotEars { private enum Phase { Off, MissingFiles, LoadingModel, StartMic, WaitMic, Listening, Failed } private struct Heard { public int SpeakerId; public string Text; public float Peak; } private sealed class SpeakerRec { public IntPtr Rec; public float Peak; public string Name; public string LastFired; public int LastPartialFrame; public string LastPartialLogged; } private static Phase _phase = Phase.Off; private static IntPtr _model = IntPtr.Zero; private static IntPtr _rec = IntPtr.Zero; private static Thread _worker; private static volatile bool _run; private static AudioClip _clip; private static string _device; private static int _micRate; private static int _lastPos; private static float[] _pump = new float[8192]; private static readonly ConcurrentQueue _in = new ConcurrentQueue(); private static readonly ConcurrentQueue _out = new ConcurrentQueue(); private static float _uttPeak; private static string _modelDir; private static string _libDir; private static float _micWaitStart; private static string[] _aliases = Array.Empty(); private static float _quietHintAt; private const float WakeWindow = 6f; private static readonly Dictionary _armed = new Dictionary(); private static readonly Dictionary _recs = new Dictionary(); private static string _grammar; private static string _grammarMode = "free-form"; private static bool _micTried; private static volatile int _flushSpeaker = -1; public static string Availability => _phase switch { Phase.Listening => null, Phase.Off => "voice commands are disabled in the config (Voice.VoiceCommands)", Phase.MissingFiles => "libvosk.dll or the vosk-model folder was not found next to BotSpike.dll", Phase.LoadingModel => "the speech model is still loading (give it a few seconds)", Phase.Failed => "the speech engine failed to start — see the earlier [Ears] error", _ => "the microphone is still starting up", }; private static int LocalSpeakerId { get { try { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance != (Object)null) { return ((Object)instance).GetInstanceID(); } } catch { } return -1; } } private static string StripName(string text) { try { if (string.IsNullOrEmpty(text)) { return text; } string[] array = text.Split(' '); StringBuilder stringBuilder = new StringBuilder(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text2 = array2[i].Trim(); if (text2.Length != 0 && !NameMatcher.Mentions(text2, BotSpawner.ChosenName)) { if (stringBuilder.Length > 0) { stringBuilder.Append(' '); } stringBuilder.Append(text2); } } return (stringBuilder.Length > 0) ? stringBuilder.ToString() : text; } catch { return text; } } [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibraryW(string path); [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetDllDirectoryW(string path); [DllImport("libvosk")] private static extern void vosk_set_log_level(int level); [DllImport("libvosk")] private static extern IntPtr vosk_model_new(string path); [DllImport("libvosk")] private static extern void vosk_model_free(IntPtr model); [DllImport("libvosk")] private static extern IntPtr vosk_recognizer_new(IntPtr model, float rate); [DllImport("libvosk")] private static extern IntPtr vosk_recognizer_new_grm(IntPtr model, float rate, string grammar); [DllImport("libvosk")] private static extern void vosk_recognizer_free(IntPtr rec); [DllImport("libvosk")] private static extern int vosk_recognizer_accept_waveform_f(IntPtr rec, float[] data, int length); [DllImport("libvosk")] private static extern IntPtr vosk_recognizer_result(IntPtr rec); [DllImport("libvosk")] private static extern IntPtr vosk_recognizer_partial_result(IntPtr rec); [DllImport("libvosk")] private static extern IntPtr vosk_recognizer_final_result(IntPtr rec); [DllImport("libvosk")] private static extern void vosk_recognizer_reset(IntPtr rec); public static void Init() { try { if (Plugin.CfgVoiceCommands == null || !Plugin.CfgVoiceCommands.Value) { _phase = Phase.Off; return; } string directoryName = Path.GetDirectoryName(typeof(BotEars).Assembly.Location); _libDir = FindFileDir(directoryName, "libvosk.dll"); _modelDir = ((Plugin.CfgVoiceModelPath != null && !string.IsNullOrEmpty(Plugin.CfgVoiceModelPath.Value)) ? Plugin.CfgVoiceModelPath.Value : FindModelDir(directoryName)); _aliases = ((Plugin.CfgWakeAliases != null) ? Plugin.CfgWakeAliases.Value : "bertram").ToLowerInvariant().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < _aliases.Length; i++) { _aliases[i] = _aliases[i].Trim(); } if (_libDir == null || _modelDir == null) { _phase = Phase.MissingFiles; Plugin.Log.LogWarning((object)("[Ears] Voice commands OFF — " + ((_libDir == null) ? "libvosk.dll not found. " : "") + ((_modelDir == null) ? "no vosk-model* folder found. " : "") + "Put them next to BotSpike.dll (" + directoryName + ").")); } else { _phase = Phase.LoadingModel; _run = true; Thread thread = new Thread(LoadModelThread); thread.IsBackground = true; thread.Name = "BotSpike.VoskLoad"; thread.Start(); Plugin.Log.LogInfo((object)("[Ears] Loading speech model in the background: " + _modelDir)); } } catch (Exception ex) { _phase = Phase.Failed; Plugin.Log.LogError((object)("[Ears] Init failed: " + ex)); } } private static void LoadModelThread() { try { SetDllDirectoryW(_libDir); if (LoadLibraryW(Path.Combine(_libDir, "libvosk.dll")) == IntPtr.Zero) { Plugin.Log.LogError((object)("[Ears] LoadLibrary(libvosk.dll) failed (err " + Marshal.GetLastWin32Error() + ").")); _phase = Phase.Failed; return; } vosk_set_log_level(-1); float num = Environment.TickCount; _model = vosk_model_new(_modelDir.Replace('\\', '/')); if (_model == IntPtr.Zero) { Plugin.Log.LogError((object)"[Ears] vosk_model_new failed — model folder unreadable or wrong format."); _phase = Phase.Failed; return; } if (Plugin.CfgVoiceGrammar != null && !Plugin.CfgVoiceGrammar.Value) { Plugin.Log.LogWarning((object)"[Ears] Voice.KeywordGrammar is OFF, so recognition is free-form and will be poor. This is the single biggest accuracy setting there is — turn it on."); } if (Plugin.CfgVoiceGrammar == null || Plugin.CfgVoiceGrammar.Value) { string grammar = BuildGrammar(withName: true); _rec = vosk_recognizer_new_grm(_model, 16000f, grammar); if (_rec != IntPtr.Zero) { _grammar = grammar; _grammarMode = "keyword grammar (name included)"; } if (_rec == IntPtr.Zero) { string grammar2 = BuildGrammar(Vetted(BuildWordSet(withName: true))); _rec = vosk_recognizer_new_grm(_model, 16000f, grammar2); if (_rec != IntPtr.Zero) { _grammar = grammar2; _grammarMode = "keyword grammar (vetted word list)"; } } if (_rec == IntPtr.Zero) { Plugin.Log.LogWarning((object)"[Ears] Every grammar attempt failed — falling back to free-form, which on a model this small is the weak setting. Recognition will be poor and there is nothing further to try here."); } } if (_rec == IntPtr.Zero) { _rec = vosk_recognizer_new(_model, 16000f); } if (_rec == IntPtr.Zero) { Plugin.Log.LogError((object)"[Ears] vosk_recognizer_new failed."); _phase = Phase.Failed; return; } Plugin.Log.LogInfo((object)("[Ears] Model ready in " + (((float)Environment.TickCount - num) / 1000f).ToString("F1") + "s — mode: " + _grammarMode + ".")); _worker = new Thread(WorkerLoop) { IsBackground = true, Name = "BotSpike.VoskWork" }; _worker.Start(); _phase = Phase.Listening; Plugin.Log.LogInfo((object)"[Ears] LISTENING to every player's in-game voice — including dead ones. No microphone is opened; this reads the audio the game already decodes."); } catch (Exception ex) { _phase = Phase.Failed; Plugin.Log.LogError((object)("[Ears] Model load failed: " + ex)); } } private static IntPtr NewRecognizer() { if (_grammar != null) { try { IntPtr intPtr = vosk_recognizer_new_grm(_model, 16000f, _grammar); if (intPtr != IntPtr.Zero) { return intPtr; } } catch { } } return vosk_recognizer_new(_model, 16000f); } private static HashSet Vetted(HashSet candidates) { HashSet hashSet = new HashSet(); List list = new List(); int tickCount = Environment.TickCount; foreach (string candidate in candidates) { IntPtr intPtr = IntPtr.Zero; try { intPtr = vosk_recognizer_new_grm(_model, 16000f, "[\"" + candidate + "\",\"[unk]\"]"); } catch { } if (intPtr != IntPtr.Zero) { hashSet.Add(candidate); try { vosk_recognizer_free(intPtr); } catch { } } else { list.Add(candidate); } } if (list.Count > 0) { Plugin.Log.LogWarning((object)("[Ears] The speech model has no pronunciation for " + list.Count + " of our command words, and a single one of those rejects the whole grammar. Dropped: " + string.Join(", ", list.ToArray()) + ". Any phrase using those can no longer be recognised — reword them in BotCommands if they matter.")); } Plugin.Log.LogInfo((object)("[Ears] Vetted " + candidates.Count + " words in " + (Environment.TickCount - tickCount) + "ms; " + hashSet.Count + " usable.")); return hashSet; } private static HashSet BuildWordSet(bool withName) { HashSet hashSet = new HashSet(); if (withName) { hashSet.Add("bertram"); } string[] array; foreach (string item2 in BotCommands.AllPhrases()) { array = item2.ToLowerInvariant().Split(' '); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && (withName || !(text == "bertram"))) { hashSet.Add(text); } } } array = new string[117] { "would", "could", "should", "will", "shall", "can", "may", "might", "must", "please", "thanks", "thank", "sorry", "hey", "hi", "ok", "okay", "yeah", "yes", "no", "you", "your", "yours", "we", "us", "our", "i", "me", "my", "he", "she", "they", "them", "the", "a", "an", "this", "that", "these", "those", "it", "its", "to", "for", "of", "with", "and", "or", "but", "if", "so", "then", "than", "on", "off", "in", "out", "up", "down", "over", "under", "here", "there", "back", "just", "maybe", "please", "quick", "quickly", "now", "soon", "again", "still", "do", "does", "did", "be", "is", "are", "was", "were", "been", "get", "got", "go", "want", "need", "like", "mind", "think", "know", "able", "let", "lets", "make", "bit", "little", "some", "any", "all", "one", "two", "very", "really", "actually", "what", "where", "when", "why", "who", "how", "not", "dont", "cant", "wont", "isnt", "wasnt" }; foreach (string item in array) { hashSet.Add(item); } if (Plugin.CfgWakeAliases != null) { array = Plugin.CfgWakeAliases.Value.ToLowerInvariant().Split(','); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Trim().Split(' '); for (int j = 0; j < array2.Length; j++) { string text2 = array2[j].Trim(); if (text2.Length > 0 && text2 != "bertram") { hashSet.Add(text2); } } } } return hashSet; } private static string BuildGrammar(HashSet words) { StringBuilder stringBuilder = new StringBuilder("["); foreach (string word in words) { if (word != null && word.Length != 0) { stringBuilder.Append('"').Append(word.Replace("\"", "")).Append('"') .Append(','); } } stringBuilder.Append("\"[unk]\"]"); return stringBuilder.ToString(); } private static string BuildGrammar(bool withName) { return BuildGrammar(BuildWordSet(withName)); } public static void Tick() { switch (_phase) { case Phase.StartMic: try { if (Microphone.devices == null || Microphone.devices.Length == 0) { Plugin.Log.LogWarning((object)"[Ears] No microphone devices visible to Unity — voice commands off. (If the game's voice chat holds the mic exclusively on your setup, this is that.)"); _phase = Phase.Failed; break; } _clip = Microphone.Start(_device, true, 10, 16000); _micWaitStart = Time.unscaledTime; _lastPos = 0; _phase = Phase.WaitMic; break; } catch (Exception ex) { Plugin.Log.LogError((object)("[Ears] Microphone.Start failed: " + ex.Message)); _phase = Phase.Failed; break; } case Phase.WaitMic: if ((Object)(object)_clip == (Object)null) { _phase = Phase.Failed; break; } if (Microphone.GetPosition(_device) <= 0) { if (Time.unscaledTime - _micWaitStart > 5f) { Plugin.Log.LogWarning((object)"[Ears] Microphone never produced data (held exclusively by something else?) — voice commands off."); Microphone.End(_device); _phase = Phase.Failed; } break; } _micRate = _clip.frequency; _phase = Phase.Listening; Plugin.Log.LogInfo((object)("[Ears] LISTENING on '" + (_device ?? "default mic") + "' at " + _micRate + "Hz. Say \"Bertram, ...\" — same phrases as chat.")); try { Plugin.Log.LogInfo((object)("[Ears] Devices available: " + string.Join(" | ", Microphone.devices) + " (set Voice.Device if the default is not the one you speak into)")); break; } catch { break; } case Phase.Listening: BotVoiceTap.Tick(Time.deltaTime); PumpMic(); DrainResults(); break; } } private static void PumpMic() { try { if (Plugin.CfgOwnMic == null || !Plugin.CfgOwnMic.Value) { return; } if ((Object)(object)_clip == (Object)null) { if (!_micTried) { _micTried = true; TryStartMic(); } return; } int position = Microphone.GetPosition(_device); if (position == _lastPos) { return; } int samples = _clip.samples; int num = (position - _lastPos + samples) % samples; if (num <= 0) { return; } if (num > samples - 1) { _lastPos = position; return; } float[] array = new float[num]; if (_lastPos + num <= samples) { _clip.GetData(array, _lastPos); } else { int num2 = samples - _lastPos; float[] array2 = new float[num2]; float[] array3 = new float[num - num2]; _clip.GetData(array2, _lastPos); _clip.GetData(array3, 0); Array.Copy(array2, 0, array, 0, num2); Array.Copy(array3, 0, array, num2, array3.Length); } _lastPos = position; BotVoiceTap.Submit(new BotVoiceTap.Chunk { SpeakerId = LocalSpeakerId, Samples = array, Rate = ((_micRate > 0) ? _micRate : 16000) }); } catch { } } private static void TryStartMic() { try { if (Microphone.devices == null || Microphone.devices.Length == 0) { Plugin.Log.LogWarning((object)"[Ears] No microphone visible to Unity."); return; } _device = ((Plugin.CfgVoiceDevice != null && !string.IsNullOrEmpty(Plugin.CfgVoiceDevice.Value)) ? Plugin.CfgVoiceDevice.Value : null); if (_device != null && Array.IndexOf(Microphone.devices, _device) < 0) { Plugin.Log.LogWarning((object)("[Ears] Voice.Device '" + _device + "' is not in the device list (" + string.Join(" | ", Microphone.devices) + ") — using the default.")); _device = null; } _clip = Microphone.Start(_device, true, 10, 16000); _lastPos = 0; _micRate = (((Object)(object)_clip != (Object)null) ? _clip.frequency : 16000); Plugin.Log.LogWarning((object)("[Ears] Voice.OwnMicrophone is ON — opened '" + (_device ?? "default") + "' at " + _micRate + "Hz. BE AWARE: the game records the same device through the same Unity API, and Unity allows one capture per device. If your voice chat stops working, this is why — turn it back off.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Ears] Could not open your microphone (" + ex.Message + ").")); } } public static void OpenIntercom() { try { lock (_recs) { if (_recs.TryGetValue(LocalSpeakerId, out var value) && value.Rec != IntPtr.Zero) { vosk_recognizer_reset(value.Rec); value.LastFired = null; value.Peak = 0f; } } } catch { } } public static void CloseIntercom() { _flushSpeaker = LocalSpeakerId; } private static void FlushPending() { int flushSpeaker = _flushSpeaker; if (flushSpeaker == -1) { return; } _flushSpeaker = -1; try { SpeakerRec value; lock (_recs) { if (!_recs.TryGetValue(flushSpeaker, out value)) { return; } } if (value == null || value.Rec == IntPtr.Zero) { return; } string text = ExtractText(Marshal.PtrToStringUTF8(vosk_recognizer_final_result(value.Rec))); float peak = value.Peak; value.Peak = 0f; if (string.IsNullOrEmpty(text)) { if (Plugin.CfgVoiceVerbose != null && Plugin.CfgVoiceVerbose.Value) { Plugin.Log.LogInfo((object)"[Ears] Key released with nothing decoded — no words in that one."); } return; } value.LastFired = text; _out.Enqueue(new Heard { SpeakerId = flushSpeaker, Text = text, Peak = peak }); Plugin.Log.LogInfo((object)("[Ears] Key released — committed \"" + text + "\".")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Ears] Flush: " + ex.Message)); } } private static void DrainResults() { Heard result; while (_out.TryDequeue(out result)) { string text = result.Text; float peak = result.Peak; if (string.IsNullOrEmpty(text)) { continue; } PlayerAvatar val = FindSpeaker(result.SpeakerId); string text2 = " " + text + " "; string[] aliases = _aliases; foreach (string text3 in aliases) { if (!(text3 == "bertram") && text3.Length != 0 && text2.Contains(text3)) { text2 = text2.Replace(text3, "bertram"); } } text2 = text2.Trim(); int num = 0; int num2 = 0; aliases = text2.Split(' '); foreach (string text4 in aliases) { if (text4.Length != 0) { num2++; if (text4 == "[unk]" || text4 == "unk") { num++; } } } if (num2 > 0 && num * 5 >= num2 * 3) { if (Plugin.CfgVoiceVerbose != null && Plugin.CfgVoiceVerbose.Value) { Plugin.Log.LogInfo((object)("[Ears] Ignored \"" + text + "\" — mostly unrecognised words, so that was somebody talking about something else.")); } continue; } text2 = text2.Replace("[unk]", " ").Replace(" ", " ").Trim(); if (text2.Length == 0) { continue; } bool num3 = NameMatcher.Mentions(text2, BotSpawner.ChosenName); _armed.TryGetValue(result.SpeakerId, out var value); if (num3) { value = (_armed[result.SpeakerId] = Time.unscaledTime + 6f); } bool flag = result.SpeakerId == LocalSpeakerId && BotIntercom.RecentlyOpen; bool flag2 = num3 || flag || Time.unscaledTime < value; if (!flag && Plugin.CfgIntercomOnly != null && Plugin.CfgIntercomOnly.Value) { if (flag2) { Plugin.Log.LogInfo((object)("[Ears] Heard \"" + text + "\" addressed to him, but Voice.IntercomOnly is on — hold the key to give him orders.")); } continue; } if (flag2) { text2 = StripName(text2); } string text5 = (((Object)(object)val != (Object)null) ? SemiFunc.PlayerGetName(val) : "someone"); Plugin.Log.LogInfo((object)("[Ears] " + text5 + ": \"" + text + "\" (peak " + peak.ToString("F3") + ")" + (flag ? " → INTERCOM" : (flag2 ? " → dispatching" : "")))); if (peak < 0.02f && Time.unscaledTime > _quietHintAt) { _quietHintAt = Time.unscaledTime + 30f; Plugin.Log.LogWarning((object)("[Ears] The microphone stream is effectively SILENT (peak " + peak.ToString("F4") + "; speech should reach 0.1-0.5). Nothing is wrong with the recognizer — it is being fed silence. Either the default device is not the mic you speak into, or the game holds it exclusively. Available devices were listed at startup as '[Ears] Microphones:' — set Voice.Device to one of those names.")); } if (flag2) { if (!flag) { _armed.Remove(result.SpeakerId); } if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { BotNet.SendVoiceCommand(text2, peak, flag); } else if (flag) { BotCommands.Handle(text2, val ?? PlayerAvatar.instance, addressed: true); } else { DispatchWithEarshot(text2, peak, val ?? PlayerAvatar.instance); } } } } private static void DispatchWithEarshot(string text, float peak, PlayerAvatar speaker) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Plugin.CfgVoiceEarshot == null || !Plugin.CfgVoiceEarshot.Value || !BotSpawner.HasBot) { BotCommands.Handle(text, speaker, addressed: true); return; } if ((Object)(object)speaker == (Object)null) { BotCommands.Handle(text, null, addressed: true); return; } BotHearing.Result result = BotHearing.CanHear(speaker, BotSpawner.BotPosition, peak); if (result.Heard) { BotCommands.Handle(text, speaker, addressed: true); return; } Plugin.Log.LogInfo((object)("[Ears] " + SemiFunc.PlayerGetName(speaker) + " said \"" + text + "\" — not heard: " + result.Why + " (" + result.Distance.ToString("F0") + "m vs " + result.Range.ToString("F0") + "m).")); } private static PlayerAvatar FindSpeaker(int instanceId) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null && ((Object)player).GetInstanceID() == instanceId) { return player; } } } catch { } return null; } public static void OnRemoteVoiceCommand(int senderActor, string text, float peak, bool intercom = false) { try { if (!PhotonNetwork.IsMasterClient || string.IsNullOrEmpty(text)) { return; } PlayerAvatar speaker = null; if ((Object)(object)GameDirector.instance != (Object)null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { PhotonView component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null && component.Owner != null && component.Owner.ActorNumber == senderActor) { speaker = player; break; } } } } Plugin.Log.LogInfo((object)("[Ears] Client voice command from actor " + senderActor + ": \"" + text + "\"" + (intercom ? " (INTERCOM — held the key)" : ""))); if (intercom) { BotCommands.Handle(text, speaker, addressed: true); } else { DispatchWithEarshot(text, peak, speaker); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Ears] Remote command: " + ex.Message)); } } private static void WorkerLoop() { while (_run) { FlushPending(); if (!BotVoiceTap.TryDequeue(out var c)) { Thread.Sleep(15); continue; } float[] samples = c.Samples; if (samples == null || samples.Length == 0) { continue; } try { SpeakerRec value; lock (_recs) { if (!_recs.TryGetValue(c.SpeakerId, out value)) { value = new SpeakerRec { Rec = NewRecognizer() }; _recs[c.SpeakerId] = value; } } if (value.Rec == IntPtr.Zero) { continue; } int rate = c.Rate; for (int i = 0; i < samples.Length; i++) { float num = ((samples[i] < 0f) ? (0f - samples[i]) : samples[i]); if (num > value.Peak) { value.Peak = num; } } float[] array = ((rate > 0 && rate != 16000) ? Resample(samples, rate) : samples); for (int j = 0; j < array.Length; j++) { array[j] *= 32767f; } bool flag = vosk_recognizer_accept_waveform_f(value.Rec, array, array.Length) == 1; if (Plugin.CfgVoiceVerbose != null && Plugin.CfgVoiceVerbose.Value) { try { string text = ExtractField(Marshal.PtrToStringUTF8(vosk_recognizer_partial_result(value.Rec)), "partial"); if (!string.IsNullOrEmpty(text) && text != value.LastPartialLogged) { value.LastPartialLogged = text; Plugin.Log.LogInfo((object)("[Ears/raw] partial: \"" + text + "\"")); } else if (flag && string.IsNullOrEmpty(text)) { Plugin.Log.LogInfo((object)("[Ears/raw] utterance ended with NO words (peak " + value.Peak.ToString("F3") + ", rate " + rate + ").")); } } catch { } } if (flag) { string text2 = ExtractText(Marshal.PtrToStringUTF8(vosk_recognizer_result(value.Rec))); float peak = value.Peak; value.Peak = 0f; if (!string.IsNullOrEmpty(text2) && !SameAsLast(value, text2)) { value.LastFired = text2; _out.Enqueue(new Heard { SpeakerId = c.SpeakerId, Text = text2, Peak = peak }); } } else if (Time.frameCount != value.LastPartialFrame) { value.LastPartialFrame = Time.frameCount; string text3 = ExtractField(Marshal.PtrToStringUTF8(vosk_recognizer_partial_result(value.Rec)), "partial"); if (!string.IsNullOrEmpty(text3) && text3.Length >= 6 && !SameAsLast(value, text3) && LooksActionable(text3)) { value.LastFired = text3; float peak2 = value.Peak; value.Peak = 0f; _out.Enqueue(new Heard { SpeakerId = c.SpeakerId, Text = text3, Peak = peak2 }); vosk_recognizer_reset(value.Rec); } } } catch (Exception ex) { Plugin.Log.LogError((object)("[Ears] Worker: " + ex.Message)); _run = false; } } } private static float[] Resample(float[] src, int fromRate) { int num = (int)((long)src.Length * 16000L / fromRate); if (num <= 0) { return Array.Empty(); } float[] array = new float[num]; float num2 = (float)fromRate / 16000f; for (int i = 0; i < num; i++) { int num3 = (int)((float)i * num2); int num4 = (int)((float)(i + 1) * num2); if (num4 <= num3) { num4 = num3 + 1; } if (num4 > src.Length) { num4 = src.Length; } if (num3 >= num4) { array[i] = 0f; continue; } float num5 = 0f; for (int j = num3; j < num4; j++) { num5 += src[j]; } array[i] = num5 / (float)(num4 - num3); } return array; } private static bool SameAsLast(SpeakerRec sr, string text) { if (sr.LastFired != null) { return string.Equals(sr.LastFired, text, StringComparison.Ordinal); } return false; } private static bool LooksActionable(string partial) { try { if (!NameMatcher.Mentions(partial, BotSpawner.ChosenName)) { return false; } return BotCommands.LooksLikeCommand(partial); } catch { return false; } } private static string ExtractField(string json, string field) { if (string.IsNullOrEmpty(json)) { return null; } string text = "\"" + field + "\""; int num = json.IndexOf(text, StringComparison.Ordinal); if (num < 0) { return null; } int num2 = json.IndexOf('"', num + text.Length); if (num2 < 0) { return null; } int num3 = json.IndexOf('"', num2 + 1); if (num3 < 0) { return null; } return json.Substring(num2 + 1, num3 - num2 - 1).Trim(); } private static string ExtractText(string json) { if (string.IsNullOrEmpty(json)) { return null; } int num = json.IndexOf("\"text\"", StringComparison.Ordinal); if (num < 0) { return null; } int num2 = json.IndexOf('"', num + 6 + 1); if (num2 < 0) { return null; } int num3 = json.IndexOf('"', num2 + 1); if (num3 < 0) { return null; } return json.Substring(num2 + 1, num3 - num2 - 1).Trim(); } public static void Shutdown() { _run = false; try { if (_worker != null) { _worker.Join(500); } } catch { } try { lock (_recs) { foreach (KeyValuePair rec in _recs) { if (rec.Value.Rec != IntPtr.Zero) { vosk_recognizer_free(rec.Value.Rec); } } _recs.Clear(); } } catch { } try { if (_rec != IntPtr.Zero) { vosk_recognizer_free(_rec); } } catch { } try { if (_model != IntPtr.Zero) { vosk_model_free(_model); } } catch { } _rec = IntPtr.Zero; _model = IntPtr.Zero; _phase = Phase.Off; } private static string FindFileDir(string baseDir, string file) { try { if (File.Exists(Path.Combine(baseDir, file))) { return baseDir; } string[] directories = Directory.GetDirectories(baseDir); foreach (string text in directories) { if (File.Exists(Path.Combine(text, file))) { return text; } } } catch { } return null; } private static string FindModelDir(string baseDir) { try { List list = new List(); string[] directories = Directory.GetDirectories(baseDir, "vosk-model*"); foreach (string item in directories) { list.Add(item); } directories = Directory.GetDirectories(baseDir); for (int i = 0; i < directories.Length; i++) { string[] directories2 = Directory.GetDirectories(directories[i], "vosk-model*"); foreach (string item2 in directories2) { list.Add(item2); } } if (list.Count == 0) { return null; } string text = null; int num = int.MinValue; foreach (string item3 in list) { string text2 = Path.GetFileName(item3).ToLowerInvariant(); int num2 = 0; if (text2.Contains("lgraph")) { num2 += 100; } if (text2.Contains("small")) { num2 -= 50; } if (num2 > num) { num = num2; text = item3; } } if (list.Count > 1) { Plugin.Log.LogWarning((object)("[Ears] " + list.Count + " speech models present: " + string.Join(", ", list.ConvertAll(Path.GetFileName).ToArray()) + ". Using '" + Path.GetFileName(text) + "'. Set Voice.ModelPath to override.")); } return text; } catch { } return null; } } public static class BotEntrance { private static Vector3 _hidePos; private static bool _haveHidePos; private static object _room; private static float _timeout; private static float _pollTimer; private static float _retryUntil; private static float _retryAt; private const float ProximityWake = 9f; private const float MaxDormant = 210f; public static bool Dormant { get; private set; } public static void MaybeRetry() { if (Dormant || _retryUntil <= 0f) { return; } if (Time.unscaledTime > _retryUntil) { _retryUntil = 0f; } else if (!(Time.unscaledTime < _retryAt)) { _retryAt = Time.unscaledTime + 2f; if (Prepare()) { _retryUntil = 0f; } } } public static void Reset() { Dormant = false; _retryUntil = 0f; _haveHidePos = false; _room = null; _timeout = 0f; } public unsafe static bool Prepare() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) try { if (Plugin.CfgHiddenEntrance == null || !Plugin.CfgHiddenEntrance.Value) { return false; } int totalPoints; ExtractionPoint val = NearestToTruck(out totalPoints); if ((Object)(object)val == (Object)null) { _retryUntil = Time.unscaledTime + 12f; Plugin.Log.LogWarning((object)("[Entrance] Found " + totalPoints + " extraction point(s) right now — retrying for 12s before he just joins the crew.")); return false; } Plugin.Log.LogInfo((object)("[Entrance] Hiding him in '" + ((Object)val).name + "'.")); _retryUntil = 0f; _hidePos = BotChute.StandingSpot(val); if (_hidePos == Vector3.zero) { Plugin.Log.LogWarning((object)("[Entrance] Couldn't find a standable spot in '" + ((Object)val).name + "' — he'll just start with the crew.")); Dormant = false; return false; } if (!BotChute.LooksStandable(_hidePos)) { Plugin.Log.LogWarning((object)("[Entrance] The spot in '" + ((Object)val).name + "' looks enclosed. Hiding him there anyway, but if he goes missing at level start, this is where to look.")); } _haveHidePos = true; _room = FindRoom(val); _timeout = 210f; Dormant = true; BotSpawner.TeleportBots(_hidePos); BotMovement.Reset(); BotMovement.Stop(); ManualLogSource log = Plugin.Log; Vector3 hidePos = _hidePos; log.LogInfo((object)("[Entrance] Tucked him into the extraction point at " + ((object)(*(Vector3*)(&hidePos))/*cast due to .constrained prefix*/).ToString() + ((_room != null) ? " (watching its room)" : " (no room — using proximity)") + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Entrance] " + ex.Message)); Dormant = false; return false; } } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (!Dormant) { return false; } if ((Object)(object)bot == (Object)null) { Dormant = false; return false; } if (_haveHidePos) { bot.transform.position = _hidePos; Rigidbody val = bot.GetComponent() ?? bot.GetComponentInChildren(true); if ((Object)(object)val != (Object)null) { val.velocity = Vector3.zero; val.angularVelocity = Vector3.zero; val.position = _hidePos; } } BotMovement.ForceCrouch = true; if ((Object)(object)avatar != (Object)null) { Reflect.Set(avatar, "clientPosition", _hidePos); Reflect.Set(avatar, "clientPositionCurrent", _hidePos); Reflect.Set(avatar, "isMoving", false); Reflect.Set(avatar, "isSprinting", false); Reflect.Set(avatar, "isCrouching", true); Reflect.Set(avatar, "isGrounded", true); Reflect.Set(avatar, "rbVelocity", Vector3.zero); } _timeout -= dt; if (_timeout <= 0f) { Wake("nobody ever came — letting him out anyway"); return false; } _pollTimer -= dt; if (_pollTimer > 0f) { return true; } _pollTimer = 0.25f; if (AnyHumanInExtraction()) { Wake("a crewmate reached the extraction room"); return false; } if (AnyPlayerNear(_hidePos, 9f)) { Wake("a player came within " + 9f.ToString("F0") + "m"); return false; } return true; } private static void Wake(string why) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) Dormant = false; BotMovement.ForceCrouch = false; BotMovement.Reset(); Plugin.Log.LogInfo((object)("[Entrance] Stepping out — " + why + ".")); BotPersona.SpeakExact(BotPersona.Emerging, _hidePos); Launch(); } private static void Launch() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { GameObject botObject = BotSpawner.BotObject; if ((Object)(object)botObject == (Object)null) { return; } Rigidbody val = botObject.GetComponent() ?? botObject.GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { return; } Vector3 val2 = Vector3.up * 5.5f; if (BotHands.NearestHuman(botObject.transform.position, out var pos)) { Vector3 val3 = pos - botObject.transform.position; val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude > 0.25f) { val2 += ((Vector3)(ref val3)).normalized * 2.2f; } } val.velocity = val2; Plugin.Log.LogInfo((object)("[Entrance] Launched him out of the chute at " + ((Vector3)(ref val2)).magnitude.ToString("F1") + " m/s — the flight is deliberate now, not leftover physics.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Entrance] Launch: " + ex.Message)); } } private static ExtractionPoint NearestToTruck(out int totalPoints) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) ExtractionPoint[] array = Object.FindObjectsOfType(); totalPoints = ((array != null) ? array.Length : 0); if (array == null || array.Length == 0) { return null; } Vector3 position; if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { position = ((Component)TruckSafetySpawnPoint.instance).transform.position; } else { if (!((Object)(object)PlayerAvatar.instance != (Object)null)) { return array[0]; } position = ((Component)PlayerAvatar.instance).transform.position; } ExtractionPoint result = null; float num = float.MaxValue; ExtractionPoint[] array2 = array; foreach (ExtractionPoint val in array2) { if (!((Object)(object)val == (Object)null)) { Transform val2 = (((Object)(object)val.safetySpawn != (Object)null) ? val.safetySpawn : ((Component)val).transform); float num2 = Vector3.Distance(position, val2.position); if (num2 < num) { num = num2; result = val; } } } return result; } private static object FindRoom(ExtractionPoint ep) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) try { RoomVolume componentInParent = ((Component)ep).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } Transform val = (((Object)(object)ep.safetySpawn != (Object)null) ? ep.safetySpawn : ((Component)ep).transform); RoomVolume val2 = null; float num = float.MaxValue; RoomVolume[] array = Object.FindObjectsOfType(); foreach (RoomVolume val3 in array) { if (!((Object)(object)val3 == (Object)null) && val3.Extraction) { float num2 = Vector3.Distance(val.position, ((Component)val3).transform.position); if (num2 < num) { num = num2; val2 = val3; } } } return (num < 30f) ? val2 : null; } catch { return null; } } private static bool AnyHumanInExtraction() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "isDisabled")) { object obj = Reflect.Get(player, "roomVolumeCheck"); if (obj != null && Reflect.Get(obj, "inExtractionPoint")) { return true; } } } } catch { } return false; } private static bool AnyPlayerNear(Vector3 pos, float range) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "isDisabled") && Vector3.Distance(pos, ((Component)player).transform.position) <= range) { return true; } } } catch { } return false; } } public static class BotEspeak { private delegate int SynthCallback(IntPtr wav, int numsamples, IntPtr events); private struct Pending { public float[] Samples; public int Rate; public AudioSource On; } private const int AUDIO_OUTPUT_RETRIEVAL = 1; private const int espeakRATE = 1; private const int espeakVOLUME = 2; private const int espeakPITCH = 3; private const uint espeakCHARS_UTF8 = 1u; private static bool _tried; private static bool _ready; private static int _rate; private static SynthCallback _callback; private static readonly List _pcm = new List(); private static readonly object _lock = new object(); private static readonly Queue _queue = new Queue(); public static bool Ready => _ready; public static bool Active { get { if (Plugin.CfgEspeak == null || !Plugin.CfgEspeak.Value) { return false; } if (!_ready) { return Init(); } return true; } } public static float SpeakingUntil { get; private set; } public static bool Speaking => Time.unscaledTime < SpeakingUntil; [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern int espeak_Initialize(int output, int buflen, IntPtr path, int options); [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern void espeak_SetSynthCallback(SynthCallback cb); [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern int espeak_SetVoiceByName([MarshalAs(UnmanagedType.LPUTF8Str)] string voice); [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern int espeak_SetParameter(int parameter, int value, int relative); [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern int espeak_Synth([MarshalAs(UnmanagedType.LPUTF8Str)] string text, int size, uint position, int position_type, uint end_position, uint flags, UIntPtr unique_identifier, IntPtr user_data); [DllImport("libespeak-ng", CallingConvention = CallingConvention.Cdecl)] private static extern int espeak_Synchronize(); [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibraryW(string path); private static bool Init() { if (_tried) { return _ready; } _tried = true; try { string text = FindVoiceDir(); if (text == null) { Plugin.Log.LogInfo((object)"[Espeak] No BotSpikeVoice folder found — staying on the game's built-in voice."); return false; } string text2 = Path.Combine(text, "libespeak-ng.dll"); if (!File.Exists(text2) || LoadLibraryW(text2) == IntPtr.Zero) { Plugin.Log.LogWarning((object)("[Espeak] Couldn't load " + text2 + " (err " + Marshal.GetLastWin32Error() + ").")); return false; } Plugin.Log.LogWarning((object)("[Espeak] init 1/3 — calling espeak_Initialize with data at " + text)); IntPtr intPtr = Marshal.StringToHGlobalAnsi(text); try { _rate = espeak_Initialize(1, 0, intPtr, 0); } finally { Marshal.FreeHGlobal(intPtr); } if (_rate <= 0) { Plugin.Log.LogWarning((object)("[Espeak] espeak_Initialize returned " + _rate + " — is espeak-ng-data present in " + text + "?")); return false; } Plugin.Log.LogWarning((object)("[Espeak] init 2/3 — espeak_Initialize returned " + _rate + "Hz. Setting the synth callback.")); _callback = OnSynth; espeak_SetSynthCallback(_callback); Plugin.Log.LogWarning((object)"[Espeak] init 3/3 — callback set."); _ready = true; Plugin.Log.LogWarning((object)("[Espeak] Ready at " + _rate + "Hz from '" + text + "'. Bertram will use eSpeak NG; every other voice in the game is untouched.")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Espeak] Init failed: " + ex.Message)); return false; } } private static string FindVoiceDir() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = directoryName; for (int i = 0; i < 6; i++) { if (text == null) { break; } string text2 = Path.Combine(text, "BotSpikeVoice"); if (Directory.Exists(Path.Combine(text2, "espeak-ng-data"))) { return text2; } text = Directory.GetParent(text)?.FullName; } string text3 = FindPayloadZip(directoryName); if (text3 == null) { return null; } string text4 = Path.Combine(Paths.BepInExRootPath, "BotSpikeVoice"); Plugin.Log.LogInfo((object)("[Espeak] First run — unpacking the voice payload to " + text4 + ".")); Directory.CreateDirectory(text4); ZipFile.ExtractToDirectory(text3, text4); return Directory.Exists(Path.Combine(text4, "espeak-ng-data")) ? text4 : null; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Espeak] Couldn't unpack the voice payload: " + ex.Message)); return null; } } private static string FindPayloadZip(string baseDir) { try { string text = Path.Combine(baseDir, "espeak-voice.zip"); if (File.Exists(text)) { return text; } string[] files = Directory.GetFiles(baseDir, "espeak-voice.zip", SearchOption.AllDirectories); int num = 0; if (num < files.Length) { return files[num]; } } catch { } return null; } private static int OnSynth(IntPtr wav, int numsamples, IntPtr events) { try { if (numsamples <= 0 || wav == IntPtr.Zero) { return 0; } short[] array = new short[numsamples]; Marshal.Copy(wav, array, 0, numsamples); _pcm.AddRange(array); } catch { } return 0; } private static short[] Render(string text, string voice, int pitch, int rate) { lock (_lock) { try { if (!_ready || string.IsNullOrEmpty(text)) { return null; } _pcm.Clear(); espeak_SetVoiceByName(string.IsNullOrEmpty(voice) ? "en+m3" : voice); espeak_SetParameter(3, Mathf.Clamp(pitch, 0, 99), 0); espeak_SetParameter(1, Mathf.Clamp(rate, 80, 450), 0); espeak_SetParameter(2, 200, 0); espeak_Synth(text, text.Length * 2 + 2, 0u, 0, 0u, 1u, UIntPtr.Zero, IntPtr.Zero); espeak_Synchronize(); return (_pcm.Count > 0) ? _pcm.ToArray() : null; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Espeak] Synth: " + ex.Message)); return null; } } } public static bool Speak(string text, AudioSource on) { if (!Active || (Object)(object)on == (Object)null || string.IsNullOrEmpty(text)) { return false; } string line = Clean(text); if (line.Length == 0) { return false; } string voice = ((Plugin.CfgEspeakVoice != null) ? Plugin.CfgEspeakVoice.Value : "en+m3"); int pitch = ((Plugin.CfgEspeakPitch != null) ? Plugin.CfgEspeakPitch.Value : 45); int rate = ((Plugin.CfgEspeakRate != null) ? Plugin.CfgEspeakRate.Value : 165); ThreadPool.QueueUserWorkItem(delegate { short[] array = Render(line, voice, pitch, rate); if (array == null || array.Length == 0) { return; } float[] array2 = new float[array.Length]; for (int i = 0; i < array.Length; i++) { array2[i] = (float)array[i] / 32768f; } lock (_queue) { _queue.Enqueue(new Pending { Samples = array2, Rate = _rate, On = on }); } }); return true; } public static void Tick() { try { if (Speaking) { return; } Pending pending; lock (_queue) { if (_queue.Count == 0) { return; } while (_queue.Count > 2) { _queue.Dequeue(); } pending = _queue.Dequeue(); } if (!((Object)(object)pending.On == (Object)null) && pending.Samples != null && pending.Samples.Length != 0) { AudioClip val = AudioClip.Create("Bertram", pending.Samples.Length, 1, pending.Rate, false); val.SetData(pending.Samples, 0); pending.On.PlayOneShot(val, 1f); SpeakingUntil = Time.unscaledTime + val.length + 0.25f; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Espeak] Play: " + ex.Message)); } } private static string Clean(string text) { try { string text2 = text.Trim(); while (text2.StartsWith("[") && text2.IndexOf(']') > 0) { text2 = text2.Substring(text2.IndexOf(']') + 1).Trim(); } return text2; } catch { return text; } } } [HarmonyPatch] public static class BotEspeakHook { private static AudioSource _voice; private static bool _announced; [HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSpeak")] [HarmonyPostfix] public static void SpeakInHisRealVoice(PlayerAvatar __instance, string _message) { try { if ((Object)(object)__instance == (Object)null || !BotSpawner.IsBot(__instance) || !BotEspeak.Active) { return; } object obj = Reflect.Get(__instance, "voiceChat"); MuteVanillaVoice((Component)((obj is Component) ? obj : null)); AudioSource val = EnsureSource(__instance); if (!((Object)(object)val == (Object)null)) { if (!_announced) { _announced = true; Plugin.Log.LogInfo((object)"[Espeak] Speaking as Bertram on this machine. Every client with the mod renders the same line locally, which is how the game's own TTS already works — no audio is networked."); } BotEspeak.Speak(_message, val); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Espeak] hook: " + ex.Message)); } } private static AudioSource EnsureSource(PlayerAvatar bot) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) try { object obj = Reflect.Get(bot, "voiceChat"); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } AudioSource val2 = Reflect.Get(val, "ttsAudioSource"); if ((Object)(object)_voice == (Object)null) { GameObject val3 = new GameObject("BotSpike.EspeakVoice"); val3.transform.SetParent(val.transform, false); val3.transform.localPosition = Vector3.zero; _voice = val3.AddComponent(); _voice.playOnAwake = false; _voice.loop = false; } else if ((Object)(object)((Component)_voice).transform.parent != (Object)(object)val.transform) { ((Component)_voice).transform.SetParent(val.transform, false); ((Component)_voice).transform.localPosition = Vector3.zero; } if ((Object)(object)val2 != (Object)null) { _voice.spatialBlend = val2.spatialBlend; _voice.minDistance = val2.minDistance; _voice.maxDistance = val2.maxDistance; _voice.rolloffMode = val2.rolloffMode; _voice.outputAudioMixerGroup = val2.outputAudioMixerGroup; } _voice.volume = 1f; _voice.mute = false; return _voice; } catch { return null; } } public static void MuteVanillaVoice(Component vcc) { try { if ((Object)(object)vcc == (Object)null) { return; } AudioSource val = Reflect.Get(vcc, "ttsAudioSource"); if (!((Object)(object)val == (Object)null)) { bool active = BotEspeak.Active; if (val.mute) { val.mute = false; } float num = (active ? 0f : 1f); if (!Mathf.Approximately(val.volume, num)) { val.volume = num; } object obj = Reflect.Get(vcc, "lowPassLogicTTS"); if (obj != null && !Mathf.Approximately(Reflect.Get(obj, "Volume"), num)) { Reflect.Set(obj, "Volume", num); } } } catch { } } public static void Reset() { _voice = null; _announced = false; } } public static class BotEyeContact { private const float Range = 4.5f; private const float HoldSeconds = 10f; private const float BotDot = 0.8f; private const float PlayerDot = 0.9f; private const float Cooldown = 90f; private static PlayerAvatar _staring; private static float _held; private static float _nextAt; public static void Reset() { _staring = null; _held = 0f; } public static void Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)bot == (Object)null || (Object)(object)avatar == (Object)null) { _staring = null; _held = 0f; return; } if (BotHands.Current != BotHands.Job.Idle || BotScout.Active || BotDanger.Level != BotDanger.Threat.Clear || BotHush.Active || Reflect.Get(avatar, "isDisabled")) { _staring = null; _held = 0f; return; } PlayerAvatar val = WhoIsStaring(bot, avatar); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)_staring) { _staring = val; _held = 0f; return; } _held += dt; if (!(_held < 10f) && !(Time.unscaledTime < _nextAt)) { _held = 0f; _nextAt = Time.unscaledTime + 90f; BotPersona.SpeakExact(BotPersona.EyeContact, bot.transform.position, SemiFunc.PlayerGetName(val)); BotComposer.MaybeSecondThought(bot.transform.position, 0.3f); Plugin.Log.LogInfo((object)("[Eyes] " + SemiFunc.PlayerGetName(val) + " held eye contact for ten seconds. He's said something about it.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Eyes] " + ex.Message)); } } private static PlayerAvatar WhoIsStaring(GameObject bot, PlayerAvatar avatar) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00e9: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null) { return null; } Vector3 val = bot.transform.position + Vector3.up * 1.3f; Vector3 forward = bot.transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { return null; } ((Vector3)(ref forward)).Normalize(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player) || Reflect.Get(player, "isDisabled")) { continue; } Transform val2 = ViewTransform(player); if ((Object)(object)val2 == (Object)null) { continue; } Vector3 position = val2.position; Vector3 val3 = val - position; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude > 4.5f || magnitude < 0.2f) { continue; } val3 /= magnitude; if (!(Vector3.Dot(val2.forward, val3) < 0.9f)) { Vector3 val4 = position - val; val4.y = 0f; if (!(((Vector3)(ref val4)).sqrMagnitude < 0.0001f) && !(Vector3.Dot(forward, ((Vector3)(ref val4)).normalized) < 0.8f) && !Blocked(val, position)) { return player; } } } return null; } private static Transform ViewTransform(PlayerAvatar p) { try { PlayerVisionTarget val = Reflect.Get(p, "PlayerVisionTarget"); if ((Object)(object)val != (Object)null && (Object)(object)val.VisionTransform != (Object)null) { return val.VisionTransform; } } catch { } return ((Component)p).transform; } private static bool Blocked(Vector3 a, Vector3 b) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { int blockers = BotSight.Blockers; if (blockers == 0) { return false; } return Physics.Linecast(a, b, blockers, (QueryTriggerInteraction)1); } catch { return false; } } } public static class BotFacts { public static int Haul; public static int Goal; public static int Shortfall; public static bool HaveMoneyFacts; public static int ExtractionsDone; public static int ExtractionsTotal; public static int Hp; public static int HpMax; public static int Deaths; public static bool Hurt; public static string BuddyName; public static string NearestName; public static float NearestDist; public static int PlayersUp; public static int PlayersDown; public static string CarryingName; public static int CarryingValue; public static string LastEnemy; public static float SinceEnemy = 999f; public static float MinutesHere; private static readonly Dictionary Breakages = new Dictionary(); public static int TotalBreakages; public static string WorstOffender; private static float _levelStart; private static float _refreshAt; public static void NewLevel() { _levelStart = Time.time; Breakages.Clear(); TotalBreakages = 0; WorstOffender = null; LastEnemy = null; SinceEnemy = 999f; } public static void NoteBreakage(string who) { if (string.IsNullOrEmpty(who)) { return; } Breakages.TryGetValue(who, out var value); Breakages[who] = value + 1; TotalBreakages++; int num = -1; foreach (KeyValuePair breakage in Breakages) { if (breakage.Value > num) { num = breakage.Value; WorstOffender = breakage.Key; } } } public static int BreakagesBy(string who) { if (who == null || !Breakages.TryGetValue(who, out var value)) { return 0; } return value; } public static void NoteEnemy(string name) { if (!string.IsNullOrEmpty(name)) { LastEnemy = name; SinceEnemy = 0f; } } public static void Refresh(PlayerAvatar bot, float dt) { //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: 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_01fa: Unknown result type (might be due to invalid IL or missing references) SinceEnemy += dt; if (Time.unscaledTime < _refreshAt) { return; } _refreshAt = Time.unscaledTime + 0.75f; try { MinutesHere = ((_levelStart > 0f) ? ((Time.time - _levelStart) / 60f) : 0f); HaveMoneyFacts = false; if ((Object)(object)RoundDirector.instance != (Object)null) { Haul = Reflect.Get(RoundDirector.instance, "currentHaul"); Goal = Reflect.Get(RoundDirector.instance, "haulGoal"); Shortfall = Mathf.Max(0, Goal - Haul); ExtractionsDone = Reflect.Get(RoundDirector.instance, "extractionPointsCompleted"); ExtractionsTotal = Reflect.Get(RoundDirector.instance, "extractionPoints"); HaveMoneyFacts = Goal > 0; } if ((Object)(object)bot != (Object)null) { PlayerHealth playerHealth = bot.playerHealth; if ((Object)(object)playerHealth != (Object)null) { Hp = Reflect.Get(playerHealth, "health"); HpMax = Reflect.Get(playerHealth, "maxHealth"); Hurt = HpMax > 0 && Hp * 2 < HpMax; } Deaths = BotSpawner.Deaths; } BuddyName = (((Object)(object)BotBuddy.Current != (Object)null) ? SemiFunc.PlayerGetName(BotBuddy.Current) : null); NearestName = null; NearestDist = 999f; PlayersUp = (PlayersDown = 0); if ((Object)(object)GameDirector.instance != (Object)null && (Object)(object)bot != (Object)null) { Vector3 position = ((Component)bot).transform.position; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } if (Reflect.Get(player, "isDisabled")) { PlayersDown++; continue; } PlayersUp++; float num = Vector3.Distance(position, ((Component)player).transform.position); if (num < NearestDist) { NearestDist = num; NearestName = SemiFunc.PlayerGetName(player); } } } CarryingName = null; CarryingValue = 0; PhysGrabObject carried = BotHands.Carried; if ((Object)(object)carried != (Object)null) { ValuableObject val = ((Component)carried).GetComponent() ?? ((Component)carried).GetComponentInParent(); if ((Object)(object)val != (Object)null) { CarryingValue = Mathf.RoundToInt(Reflect.Get(val, "dollarValueCurrent")); CarryingName = Prettify(((Object)((Component)val).gameObject).name); } } } catch { } } public static string Prettify(string raw) { if (string.IsNullOrEmpty(raw)) { return null; } string text = raw.Replace("(Clone)", "").Trim(); string[] array = new string[6] { "Valuable ", "Item ", "Museum ", "Manor ", "Arctic ", "Wizard " }; foreach (string text2 in array) { if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { text = text.Substring(text2.Length); } } return text.Trim().ToLowerInvariant(); } public static string Money(int v) { return "$" + v.ToString("N0"); } } public static class BotGrudge { private sealed class Score { public float Points; public float LastAt; public float SulkUntil; public int Hushes; public int Yields; } private static readonly Dictionary Ledger = new Dictionary(); private const float SnapAt = 4f; private const float DecayPerSecond = 0.06f; private const float SulkSeconds = 90f; private static readonly string[] Mild = new string[14] { "stupid", "dumb", "useless", "annoying", "bad bot", "rubbish", "pathetic", "clumsy", "slow", "junk", "scrap", "toaster", "tin can", "shut it" }; private static readonly string[] Harsh = new string[12] { "idiot", "moron", "worthless", "garbage", "trash", "hate you", "loser", "broken", "defective", "waste of space", "get lost", "piece of junk" }; private static readonly string[] Vile = new string[12] { "shit", "fuck", "fucking", "bastard", "arsehole", "asshole", "prick", "dickhead", "twat", "wanker", "cunt", "bitch" }; public static void Reset() { Ledger.Clear(); } public static bool IsSulkingAt(PlayerAvatar p) { if ((Object)(object)p == (Object)null) { return false; } if (!Ledger.TryGetValue(((Object)p).GetInstanceID(), out var value)) { return false; } return Time.unscaledTime < value.SulkUntil; } public static bool JudgeMessage(string lowerText, PlayerAvatar speaker, Vector3 botPos) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)speaker == (Object)null || string.IsNullOrEmpty(lowerText)) { return false; } float num = 0f; string[] vile = Vile; foreach (string needle in vile) { if (Contains(lowerText, needle)) { num = 3f; break; } } if (num <= 0f) { vile = Harsh; foreach (string needle2 in vile) { if (Contains(lowerText, needle2)) { num = 2f; break; } } } if (num <= 0f) { vile = Mild; foreach (string needle3 in vile) { if (Contains(lowerText, needle3)) { num = 1f; break; } } } if (num <= 0f) { return false; } return Offend(speaker, num, "insulted him", botPos); } public static bool NoteHush(PlayerAvatar speaker, Vector3 botPos) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)speaker == (Object)null) { return false; } Score score = Get(speaker); score.Hushes++; if (score.Hushes < 3) { return false; } return Offend(speaker, 1f, "keeps shutting him up", botPos); } public static bool NoteDismissal(PlayerAvatar speaker, Vector3 botPos) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)speaker == (Object)null) { return false; } bool flag = BotBuddy.Current == speaker; return Offend(speaker, flag ? 2f : 0.5f, flag ? "dismissed him to his face" : "told him to get lost", botPos); } public static bool NoteYield(PlayerAvatar taker, Vector3 botPos) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)taker == (Object)null) { return false; } Score score = Get(taker); score.Yields++; if (score.Yields < 4) { return false; } score.Yields = 0; return Offend(taker, 1f, "keeps taking things off him", botPos); } private static Score Get(PlayerAvatar p) { int instanceID = ((Object)p).GetInstanceID(); if (!Ledger.TryGetValue(instanceID, out var value)) { Dictionary ledger = Ledger; Score obj = new Score { LastAt = Time.unscaledTime }; value = obj; ledger[instanceID] = obj; } float num = Time.unscaledTime - value.LastAt; if (num > 0f) { value.Points = Mathf.Max(0f, value.Points - num * 0.06f); } value.LastAt = Time.unscaledTime; return value; } private static bool Offend(PlayerAvatar who, float weight, string why, Vector3 botPos) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) Score score = Get(who); if (Time.unscaledTime < score.SulkUntil) { return false; } score.Points += weight; string text = SemiFunc.PlayerGetName(who); Plugin.Log.LogInfo((object)("[Grudge] " + text + " " + why + " (+" + weight.ToString("F0") + ", now " + score.Points.ToString("F1") + "/" + 4f + ").")); if (score.Points < 4f) { return false; } score.Points = 0f; score.Hushes = 0; score.SulkUntil = Time.unscaledTime + 90f; Plugin.Log.LogWarning((object)("[Grudge] " + text + " pushed it too far — he's walking off.")); BotPersona.SpeakExact(BotPersona.HadEnough, botPos); BotHands.DropEverything(); BotScout.Stop(botPos, announce: false); if (BotBuddy.Current == who) { BotBuddy.Reroll(botPos); } return true; } private static bool Contains(string haystack, string needle) { int num = haystack.IndexOf(needle, StringComparison.Ordinal); if (num < 0) { return false; } bool num2 = num == 0 || !char.IsLetter(haystack[num - 1]); int num3 = num + needle.Length; bool flag = num3 >= haystack.Length || !char.IsLetter(haystack[num3]); return num2 && flag; } } public static class BotHands { public enum Job { Idle, Fetching, Hauling, CartFetch, CartDeliver } private static PhysGrabObject _target; private static PhysGrabObject _cart; private static PhysGrabCart _cartComp; private static Vector3 _cartDest; private static PlayerAvatar _cartRequester; private static Vector3 _snagLast; private static float _snagTimer; private static float _snagBackoff; private static int _snagStrikes; private static bool _snagPrimed; private static string _cartFor; private static PhysGrabObject _carried; private static PhysGrabber _holder; private static Vector3 _aimSmooth; private static bool _aimInit; private static float _scanTimer; private static float _giveUpTimer; private static Vector3 _dropSpot; private static bool _haveDropSpot; private static int _repositionCount; private static bool _announcedAssist; private static float _depositTimer; private static float _overBasketFor; private static bool _showboat; private static bool _showboatDecided; private const float FallbackGrabRange = 4f; private const float ApproachRange = 1.6f; private const float AimSpeed = 2.5f; private const float DropDistance = 1.9f; private const float MaxFetchRange = 14f; public const float LeashRange = 15f; private const float GiveUpAfter = 25f; private const float CarryingSpeedScale = 0.55f; private const float CarryDistance = 1.85f; private const float CarryHeight = 1.35f; private const float CarryHeightWalking = 1.85f; private const float DepositHeight = 1.35f; private const float CarryGain = 4f; private const float MaxCarrySpeed = 3f; private const float CarryDamping = 0.25f; private const float CarryForce = 12f; private const float DepositTimeout = 1.5f; private const float CartRadius = 2f; private const float HeavyMass = 1f; private const float LiftAssist = 0.75f; private static bool _dropRefined; private static float _stuckCarry; private static Vector3 _holdDir; private static bool _extracting; private static float _extractUntil; private static float _extractFromY; private const float HoldTurnRate = 110f; private const float MaxCarriedSpeed = 4.5f; private const float ExtractLift = 1.1f; private static BoxCollider _dropBox; private static PhysGrabCart[] _cartCache = Array.Empty(); private static float _cartCacheAt; private static readonly Dictionary _blacklist = new Dictionary(); private static readonly Dictionary _reachFails = new Dictionary(); private static float _opennessAt; private static float _openness = 1f; private static readonly List> _ignored = new List>(); private static float _valueAtPickup = -1f; public static Job Current { get; private set; } = Job.Idle; public static PhysGrabObject Carried => _carried; public static PlayerAvatar OfferTarget { get; set; } public static bool WorkPaused { get; private set; } public static bool IsCarrying(PhysGrabObject obj) { if ((Object)(object)obj != (Object)null) { return (Object)(object)_carried == (Object)(object)obj; } return false; } public static bool GrabDirectly(PhysGrabObject obj) { try { GameObject botObject = BotSpawner.BotObject; PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botObject == (Object)null || (Object)(object)botAvatar == (Object)null || (Object)(object)obj == (Object)null) { return false; } PhysGrabber physGrabber = botAvatar.physGrabber; if ((Object)(object)physGrabber == (Object)null) { return false; } return TryGrab(physGrabber, obj, botObject); } catch { return false; } } public static void HoldAt(PhysGrabber grabber, PhysGrabObject obj, GameObject bot, float dt, Vector3 aim) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) try { KeepHolding(grabber, obj, bot, dt, aim); } catch { } } public static bool DeliverTo(PhysGrabObject obj, Vector3 dest, string forName, float giveUp) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)obj == (Object)null) { return false; } DropEverything(); _cart = obj; _cartComp = null; _cartDest = dest; _cartFor = forName; Current = Job.CartFetch; _giveUpTimer = giveUp; return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] DeliverTo: " + ex.Message)); return false; } } public static bool FetchCart(PlayerAvatar requester, Vector3 botPos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) try { PhysGrabCart comp; PhysGrabObject val = NearestCart(botPos, out comp); if ((Object)(object)val == (Object)null) { BotPersona.Speak("nocart", BotPersona.NoCart, 1f, 15f, botPos); return false; } Vector3 val2 = (((Object)(object)requester != (Object)null) ? ((Component)requester).transform.position : botPos); if (Vector3.Distance(((Component)val).transform.position, val2) < 1.4f) { BotPersona.Speak("cartclose", BotPersona.CartAlreadyHere, 1f, 15f, botPos); return false; } DropEverything(); _cart = val; _cartComp = comp; _cartDest = val2; _cartRequester = requester; _cartFor = (((Object)(object)requester != (Object)null) ? SemiFunc.PlayerGetName(requester) : null); Current = Job.CartFetch; _giveUpTimer = 50f; BotPersona.Speak("cartgo", BotPersona.CartOnIt, 1f, 10f, botPos, _cartFor); Plugin.Log.LogInfo((object)("[Cart] Fetching the nearest cart for " + (_cartFor ?? "someone") + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Cart] " + ex.Message)); return false; } } private static PhysGrabObject NearestCart(Vector3 from, out PhysGrabCart comp) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject result = null; comp = null; float num = float.MaxValue; try { PhysGrabCart[] array = Carts(); foreach (PhysGrabCart val in array) { if ((Object)(object)val == (Object)null) { continue; } PhysGrabObject val2 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInParent() ?? ((Component)val).GetComponentInChildren(); if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(from, ((Component)val2).transform.position); if (num2 < num) { num = num2; result = val2; comp = val; } } } } catch { } return result; } private static bool Snagged(PhysGrabObject obj, Vector3 botPos, float dt) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)obj == (Object)null) { _snagPrimed = false; return false; } Vector3 position = ((Component)obj).transform.position; if (_snagBackoff > 0f) { _snagBackoff -= dt; return true; } if (!_snagPrimed) { _snagPrimed = true; _snagLast = position; _snagTimer = 0f; return false; } if (!BotMovement.HasGoal) { _snagLast = position; _snagTimer = 0f; return false; } float num = Mathf.Clamp(BotMovement.SpeedScale, 0.2f, 1f); float num2 = 0.12f * num; float num3 = 0.75f / num; if (Vector3.Distance(position, _snagLast) > num2) { _snagLast = position; _snagTimer = 0f; return false; } _snagTimer += dt; if (_snagTimer < num3) { return false; } _snagTimer = 0f; _snagStrikes++; _snagBackoff = 0.9f; _snagLast = position; Plugin.Log.LogInfo((object)("[Hands] It's caught on something (strike " + _snagStrikes + ") — easing off.")); return true; } private static void ClearSnag() { _snagPrimed = false; _snagTimer = 0f; _snagBackoff = 0f; _snagStrikes = 0; } private static void SeedGrabOrientation(PhysGrabber grabber, PhysGrabObject target) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)grabber == (Object)null || (Object)(object)target == (Object)null) { return; } Transform transform = ((Component)target).transform; Transform val = null; PlayerAvatar playerAvatar = grabber.playerAvatar; if ((Object)(object)playerAvatar != (Object)null && (Object)(object)playerAvatar.localCamera != (Object)null) { try { val = playerAvatar.localCamera.GetOverrideTransform(); } catch { } } if ((Object)(object)val == (Object)null) { val = ((Component)grabber).transform; } Quaternion val2 = Reflect.Get(target, "physRotation"); if (Mathf.Abs(val2.x) + Mathf.Abs(val2.y) + Mathf.Abs(val2.z) + Mathf.Abs(val2.w) < 0.0001f) { val2 = Quaternion.identity; } Quaternion val3 = Quaternion.Inverse(val2); Vector3 val4 = val3 * val.InverseTransformDirection(transform.forward); Vector3 val5 = val3 * val.InverseTransformDirection(transform.up); if (((Vector3)(ref val4)).sqrMagnitude < 1E-06f) { val4 = Vector3.forward; } if (((Vector3)(ref val5)).sqrMagnitude < 1E-06f) { val5 = Vector3.up; } grabber.cameraRelativeGrabbedForward = ((Vector3)(ref val4)).normalized; grabber.cameraRelativeGrabbedUp = ((Vector3)(ref val5)).normalized; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] SeedGrabOrientation: " + ex.Message)); } } private static Vector3 CartHandlePos(Vector3 fallback) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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) try { if ((Object)(object)_cartComp != (Object)null) { Vector3 val = BotCart.SteeringGrip(_cartComp, Vector3.zero); if (val != Vector3.zero) { return val; } } if ((Object)(object)_cartComp != (Object)null && (Object)(object)_cartComp.handlePoint != (Object)null) { return _cartComp.handlePoint.position; } } catch { } return fallback; } public static void PauseWork(bool paused) { WorkPaused = paused; if (paused) { DropEverything(); } } public static void DropEverything() { if ((Object)(object)_holder != (Object)null) { try { ReleaseAll(_holder); } catch { } } _target = null; Current = Job.Idle; BotMovement.SpeedScale = 1f; } public static void Reset() { if ((Object)(object)_carried != (Object)null && (Object)(object)_holder != (Object)null) { try { ReleaseAll(_holder); } catch { } } _holder = null; _aimInit = false; _dropRefined = false; _stuckCarry = 0f; IgnoreSelfCollision(null, null, ignore: false); BotMovement.SpeedScale = 1f; Current = Job.Idle; _target = null; _carried = null; _cart = null; _cartComp = null; _cartRequester = null; _cartFor = null; _dropBox = null; OfferTarget = null; ClearSnag(); _haveDropSpot = false; _announcedAssist = false; _repositionCount = 0; _depositTimer = 0f; _overBasketFor = 0f; _showboat = false; _showboatDecided = false; _giveUpTimer = 0f; } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_056d: Unknown result type (might be due to invalid IL or missing references) //IL_0574: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Unknown result type (might be due to invalid IL or missing references) //IL_0586: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: 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_017d: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_05a9: Unknown result type (might be due to invalid IL or missing references) //IL_05aa: Unknown result type (might be due to invalid IL or missing references) //IL_05b4: Unknown result type (might be due to invalid IL or missing references) //IL_05b9: Unknown result type (might be due to invalid IL or missing references) //IL_05be: Unknown result type (might be due to invalid IL or missing references) //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Unknown result type (might be due to invalid IL or missing references) //IL_05d2: Unknown result type (might be due to invalid IL or missing references) //IL_0917: Unknown result type (might be due to invalid IL or missing references) //IL_0922: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_07ee: Unknown result type (might be due to invalid IL or missing references) //IL_07f3: Unknown result type (might be due to invalid IL or missing references) //IL_07fb: Unknown result type (might be due to invalid IL or missing references) //IL_07fc: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_0803: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_034d: 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) //IL_05ed: Unknown result type (might be due to invalid IL or missing references) //IL_05ef: Unknown result type (might be due to invalid IL or missing references) //IL_0956: Unknown result type (might be due to invalid IL or missing references) //IL_08fa: Unknown result type (might be due to invalid IL or missing references) //IL_083d: Unknown result type (might be due to invalid IL or missing references) //IL_083e: Unknown result type (might be due to invalid IL or missing references) //IL_0845: Unknown result type (might be due to invalid IL or missing references) //IL_084a: Unknown result type (might be due to invalid IL or missing references) //IL_0825: Unknown result type (might be due to invalid IL or missing references) //IL_082a: Unknown result type (might be due to invalid IL or missing references) //IL_082f: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0691: Unknown result type (might be due to invalid IL or missing references) //IL_06c8: Unknown result type (might be due to invalid IL or missing references) //IL_06ec: Unknown result type (might be due to invalid IL or missing references) //IL_0653: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0470: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Unknown result type (might be due to invalid IL or missing references) //IL_04a3: Unknown result type (might be due to invalid IL or missing references) //IL_04a9: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_09b6: Unknown result type (might be due to invalid IL or missing references) //IL_09c1: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Unknown result type (might be due to invalid IL or missing references) //IL_09f5: Unknown result type (might be due to invalid IL or missing references) //IL_0a00: Unknown result type (might be due to invalid IL or missing references) //IL_0a20: Unknown result type (might be due to invalid IL or missing references) //IL_0a25: Unknown result type (might be due to invalid IL or missing references) //IL_0ada: Unknown result type (might be due to invalid IL or missing references) //IL_0b0d: Unknown result type (might be due to invalid IL or missing references) //IL_0b04: Unknown result type (might be due to invalid IL or missing references) //IL_0b12: Unknown result type (might be due to invalid IL or missing references) //IL_0b1c: Unknown result type (might be due to invalid IL or missing references) //IL_0b1d: Unknown result type (might be due to invalid IL or missing references) //IL_0bfc: Unknown result type (might be due to invalid IL or missing references) //IL_0cac: Unknown result type (might be due to invalid IL or missing references) //IL_0cad: Unknown result type (might be due to invalid IL or missing references) //IL_0c5d: Unknown result type (might be due to invalid IL or missing references) //IL_0c67: Unknown result type (might be due to invalid IL or missing references) //IL_0c6c: Unknown result type (might be due to invalid IL or missing references) //IL_0c3e: Unknown result type (might be due to invalid IL or missing references) //IL_0b5b: Unknown result type (might be due to invalid IL or missing references) //IL_0cf2: Unknown result type (might be due to invalid IL or missing references) //IL_0cf7: Unknown result type (might be due to invalid IL or missing references) //IL_0cfc: Unknown result type (might be due to invalid IL or missing references) //IL_0d01: Unknown result type (might be due to invalid IL or missing references) //IL_0d11: Unknown result type (might be due to invalid IL or missing references) //IL_0c85: Unknown result type (might be due to invalid IL or missing references) //IL_0c8a: Unknown result type (might be due to invalid IL or missing references) //IL_0c8c: Unknown result type (might be due to invalid IL or missing references) //IL_0d1f: Unknown result type (might be due to invalid IL or missing references) //IL_0ba1: Unknown result type (might be due to invalid IL or missing references) //IL_0ba6: Unknown result type (might be due to invalid IL or missing references) //IL_0bb0: Unknown result type (might be due to invalid IL or missing references) //IL_0bb5: Unknown result type (might be due to invalid IL or missing references) //IL_0bba: Unknown result type (might be due to invalid IL or missing references) //IL_0bbe: Unknown result type (might be due to invalid IL or missing references) //IL_0bc5: Unknown result type (might be due to invalid IL or missing references) //IL_0b8e: Unknown result type (might be due to invalid IL or missing references) //IL_0b93: Unknown result type (might be due to invalid IL or missing references) //IL_0b97: Unknown result type (might be due to invalid IL or missing references) //IL_0b9c: Unknown result type (might be due to invalid IL or missing references) //IL_0db8: Unknown result type (might be due to invalid IL or missing references) //IL_0dbd: Unknown result type (might be due to invalid IL or missing references) //IL_0dbe: Unknown result type (might be due to invalid IL or missing references) //IL_0dc3: Unknown result type (might be due to invalid IL or missing references) //IL_0bd8: Unknown result type (might be due to invalid IL or missing references) //IL_0bdf: Unknown result type (might be due to invalid IL or missing references) //IL_0be6: Unknown result type (might be due to invalid IL or missing references) //IL_0bed: Unknown result type (might be due to invalid IL or missing references) //IL_0bd4: Unknown result type (might be due to invalid IL or missing references) //IL_0de5: Unknown result type (might be due to invalid IL or missing references) //IL_0dec: Unknown result type (might be due to invalid IL or missing references) //IL_0d5d: Unknown result type (might be due to invalid IL or missing references) //IL_0d62: Unknown result type (might be due to invalid IL or missing references) //IL_0e4f: Unknown result type (might be due to invalid IL or missing references) //IL_0e44: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || (Object)(object)avatar == (Object)null) { return false; } PhysGrabber physGrabber = avatar.physGrabber; if ((Object)(object)physGrabber == (Object)null) { return false; } if (WorkPaused) { return false; } ExpireBlacklist(); Vector3 position = bot.transform.position; _giveUpTimer -= dt; if ((Object)(object)_carried == (Object)null && (Object)(object)_holder == (Object)null) { SetBeamVisible(physGrabber, visible: false); } Vector3 val2; Rigidbody rb3; bool flag2; int num6; bool flag4; int num12; switch (Current) { case Job.Idle: _scanTimer -= dt; if (_scanTimer > 0f) { return false; } _scanTimer = 0.5f; _target = FindLoot(position); if ((Object)(object)_target != (Object)null) { Current = Job.Fetching; _giveUpTimer = 25f; BotMovement.SetDestination(((Component)_target).transform.position); Plugin.Log.LogInfo((object)("[Hands] Fetching " + ((Object)_target).name)); BotChatter.SayFetching(); } return (Object)(object)_target != (Object)null; case Job.CartFetch: { if ((Object)(object)_cart == (Object)null || _giveUpTimer <= 0f) { Reset(); return false; } Vector3 position6 = ((Component)_cart).transform.position; Vector3 val13 = CartHandlePos(position6); Vector3 val14 = val13 - position6; val14.y = 0f; if (((Vector3)(ref val14)).sqrMagnitude < 0.04f) { val14 = val13 - position; } val14.y = 0f; Vector3 val15 = ((((Vector3)(ref val14)).sqrMagnitude > 0.0001f) ? (val13 + ((Vector3)(ref val14)).normalized * 0.9f) : val13); BotMovement.SetDestination(new Vector3(val15.x, position.y, val15.z)); BotMovement.FaceTowards(position6); float num11 = ((physGrabber.grabRange > 0.5f) ? physGrabber.grabRange : 4f); if (Vector3.Distance(position + Vector3.up * 1.35f, val13) <= num11 && TryGrab(physGrabber, _cart, bot, val13)) { _carried = _cart; Current = Job.CartDeliver; _giveUpTimer = 50f; KeepHolding(physGrabber, _carried, bot, dt, null); Plugin.Log.LogInfo((object)"[Cart] Got it — delivering."); } return true; } case Job.CartDeliver: { if ((Object)(object)_carried == (Object)null || _giveUpTimer <= 0f) { ReleaseAll(physGrabber); Reset(); return false; } if ((Object)(object)_cartRequester != (Object)null && (Object)(object)((Component)_cartRequester).gameObject != (Object)null) { _cartDest = ((Component)_cartRequester).transform.position; } BotMovement.SetDestination(_cartDest); if ((Object)(object)OfferTarget != (Object)null) { Vector3 position4 = ((Component)OfferTarget).transform.position; BotMovement.Stop(); BotMovement.FaceTowards(position4); Vector3 val10 = position4 - position; val10.y = 0f; Vector3 value = position + Vector3.up * 1.25f + ((((Vector3)(ref val10)).sqrMagnitude > 0.04f) ? (((Vector3)(ref val10)).normalized * 0.8f) : Vector3.zero); if (Reflect.Get(physGrabber, "initialPressTimer") <= 0.02f) { Reflect.Set(physGrabber, "initialPressTimer", 0.1f); } KeepHolding(physGrabber, _carried, bot, dt, value); return true; } BotMovement.FaceTowards(BotMovement.NextCorner(out var corner) ? corner : _cartDest); if (Snagged(_carried, position, dt)) { BotMovement.Stop(); if (_snagStrikes >= 4) { Plugin.Log.LogInfo((object)"[Cart] Thoroughly stuck — letting go and coming at it again."); ReleaseAll(physGrabber); ClearSnag(); Reset(); return false; } KeepHolding(physGrabber, _carried, bot, dt, null); return true; } Vector3 val11 = BotCart.Maintain(_cartComp, physGrabber, ((Component)_carried).transform.position); KeepHolding(physGrabber, _carried, bot, dt, val11 + Vector3.up * 0.15f); if ((Object)(object)physGrabber.physGrabPoint != (Object)null) { physGrabber.physGrabPoint.position = val11; } Vector3 position5 = ((Component)_carried).transform.position; float num9 = Vector2.Distance(new Vector2(position5.x, position5.z), new Vector2(_cartDest.x, _cartDest.z)); float num10 = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(_cartDest.x, _cartDest.z)); if (num9 <= 1.35f || num10 <= 2f) { ReleaseAll(physGrabber); BotPersona.Speak("cartdone", BotPersona.CartDelivered, 1f, 10f, position, _cartFor); Plugin.Log.LogInfo((object)("[Cart] Delivered right to " + (_cartFor ?? "requester") + ".")); Reset(); return false; } return true; } case Job.Fetching: { if ((Object)(object)_target == (Object)null || _giveUpTimer <= 0f) { Reset(); return false; } Vector3 position3 = ((Component)_target).transform.position; Vector3 val8 = default(Vector3); ((Vector3)(ref val8))..ctor(position3.x, position.y, position3.z); BotMovement.SetDestination(val8); float num7 = ((physGrabber.grabRange > 0.5f) ? physGrabber.grabRange : 4f); Vector3 val9 = position + Vector3.up * 1.35f; bool num8 = Vector3.Distance(val9, position3) <= num7; bool flag3 = Vector3.Distance(val8, position) <= 2.2f; if (num8 && (flag3 || Vector3.Distance(val9, position3) <= num7 * 0.75f)) { if (IsTooHeavyForSolo(_target) && !HumanAlsoGrabbing(_target, physGrabber)) { Plugin.Log.LogInfo((object)("[Hands] Item '" + ((Object)_target).name + "' is too heavy to move alone. Skipping until a human helps.")); Blacklist((Component)(object)_target, 45f, "too heavy for solo lift"); BotPersona.SpeakTooHeavySolo(position); Reset(); return false; } if (TryGrab(physGrabber, _target, bot)) { _carried = _target; _target = null; Current = Job.Hauling; _giveUpTimer = 50f; _haveDropSpot = FindDropSpot(position, out _dropSpot); KeepHolding(physGrabber, _carried, bot, dt, null); _extracting = true; _extractFromY = ((Component)_carried).transform.position.y; _extractUntil = Time.unscaledTime + 3.5f; BotOpinions.OnPickedUp(_carried, position); Plugin.Log.LogInfo((object)"[Hands] Grabbed it — hauling to extraction."); BotChatter.SayHauling(); } } return true; } case Job.Hauling: { if ((Object)(object)_carried == (Object)null || _giveUpTimer <= 0f) { ReleaseAll(physGrabber); Reset(); return false; } ValuableObject component = ((Component)_carried).GetComponent(); if ((Object)(object)component != (Object)null && _valueAtPickup > 0f) { float num = Reflect.Get(component, "dollarValueCurrent"); if (num < _valueAtPickup - 1f) { Plugin.Log.LogWarning((object)("[Hands] Item '" + ((Object)_carried).name + "' took damage (" + num.ToString("F0") + " vs " + _valueAtPickup.ToString("F0") + ") — setting down gently & repositioning.")); _valueAtPickup = num; PhysGrabObject carried = _carried; Vector3 position2 = ((Component)carried).transform.position; ReleaseAll(physGrabber); Vector3 val = position - position2; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { val = -bot.transform.forward; } ((Vector3)(ref val)).Normalize(); BotMovement.Stop(); BotMovement.SetDestination(position + val * 1.3f); BotChatter.SayOops(); _repositionCount++; if (_repositionCount >= 3) { Blacklist((Component)(object)carried, 35f, "damaged multiple times while hauling; stepping away"); _repositionCount = 0; Reset(); return false; } _target = carried; _carried = null; Current = Job.Fetching; _giveUpTimer = 25f; return true; } } if (HumanAlsoGrabbing(_carried, physGrabber)) { if (IsHeavy(_carried)) { AssistCarry(_carried, physGrabber, bot); return true; } Plugin.Log.LogInfo((object)"[Hands] A player took over — letting go."); BotChatter.SayYielding(); try { BotGrudge.NoteYield(Reflect.Get(_carried, "lastPlayerGrabbing"), bot.transform.position); } catch { } ReleaseAll(physGrabber); Reset(); return false; } if (Vector3.Distance(position, ((Component)_carried).transform.position) > 6f) { Plugin.Log.LogInfo((object)"[Hands] Lost the object — releasing cleanly."); ReleaseAll(physGrabber); Reset(); return false; } if (!_haveDropSpot) { _haveDropSpot = FindDropSpot(position, out _dropSpot); } if (!_haveDropSpot) { ReleaseAll(physGrabber); Reset(); return false; } Rigidbody rb = _carried.rb; float num2 = ((physGrabber.grabRange > 0.5f) ? physGrabber.grabRange : 4f); float num3 = Mathf.Min(num2 * 1.6f, 8f); if (Vector3.Distance(position, ((Component)_carried).transform.position) > num3) { PhysGrabObject carried2 = _carried; ReleaseAll(physGrabber); Blacklist((Component)(object)carried2, 40f, "it was snagged and he was towing it"); BotChatter.SayOops(); Reset(); return false; } bool num4 = Vector3.Distance(position, ((Component)_carried).transform.position) > num2 * 1.6f; int num5; if (!((Object)(object)rb == (Object)null)) { val2 = rb.velocity; num5 = ((((Vector3)(ref val2)).magnitude < 0.25f) ? 1 : 0); } else { num5 = 1; } bool flag = (byte)num5 != 0; if (num4 && flag) { _stuckCarry += dt; } else { _stuckCarry = 0f; } if (BotMovement.BlockedFor > 4f) { PhysGrabObject carried3 = _carried; ReleaseAll(physGrabber); Blacklist((Component)(object)carried3, 30f, "he had it wedged in the scenery"); BotChatter.SayOops(); Reset(); return false; } if (_stuckCarry > 3.5f || IsBanked((Component)(object)_carried)) { PhysGrabObject carried4 = _carried; string why = (IsBanked((Component)(object)carried4) ? "it's already banked in extraction" : "it won't budge"); ReleaseAll(physGrabber); Blacklist((Component)(object)carried4, 45f, why); BotChatter.SayOops(); Reset(); return false; } BotMovement.SetDestination(_dropSpot); Rigidbody rb2 = _carried.rb; Vector3 val3 = (((Object)(object)rb2 != (Object)null) ? rb2.position : ((Component)_carried).transform.position); Vector3? val4 = null; if (Vector3.Distance(position, _dropSpot) <= 1.9f) { BotMovement.Stop(); if (!_showboatDecided) { _showboatDecided = true; _showboat = Random.value < 0.18f; } if (!_showboat) { BotMovement.FaceTowards(_dropSpot); } if (!_dropRefined) { _dropRefined = true; RefineDropToCartCenter(ref _dropSpot); } if ((Object)(object)_dropBox != (Object)null) { Bounds bounds = ((Collider)_dropBox).bounds; _dropSpot = ((Bounds)(ref bounds)).center; } Vector3 val5 = _dropSpot + Vector3.up * 1.35f; val4 = (Vector3)((val3.y < val5.y - 0.25f) ? new Vector3(val3.x, val5.y, val3.z) : val5); } if (Snagged(_carried, position, dt)) { BotMovement.Stop(); if (_snagStrikes >= 4) { Plugin.Log.LogInfo((object)"[Hands] Can't get it past whatever that is — putting it down."); ReleaseAll(physGrabber); BotPersona.Speak("stuckitem", BotPersona.StuckItem, 1f, 25f, position); ClearSnag(); Reset(); return false; } PhysGrabObject carried5 = _carried; Vector3? val6 = val4; val2 = Vector3.up * 0.45f; KeepHolding(physGrabber, carried5, bot, dt, val6.HasValue ? new Vector3?(val6.GetValueOrDefault() + val2) : ((Vector3?)null)); return true; } KeepHolding(physGrabber, _carried, bot, dt, val4); if (Vector3.Distance(position, _dropSpot) <= 1.9f) { rb3 = _carried.rb; _depositTimer += dt; flag2 = (Object)(object)rb3 == (Object)null; if ((Object)(object)rb3 != (Object)null) { Vector3 val7 = rb3.position - _dropSpot; val7.y = 0f; if (OverTheBasket(rb3.position)) { num6 = ((rb3.position.y > _dropSpot.y + 0.15f) ? 1 : 0); if (num6 != 0) { _overBasketFor += dt; goto IL_0d59; } } else { num6 = 0; } _overBasketFor = 0f; goto IL_0d59; } goto IL_0d86; } goto IL_0e61; } default: { return false; } IL_0d86: flag4 = _depositTimer > 1.5f; if (flag4) { Plugin.Log.LogInfo((object)"[Hands] Deposit timed out — dropping it here."); } if (flag2 || flag4) { PhysGrabObject carried6 = _carried; Vector3 val12 = _dropSpot - position; val12.y = 0f; bool flag5 = ((Vector3)(ref val12)).sqrMagnitude > 0.25f && Vector3.Dot(bot.transform.forward, ((Vector3)(ref val12)).normalized) < 0.35f; ReleaseAll(physGrabber); Plugin.Log.LogInfo((object)("[Hands] Delivered." + (flag5 ? " Without looking." : ""))); if (flag5) { BotPersona.Speak("nolook", BotPersona.NoLookBrag, 0.9f, 20f, position); } else if (!BragIfIntact(carried6, position)) { BotChatter.SayDelivered(); } Reset(); } goto IL_0e61; IL_0e61: return true; IL_0d59: if (num6 != 0) { val2 = rb3.velocity; num12 = ((((Vector3)(ref val2)).magnitude < 2.5f || _overBasketFor > 0.15f) ? 1 : 0); } else { num12 = 0; } flag2 = (byte)num12 != 0; goto IL_0d86; } } private static PhysGrabObject FindLoot(Vector3 from) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) try { ValuableObject val = null; float num = float.MinValue; ValuableObject[] array = Object.FindObjectsOfType(); foreach (ValuableObject val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } PhysGrabObject component = ((Component)val2).GetComponent(); if ((Object)(object)component == (Object)null || (component.playerGrabbing != null && component.playerGrabbing.Count > 0) || (IsTooHeavyForSolo(component) && !HumanAlsoGrabbing(component, null))) { continue; } Vector3 position = ((Component)val2).transform.position; float num2 = Vector3.Distance(from, position); if (!(num2 > 14f) && (!NearestHuman(from, out var pos) || !(Vector3.Distance(pos, position) > 15f)) && !IsInCart(position) && !IsBanked((Component)(object)val2) && !_blacklist.ContainsKey(((Object)val2).GetInstanceID()) && HasClearLine(from, position, component)) { float num3 = Reflect.Get(val2, "dollarValueCurrent"); float num4 = 0f - num2 + Mathf.Clamp(num3 * 0.002f, 0f, 8f); if (num4 > num) { num = num4; val = val2; } } } return ((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null; } catch { return null; } } private static bool FindDropSpot(Vector3 from, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { PhysGrabCart val = null; float num = float.MaxValue; PhysGrabCart[] array = Carts(); foreach (PhysGrabCart val2 in array) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(from, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null) { spot = ((Component)val).transform.position; return true; } } catch { } try { ExtractionPoint val3 = Object.FindObjectOfType(); if ((Object)(object)val3 != (Object)null) { spot = ((Component)val3).transform.position; return true; } } catch { } return false; } private static bool IsInCart(Vector3 pos) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) try { PhysGrabCart[] array = Carts(); foreach (PhysGrabCart val in array) { if ((Object)(object)val == (Object)null) { continue; } Vector3 val2 = pos - ((Component)val).transform.position; if (Mathf.Abs(val2.y) < 2f) { Vector2 val3 = new Vector2(val2.x, val2.z); if (((Vector2)(ref val3)).magnitude < 2f) { return true; } } } } catch { } return false; } public static bool IsInCartPublic(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return IsInCart(p); } catch { return false; } } public static bool IsCart(PhysGrabObject obj) { if ((Object)(object)obj == (Object)null) { return false; } try { return (Object)(object)((Component)obj).GetComponent() != (Object)null || (Object)(object)((Component)obj).GetComponentInParent() != (Object)null || (Object)(object)((Component)obj).GetComponentInChildren() != (Object)null; } catch { return false; } } private static bool IsHeavy(PhysGrabObject obj) { try { return (Object)(object)obj != (Object)null && !IsCart(obj) && (Object)(object)obj.rb != (Object)null && obj.rb.mass >= 1f; } catch { return false; } } public static bool IsTooHeavyForSolo(PhysGrabObject obj) { if ((Object)(object)obj == (Object)null || (Object)(object)obj.rb == (Object)null) { return false; } if (IsCart(obj)) { return false; } return obj.rb.mass >= 3f; } private static void AssistCarry(PhysGrabObject obj, PhysGrabber grabber, GameObject bot) { try { BotMovement.SpeedScale = 0.55f; } catch { } } private static bool OverTheBasket(Vector3 worldPos) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_dropBox == (Object)null) { Vector3 val = worldPos - _dropSpot; val.y = 0f; return ((Vector3)(ref val)).magnitude < 0.55f; } Vector3 val2 = ((Component)_dropBox).transform.InverseTransformPoint(worldPos) - _dropBox.center; Vector3 val3 = _dropBox.size * 0.5f; float num = Mathf.Max(0.05f, val3.x - 0.12f); float num2 = Mathf.Max(0.05f, val3.z - 0.12f); return Mathf.Abs(val2.x) < num && Mathf.Abs(val2.z) < num2; } catch { return true; } } private static PhysGrabCart[] Carts() { if (Time.unscaledTime >= _cartCacheAt) { _cartCacheAt = Time.unscaledTime + 1f; try { _cartCache = Object.FindObjectsOfType(); } catch { _cartCache = Array.Empty(); } } return _cartCache; } private static void RefineDropToCartCenter(ref Vector3 spot) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) try { PhysGrabCart[] array = Carts(); foreach (PhysGrabCart val in array) { if ((Object)(object)val == (Object)null) { continue; } Vector3 position = ((Component)val).transform.position; if (Vector3.Distance(position, spot) > 3.5f) { continue; } object obj = Reflect.Get(val, "inCart"); GameObject val2 = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val2 == (Object)null) { Component val3 = (Component)((obj is Component) ? obj : null); if (val3 != null) { val2 = val3.gameObject; } } BoxCollider val4 = (_dropBox = (((Object)(object)val2 != (Object)null) ? val2.GetComponent() : null)); Vector3 val5; if (!((Object)(object)val4 != (Object)null)) { val5 = position; } else { Bounds bounds = ((Collider)val4).bounds; val5 = ((Bounds)(ref bounds)).center; } spot = val5; break; } } catch { } } private static float ItemHalfWidth(PhysGrabObject obj) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { Collider componentInChildren = ((Component)obj).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = componentInChildren.bounds; Vector3 extents = ((Bounds)(ref bounds)).extents; return Mathf.Clamp(Mathf.Max(extents.x, extents.z), 0.15f, 1.2f); } } catch { } return 0.35f; } private static Vector3 KeepOverPath(Vector3 botPos, Vector3 want, float halfWidth, float high) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = botPos + Vector3.up * high; NavMeshHit val2 = default(NavMeshHit); NavMeshHit val3 = default(NavMeshHit); for (int i = 0; i < 4; i++) { if (NavMesh.SamplePosition(new Vector3(want.x, botPos.y, want.z), ref val2, 0.9f, -1) && (!NavMesh.FindClosestEdge(((NavMeshHit)(ref val2)).position, ref val3, -1) || ((NavMeshHit)(ref val3)).distance >= halfWidth)) { return want; } want = Vector3.Lerp(want, val, 0.34f); } return want; } catch { return want; } } private static float HeadroomAbove(Vector3 feet) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) try { int blockers = BotSight.Blockers; if (blockers == 0) { return 3f; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(feet + Vector3.up * 0.5f, Vector3.up, ref val, 3.5f, blockers, (QueryTriggerInteraction)1)) { return 0.5f + ((RaycastHit)(ref val)).distance; } } catch { } return 3f; } private static float Openness(Vector3 at) { //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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < _opennessAt) { return _openness; } _opennessAt = Time.unscaledTime + 0.15f; try { int blockers = BotSight.Blockers; if (blockers == 0) { return _openness = 1f; } float num = 3f; Vector3[] array = (Vector3[])(object)new Vector3[4] { Vector3.forward, Vector3.back, Vector3.left, Vector3.right }; RaycastHit val2 = default(RaycastHit); foreach (Vector3 val in array) { if (Physics.Raycast(at, val, ref val2, 3f, blockers, (QueryTriggerInteraction)1) && ((RaycastHit)(ref val2)).distance < num) { num = ((RaycastHit)(ref val2)).distance; } } _openness = Mathf.Clamp01(Mathf.InverseLerp(0.45f, 1.6f, num)); } catch { _openness = 1f; } return _openness; } private static bool IsBanked(Component thing) { try { if ((Object)(object)thing == (Object)null) { return false; } PhysGrabObject val = thing.GetComponent() ?? thing.GetComponentInParent(); object obj = (((Object)(object)val != (Object)null) ? Reflect.Get(val, "roomVolumeCheck") : null); return obj != null && Reflect.Get(obj, "inExtractionPoint"); } catch { return false; } } public static void Avoid(PhysGrabObject obj, float seconds) { if (!((Object)(object)obj == (Object)null)) { ValuableObject component = ((Component)obj).GetComponent(); Blacklist((Component)(((Object)(object)component != (Object)null) ? ((object)component) : ((object)obj)), seconds, "told to leave it alone"); } } private static void Blacklist(Component thing, float seconds, string why) { try { if (!((Object)(object)thing == (Object)null)) { _blacklist[((Object)thing).GetInstanceID()] = Time.unscaledTime + seconds; Plugin.Log.LogInfo((object)("[Hands] Giving up on '" + ((Object)thing).name + "' for " + seconds.ToString("F0") + "s — " + why + ".")); } } catch { } } private static void ExpireBlacklist() { if (_blacklist.Count == 0) { return; } List list = null; foreach (KeyValuePair item in _blacklist) { if (Time.unscaledTime > item.Value) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (int item2 in list) { _blacklist.Remove(item2); } } private static bool HumanAlsoGrabbing(PhysGrabObject obj, PhysGrabber self) { try { if ((Object)(object)obj == (Object)null || obj.playerGrabbing == null) { return false; } foreach (PhysGrabber item in obj.playerGrabbing) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)self)) { PlayerAvatar playerAvatar = item.playerAvatar; if ((Object)(object)playerAvatar != (Object)null && !BotSpawner.IsBot(playerAvatar)) { return true; } } } } catch { } return false; } private static bool CanReachToGrab(GameObject bot, PhysGrabObject target) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: 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) try { if ((Object)(object)bot == (Object)null || (Object)(object)target == (Object)null) { return true; } Transform transform = ((Component)target).transform; Vector3 val = ((target.midPoint != Vector3.zero) ? target.midPoint : transform.position); Vector3 val2 = bot.transform.position + Vector3.up * 1.1f; if (Vector3.Distance(val2, val) < 2.2f) { return true; } Vector3 eye = bot.transform.position + Vector3.up * 0.55f; return RayReaches(val2, val, target) || RayReaches(val2, transform.position, target) || RayReaches(val2, val + Vector3.up * 0.35f, target) || RayReaches(eye, val, target) || RayReaches(eye, transform.position, target); } catch { return true; } } private static bool RayReaches(Vector3 eye, Vector3 to, PhysGrabObject target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Vector3 val = to - eye; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.4f) { return true; } int blockers = BotSight.Blockers; if (blockers == 0) { return true; } RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(eye, ((Vector3)(ref val)).normalized, ref val2, magnitude - 0.25f, blockers, (QueryTriggerInteraction)1)) { return true; } if ((Object)(object)((RaycastHit)(ref val2)).collider != (Object)null) { return (Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent() == (Object)(object)target; } return false; } private static void RememberValue(PhysGrabObject target) { _valueAtPickup = -1f; try { ValuableObject component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null) { _valueAtPickup = Reflect.Get(component, "dollarValueCurrent"); } } catch { } } private static bool BragIfIntact(PhysGrabObject delivered, Vector3 pos) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)delivered == (Object)null || _valueAtPickup <= 0f) { return false; } ValuableObject component = ((Component)delivered).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } float valueAtPickup = _valueAtPickup; _valueAtPickup = -1f; if (Reflect.Get(component, "dollarValueCurrent") < valueAtPickup * 0.995f) { return false; } return BotPersona.Speak("intact", BotPersona.DeliveredIntact, 0.55f, 30f, pos); } catch { return false; } } private static void IgnoreSelfCollision(GameObject bot, PhysGrabObject target, bool ignore) { try { if (!ignore) { foreach (KeyValuePair item in _ignored) { if (!((Object)(object)item.Key == (Object)null) && !((Object)(object)item.Value == (Object)null)) { try { Physics.IgnoreCollision(item.Key, item.Value, false); } catch { } } } _ignored.Clear(); } else { if ((Object)(object)bot == (Object)null || (Object)(object)target == (Object)null) { return; } Collider[] componentsInChildren = ((Component)bot.transform.root).GetComponentsInChildren(true); Collider[] componentsInChildren2 = ((Component)((Component)target).transform.root).GetComponentsInChildren(true); if (componentsInChildren.Length == 0 || componentsInChildren2.Length == 0) { return; } Collider[] array = componentsInChildren; foreach (Collider val in array) { if ((Object)(object)val == (Object)null || val.isTrigger) { continue; } Collider[] array2 = componentsInChildren2; foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !val2.isTrigger) { try { Physics.IgnoreCollision(val, val2, true); _ignored.Add(new KeyValuePair(val, val2)); } catch { } } } } if (_ignored.Count > 0) { Plugin.Log.LogInfo((object)("[Hands] Carrying '" + ((Object)((Component)target).gameObject).name + "' without letting it hit him (" + _ignored.Count + " collider pairs). His carry point sits at his chest rather than out past a camera, so every step was grinding the thing against his own body.")); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] IgnoreSelfCollision: " + ex.Message)); } } private static bool PathIsOpen(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = b - a; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.3f) { return true; } int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Raycast(a, ((Vector3)(ref val)).normalized, magnitude - 0.2f, blockers, (QueryTriggerInteraction)1); } catch { return true; } } private static bool HasClearLine(Vector3 from, Vector3 itemPos, PhysGrabObject item) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = from + Vector3.up * 1.1f; Vector3 val2 = itemPos - val; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.5f) { return true; } int blockers = BotSight.Blockers; RaycastHit val3 = default(RaycastHit); if (!Physics.Raycast(val, ((Vector3)(ref val2)).normalized, ref val3, magnitude - 0.3f, blockers, (QueryTriggerInteraction)1)) { return true; } return (Object)(object)((RaycastHit)(ref val3)).collider != (Object)null && (Object)(object)((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent() == (Object)(object)item; } catch { return true; } } public static bool NearestHuman(Vector3 from, out Vector3 pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) pos = Vector3.zero; float num = float.MaxValue; bool result = false; try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "deadSet")) { Vector3 position = ((Component)player).transform.position; float num2 = Vector3.Distance(from, position); if (num2 < num) { num = num2; pos = position; result = true; } } } } catch { } return result; } private static bool TryGrab(PhysGrabber grabber, PhysGrabObject target, GameObject bot) { return TryGrab(grabber, target, bot, null); } private static bool TryGrab(PhysGrabber grabber, PhysGrabObject target, GameObject bot, Vector3? attachAt) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) try { if (!CanReachToGrab(bot, target)) { int instanceID = ((Object)target).GetInstanceID(); _reachFails.TryGetValue(instanceID, out var value); value++; _reachFails[instanceID] = value; if (value >= 3) { _reachFails.Remove(instanceID); ValuableObject component = ((Component)target).GetComponent(); Blacklist((Component)(((Object)(object)component != (Object)null) ? ((object)component) : ((object)target)), 15f, "three tries from three angles and still something solid in the way"); } if (_target == target) { _target = null; Current = Job.Idle; } return false; } try { target.GrabStarted(grabber); Reflect.Set(grabber, "grabbedPhysGrabObject", target); grabber.grabbed = true; Transform transform = ((Component)target).transform; Vector3 val2; try { Transform val = Reflect.Get(target, "forceGrabPoint"); val2 = (Vector3)(((??)attachAt) ?? (((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) ? val.position : target.midPoint)); } catch { val2 = transform.position; } Reflect.Set(grabber, "localGrabPosition", transform.InverseTransformPoint(val2)); if ((Object)(object)grabber.physGrabPointPuller != (Object)null) { ((Component)grabber.physGrabPointPuller).gameObject.SetActive(true); } if (target.playerGrabbing != null && target.playerGrabbing.Contains(grabber)) { _holder = grabber; SeedGrabOrientation(grabber, target); IgnoreSelfCollision(bot, target, ignore: true); RememberValue(target); _reachFails.Remove(((Object)target).GetInstanceID()); Plugin.Log.LogInfo((object)"[Hands] Grab registered via GrabStarted."); return true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] GrabStarted: " + ex.Message)); } Vector3 val3 = bot.transform.position + Vector3.up * 1.1f; Vector3 val4; try { val4 = target.midPoint; } catch { val4 = ((Component)target).transform.position; } Vector3 val5 = val4 - val3; if (((Vector3)(ref val5)).sqrMagnitude < 0.0001f) { return false; } RaycastHit[] array = Physics.RaycastAll(val3, ((Vector3)(ref val5)).normalized, ((Vector3)(ref val5)).magnitude + 1f, LayerMask.GetMask(new string[1] { "PhysGrabObject" }), (QueryTriggerInteraction)1); bool flag = false; RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val6 = array2[i]; if ((Object)(object)((RaycastHit)(ref val6)).collider == (Object)null || (Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent() != (Object)(object)target || (Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponent() == (Object)null) { continue; } MethodInfoInvoke2(grabber, "StartGrabbingPhysObject", val6, target); try { PhotonView component2 = ((Component)grabber).GetComponent(); PhysGrabObjectCollider component3 = ((Component)((RaycastHit)(ref val6)).collider).GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null) { target.GrabLink(component2.ViewID, component3.colliderID, ((RaycastHit)(ref val6)).point, bot.transform.forward, Vector3.up); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Hands] GrabLink: " + ex2.Message)); } flag = true; break; } if (!flag) { Plugin.Log.LogInfo((object)("[Hands] No clear line to the object (" + array.Length + " hits) — repositioning.")); return false; } int num; if (target.playerGrabbing != null) { num = (target.playerGrabbing.Contains(grabber) ? 1 : 0); if (num != 0) { goto IL_038a; } } else { num = 0; } Plugin.Log.LogInfo((object)"[Hands] Grab did not take; will retry."); goto IL_038a; IL_038a: return (byte)num != 0; } catch (Exception ex3) { Plugin.Log.LogWarning((object)("[Hands] Grab failed: " + ex3.Message)); return false; } } private static void MethodInfoInvoke2(object target, string method, object a, object b) { try { MethodInfo methodInfo = AccessTools.Method(target.GetType(), method, (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(target, new object[2] { a, b }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] " + method + ": " + ex.InnerException?.Message)); } } private static void KeepHolding(PhysGrabber grabber, PhysGrabObject obj, GameObject bot, float dt, Vector3? aimOverride) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: 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_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_0442: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Unknown result type (might be due to invalid IL or missing references) //IL_046e: Unknown result type (might be due to invalid IL or missing references) //IL_0473: Unknown result type (might be due to invalid IL or missing references) //IL_0425: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0488: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Unknown result type (might be due to invalid IL or missing references) //IL_04bd: Unknown result type (might be due to invalid IL or missing references) //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_058d: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_0510: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_0540: Unknown result type (might be due to invalid IL or missing references) //IL_0545: Unknown result type (might be due to invalid IL or missing references) //IL_052a: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Unknown result type (might be due to invalid IL or missing references) //IL_055e: Unknown result type (might be due to invalid IL or missing references) //IL_0562: Unknown result type (might be due to invalid IL or missing references) //IL_056c: Unknown result type (might be due to invalid IL or missing references) try { Transform transform = ((Component)obj).transform; Vector3 val = Reflect.Get(grabber, "localGrabPosition"); Vector3 val2; if (val != Vector3.zero) { val2 = transform.TransformPoint(val); } else { try { val2 = obj.midPoint; } catch { val2 = transform.position; } } float num = Openness(val2); float num2 = Mathf.Lerp(0.7f, 1.85f, num); float num3 = ((BotMovement.CurrentSpeed > 0.4f) ? 1.85f : 1.35f); if (IsHeavy(obj)) { num3 += 0.25f; } float num4 = Mathf.Lerp(num3 * 0.78f, num3, num); num4 = Mathf.Min(num4, HeadroomAbove(bot.transform.position) - 0.35f); num4 = Mathf.Max(num4, 0.7f); float num5 = (IsHeavy(obj) ? 0.38f : 0.55f); BotMovement.SpeedScale = Mathf.Lerp(num5 * 0.5f, num5, num); if (Plugin.CfgCarryFragility != null) { float num6 = Mathf.Clamp(Plugin.CfgCarryFragility.Value, 0.05f, 1f); if (num6 < 0.999f) { try { obj.OverrideFragility(num6); } catch { } } } Vector3 forward = bot.transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); Vector3 val3 = forward; if (BotMovement.PointAlongPath(num2, out var point)) { Vector3 val4 = point - bot.transform.position; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude > 0.04f) { val3 = ((Vector3)(ref val4)).normalized; } } if (((Vector3)(ref _holdDir)).sqrMagnitude < 0.0001f) { _holdDir = val3; } Vector3 val5 = Vector3.RotateTowards(_holdDir, val3, 1.9198622f * Mathf.Max(dt, 0.0001f), 0f); _holdDir = ((Vector3)(ref val5)).normalized; Vector3 val6 = (Vector3)(((??)aimOverride) ?? (bot.transform.position + _holdDir * num2 + Vector3.up * num4)); if (!aimOverride.HasValue) { val6 = KeepOverPath(bot.transform.position, val6, ItemHalfWidth(obj), num4); } if (_extracting && !aimOverride.HasValue) { if (val2.y - _extractFromY >= 1.1f || PathIsOpen(val2, val6) || Time.unscaledTime > _extractUntil) { _extracting = false; } else { float num7 = HeadroomAbove(bot.transform.position) - 0.3f; float num8 = Mathf.Min(_extractFromY + 1.1f, num7); ((Vector3)(ref val6))..ctor(val2.x, Mathf.Max(num8, val2.y + 0.15f), val2.z); } } Vector3 val7 = bot.transform.position + Vector3.up * 1.35f; float num9 = ((grabber.grabRange > 0.5f) ? grabber.grabRange : 4f); Vector3 val8 = val6 - val7; if (((Vector3)(ref val8)).magnitude > num9) { val6 = val7 + ((Vector3)(ref val8)).normalized * num9; } if (!_aimInit) { _aimSmooth = val2; _aimInit = true; } _aimSmooth = Vector3.MoveTowards(_aimSmooth, val6, 2.5f * Mathf.Max(dt, 0.0001f)); Vector3 val9 = _aimSmooth; Vector3 val10 = (((Object)(object)grabber.physGrabBeamComponent != (Object)null && (Object)(object)grabber.physGrabBeamComponent.PhysGrabPointOrigin != (Object)null) ? grabber.physGrabBeamComponent.PhysGrabPointOrigin.position : (bot.transform.position + Vector3.up * 1.35f)); val5 = val9 - val10; if (((Vector3)(ref val5)).sqrMagnitude < 0.09f) { Vector3 forward2 = bot.transform.forward; if (((Vector3)(ref forward2)).sqrMagnitude < 0.0001f) { forward2 = Vector3.forward; } val9 = val10 + ((Vector3)(ref forward2)).normalized * 0.5f; } val5 = val2 - val9; if (((Vector3)(ref val5)).sqrMagnitude < 0.0025f) { val2 = val9 - bot.transform.forward * 0.1f; } if ((Object)(object)grabber.physGrabPoint != (Object)null) { grabber.physGrabPoint.position = val2; } if ((Object)(object)grabber.physGrabPointPuller != (Object)null) { grabber.physGrabPointPuller.position = val9; } if ((Object)(object)grabber.physGrabPointPlane != (Object)null) { grabber.physGrabPointPlane.position = val9; } SetBeamVisible(grabber, visible: true); if (!grabber.grabbed) { grabber.grabbed = true; } Rigidbody rb = obj.rb; if ((Object)(object)rb != (Object)null && !rb.isKinematic) { float num10 = (IsHeavy(obj) ? 2.2f : 4.5f); val5 = rb.velocity; float magnitude = ((Vector3)(ref val5)).magnitude; if (magnitude > num10) { rb.velocity *= num10 / magnitude; } val5 = rb.angularVelocity; if (((Vector3)(ref val5)).magnitude > 4.5f) { val5 = rb.angularVelocity; rb.angularVelocity = ((Vector3)(ref val5)).normalized * 4.5f; } } Reflect.Set(grabber, "physGrabPointPosition", val2); Reflect.Set(grabber, "physGrabPointPullerPosition", val9); Reflect.Set(grabber, "pullerDistance", Vector3.Distance(val9, val2)); } catch { } } private static void SetBeamVisible(PhysGrabber grabber, bool visible) { try { if ((Object)(object)grabber == (Object)null || Reflect.Get(grabber, "physGrabBeamActive") == visible) { return; } PhotonView val = grabber.photonView ?? ((Component)grabber).GetComponent(); if ((Object)(object)val != (Object)null && PhotonNetwork.InRoom) { val.RPC(visible ? "PhysGrabBeamActivateRPC" : "PhysGrabBeamDeactivateRPC", (RpcTarget)0, Array.Empty()); Reflect.Set(grabber, "physGrabBeamActive", visible); return; } Reflect.Set(grabber, "physGrabBeamActive", visible); GameObject physGrabBeam = grabber.physGrabBeam; if ((Object)(object)physGrabBeam != (Object)null) { if (physGrabBeam.activeSelf != visible) { physGrabBeam.SetActive(visible); } LineRenderer[] componentsInChildren = physGrabBeam.GetComponentsInChildren(true); foreach (LineRenderer val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { ((Renderer)val2).enabled = visible; } } } PhysGrabBeam physGrabBeamComponent = grabber.physGrabBeamComponent; if ((Object)(object)physGrabBeamComponent != (Object)null) { LineRenderer component = ((Component)physGrabBeamComponent).GetComponent(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = visible; } } } catch { } } private static void ReleaseAll(PhysGrabber grabber) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject carried = _carried; _carried = null; try { grabber.ReleaseObject(-1, 0.1f); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hands] ReleaseObject: " + ex.Message)); } try { grabber.OverrideGrabRelease(-1, 0.5f); } catch { } try { if ((Object)(object)carried != (Object)null && carried.playerGrabbing != null && carried.playerGrabbing.Contains(grabber)) { carried.GrabEnded(grabber); Plugin.Log.LogInfo((object)"[Hands] Force-unlinked a stale grab."); } } catch { } _holder = null; _openness = 1f; _holdDir = Vector3.zero; _extracting = false; BotMovement.SpeedScale = 1f; IgnoreSelfCollision(null, null, ignore: false); SetBeamVisible(grabber, visible: false); try { grabber.grabbed = false; } catch { } try { Reflect.Set(grabber, "grabbedPhysGrabObject", null); } catch { } try { Reflect.Set(grabber, "localGrabPosition", Vector3.zero); } catch { } } private static void MethodInfoInvoke(object target, string method, object arg) { try { MethodInfo methodInfo = AccessTools.Method(target.GetType(), method, (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(target, new object[1] { arg }); } } catch { } } } public static class BotHearing { public struct Result { public bool Heard; public float Distance; public float Range; public string Why; } private const float DefaultVoiceRange = 20f; private const float MuffleMultiplier = 0.8f; private const float AutoMuffleDistance = 20f; public static Vector3 GetSpeakerMouthPosition(PlayerAvatar speaker, out bool valid) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) valid = true; if ((Object)(object)speaker == (Object)null) { valid = false; return Vector3.zero; } try { if (Reflect.Get(speaker, "isDisabled")) { object obj = Reflect.Get(speaker, "playerDeathHead"); if (obj != null) { Component val = (Component)((obj is Component) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { return val.transform.position + Vector3.up * 0.3f; } } valid = false; return Vector3.zero; } return ((Component)speaker).transform.position + Vector3.up * 1.2f; } catch { return ((Component)speaker).transform.position + Vector3.up * 1.2f; } } public static Result CanHear(PlayerAvatar speaker, Vector3 botPos, float peak) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) Result result = new Result { Heard = false, Why = "no speaker" }; if ((Object)(object)speaker == (Object)null) { return result; } try { bool valid; Vector3 speakerMouthPosition = GetSpeakerMouthPosition(speaker, out valid); if (!valid) { result.Heard = false; result.Why = "dead speaker has no active death head in world"; return result; } Vector3 val = botPos + Vector3.up * 1.2f; result.Distance = Vector3.Distance(speakerMouthPosition, val); float num = 20f; AudioSource val2 = VoiceSource(speaker); if ((Object)(object)val2 != (Object)null && val2.maxDistance > 0.5f) { num = val2.maxDistance; } bool flag = result.Distance >= 20f; string text = (flag ? "too far to carry clearly" : null); if (!flag && DifferentRoom(speakerMouthPosition, val)) { flag = true; text = "different room"; } if (!flag && WallBetween(speakerMouthPosition, val)) { flag = true; text = "a wall in between"; } if (flag) { num *= 0.8f; } float num2 = Mathf.Clamp(Mathf.InverseLerp(0.02f, 0.3f, peak), 0f, 1f); num = (result.Range = num * Mathf.Lerp(0.6f, 1.25f, num2)); result.Heard = result.Distance <= num; result.Why = ((!result.Heard) ? (text ?? "out of range") : (flag ? ("heard, muffled (" + text + ")") : "heard clearly")); return result; } catch (Exception ex) { result.Heard = true; result.Why = "check failed (" + ex.Message + "), allowing"; return result; } } private static AudioSource VoiceSource(PlayerAvatar p) { try { object obj = Reflect.Get(p, "voiceChat"); return (obj != null) ? Reflect.Get(obj, "audioSource") : null; } catch { return null; } } private static bool DifferentRoom(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Component val = TriggerAt(a); Component val2 = TriggerAt(b); if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null) { return false; } return val != val2; } private static Component TriggerAt(Vector3 point) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) try { int mask = LayerMask.GetMask(new string[1] { "LowPassTrigger" }); if (mask == 0) { return null; } Collider[] array = Physics.OverlapSphere(point, 0.1f, mask, (QueryTriggerInteraction)2); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && ((Component)((Component)val).transform).CompareTag("LowPassTrigger")) { Component val2 = ((Component)val).GetComponent(typeof(LowPassTrigger)) ?? ((Component)val).GetComponentInParent(typeof(LowPassTrigger)); if ((Object)(object)val2 != (Object)null) { return val2; } } } } catch { } return null; } private static bool WallBetween(Vector3 from, Vector3 to) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.01f) { return false; } int mask = LayerMask.GetMask(new string[3] { "Default", "PhysGrabObject", "PhysGrabObjectHinge" }); if (mask == 0) { return false; } RaycastHit[] array = Physics.RaycastAll(from, val, magnitude, mask, (QueryTriggerInteraction)2); for (int i = 0; i < array.Length; i++) { RaycastHit val2 = array[i]; if ((Object)(object)((RaycastHit)(ref val2)).collider != (Object)null && ((Component)((Component)((RaycastHit)(ref val2)).collider).transform).CompareTag("Wall")) { return true; } } } catch { } return false; } } public static class BotHush { private static float _left; public const float DefaultSeconds = 30f; public static bool Active => _left > 0f; public static float SecondsLeft => Mathf.Max(0f, _left); public static void Reset() { _left = 0f; } public static void Start(float seconds) { _left = Mathf.Max(_left, seconds); Plugin.Log.LogInfo((object)("[Hush] Quiet and crouched for " + _left.ToString("F0") + "s.")); } public static bool Release() { if (_left <= 0f) { return false; } _left = 0f; Plugin.Log.LogInfo((object)"[Hush] Released — he may speak again."); return true; } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { if (_left <= 0f) { return false; } _left -= dt; if (_left <= 0f) { BotMovement.ForceCrouch = false; Plugin.Log.LogInfo((object)"[Hush] Thirty seconds up."); return false; } BotMovement.ForceCrouch = true; BotMovement.Stop(); BotMovement.SpeedScale = 1f; if ((Object)(object)avatar != (Object)null) { Reflect.Set(avatar, "isMoving", false); Reflect.Set(avatar, "isSprinting", false); } BotMovement.Tick(bot, avatar, dt); return true; } } public static class BotIdle { private const float BoredAfter = 8f; private const float DriftRadius = 5.5f; private static float _still; private static float _nextMoveAt; private static float _nextQuipAt; private static Vector3 _anchor; private static bool _haveAnchor; private static bool _wandering; private static Vector3 _lastBuddyPos; private static bool _haveBuddyPos; private static float _buddyStillFor; public static bool HoldingPosition { get; private set; } public static void CommandHeard(string intentName) { if (intentName == null) { return; } switch (intentName) { default: if (!(intentName == "social.quiet")) { if (!intentName.StartsWith("social.", StringComparison.Ordinal)) { HoldingPosition = false; } break; } goto case "move.wait"; case "move.wait": case "control.halt": case "move.hide": HoldingPosition = true; Plugin.Log.LogInfo((object)("[Idle] Parked by '" + intentName + "' — no fidgeting until told otherwise.")); break; } Settle(); } public static void Hold(bool on) { HoldingPosition = on; if (on) { Settle(); } } public static void Reset() { Settle(); _haveBuddyPos = false; _buddyStillFor = 0f; HoldingPosition = false; } public static void Poke() { Settle(); } public static bool Tick(GameObject bot, float dt, Vector3 buddyPos) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)bot == (Object)null) { return false; } if (_haveBuddyPos && Vector3.Distance(buddyPos, _lastBuddyPos) < 0.35f) { _buddyStillFor += dt; } else { _buddyStillFor = 0f; } _lastBuddyPos = buddyPos; _haveBuddyPos = true; if (HoldingPosition || _buddyStillFor < 1f || BotHush.Active || BotDanger.Level != BotDanger.Threat.Clear || BotHands.Current != BotHands.Job.Idle || BotScout.Active || BotDance.Dancing || BotAssist.Helping) { Settle(); return false; } _still += dt; if (_still < 8f) { return false; } Vector3 position = bot.transform.position; _anchor = buddyPos; _haveAnchor = true; if (!_wandering || !BotMovement.HasGoal || Vector3.Distance(position, BotMovement.Goal) < 1.1f) { if (Time.unscaledTime < _nextMoveAt) { return _wandering; } if (!PickNearby(_anchor, out var spot)) { _nextMoveAt = Time.unscaledTime + 4f; return false; } BotMovement.SetDestination(spot); _wandering = true; _nextMoveAt = Time.unscaledTime + Random.Range(5f, 11f); if (Time.unscaledTime >= _nextQuipAt && Random.value < 0.25f) { _nextQuipAt = Time.unscaledTime + 75f; BotPersona.SpeakExact(BotPersona.Bored, position); } } if (_wandering) { BotMovement.SpeedScale = 0.45f; } return _wandering; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Idle] " + ex.Message)); return false; } } public static bool StepAside(GameObject bot, Vector3 buddyPos, Vector3 buddyVel) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)bot == (Object)null || HoldingPosition) { return false; } Vector3 position = bot.transform.position; Vector3 val = position - buddyPos; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude > 2f || magnitude < 0.05f) { return false; } Vector3 val2 = buddyVel; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.35f) { return false; } if (Vector3.Dot(((Vector3)(ref val2)).normalized, ((Vector3)(ref val)).normalized) > -0.35f) { return false; } Vector3 val3 = Vector3.Cross(Vector3.up, ((Vector3)(ref val2)).normalized); if (Vector3.Dot(val3, val) < 0f) { val3 = -val3; } BotMovement.SetDestination(position + val3 * 1.6f); BotMovement.SpeedScale = 1f; _still = 0f; return true; } catch { return false; } } private static void Settle() { _still = 0f; if (_wandering) { _wandering = false; _haveAnchor = false; BotMovement.SpeedScale = 1f; } } private static bool PickNearby(Vector3 around, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) spot = around; NavMeshHit val2 = default(NavMeshHit); for (int i = 0; i < 12; i++) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(2.2f, 5.5f); if (NavMesh.SamplePosition(around + new Vector3(val.x, 0f, val.y), ref val2, 2f, -1) && !(Vector3.Distance(((NavMeshHit)(ref val2)).position, around) > 6f)) { spot = ((NavMeshHit)(ref val2)).position; return true; } } return false; } } [HarmonyPatch] public static class BotInputGate { public static bool Armed { get { if (BotBuddyHud.Visible) { if (!Input.GetKey((KeyCode)308)) { return Input.GetKey((KeyCode)307); } return true; } return false; } } private static bool IsBorrowed(InputKey key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected I4, but got Unknown switch (key - 17) { case 0: case 2: case 3: case 4: case 9: case 10: case 11: case 12: case 13: case 14: return true; default: return false; } } [HarmonyPatch(typeof(InputManager), "KeyDown")] [HarmonyPrefix] public static bool KeyDownPrefix(InputKey key, ref bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!Armed || !IsBorrowed(key)) { return true; } __result = false; return false; } [HarmonyPatch(typeof(InputManager), "KeyHold")] [HarmonyPrefix] public static bool KeyHoldPrefix(InputKey key, ref bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!Armed || !IsBorrowed(key)) { return true; } __result = false; return false; } [HarmonyPatch(typeof(InputManager), "KeyUp")] [HarmonyPrefix] public static bool KeyUpPrefix(InputKey key, ref bool __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!Armed || !IsBorrowed(key)) { return true; } __result = false; return false; } [HarmonyPatch(typeof(InputManager), "GetScrollY")] [HarmonyPrefix] public static bool ScrollPrefix(ref float __result) { if (!Armed) { return true; } __result = 0f; return false; } [HarmonyPatch(typeof(InputManager), "KeyPullAndPush")] [HarmonyPrefix] public static bool PullPushPrefix(ref float __result) { if (!Armed) { return true; } __result = 0f; return false; } } public static class BotIntercom { private static bool _held; private static float _heldSince; private static bool _warned; private static float _released; public static bool Open => _held; public static bool RecentlyOpen { get { if (!_held) { return Time.unscaledTime - _released < 1.5f; } return true; } } public static KeyCode Key { get { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) try { if (Plugin.CfgIntercomKey == null) { return (KeyCode)308; } string value = Plugin.CfgIntercomKey.Value; if (string.IsNullOrEmpty(value) || value.Equals("none", StringComparison.OrdinalIgnoreCase)) { return (KeyCode)0; } return (KeyCode)Enum.Parse(typeof(KeyCode), value, ignoreCase: true); } catch { if (!_warned) { _warned = true; Plugin.Log.LogWarning((object)"[Intercom] Voice.IntercomKey isn't a valid KeyCode — using LeftAlt."); } return (KeyCode)308; } } } public static void Reset() { _held = false; _released = 0f; } public static void Tick() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) try { KeyCode key = Key; if ((int)key == 0) { _held = false; BotSelfHear.Tick(want: false); return; } bool key2 = Input.GetKey(key); BotSelfHear.Tick(key2); if (key2 == _held) { return; } _held = key2; if (key2) { _heldSince = Time.unscaledTime; BotEars.OpenIntercom(); string availability = BotEars.Availability; if (availability != null) { Plugin.Log.LogWarning((object)("[Intercom] Key held, but speech recognition is NOT running: " + availability + " — nothing can be heard. Use the buddy panel (hold ALT and scroll) or type in chat instead.")); } else { Plugin.Log.LogWarning((object)"[Intercom] OPEN — speak now, no wake word needed."); } } else { _released = Time.unscaledTime; Plugin.Log.LogInfo((object)("[Intercom] Closed after " + (Time.unscaledTime - _heldSince).ToString("F1") + "s.")); BotEars.CloseIntercom(); } } catch { } } } public static class BotJukebox { public const int SampleRate = 22050; private const float MaxSeconds = 100f; private static AudioSource _source; private static string _nowPlaying; private static readonly Dictionary Library = new Dictionary(); private static string _folder; public static string Folder { get { if (_folder != null) { return _folder; } try { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "midi"); if (Directory.Exists(text)) { return _folder = text; } string[] directories = Directory.GetDirectories(Paths.PluginPath, "midi", SearchOption.AllDirectories); foreach (string text2 in directories) { if (Directory.GetFiles(text2, "*.mid").Length != 0) { return _folder = text2; } } return _folder = text; } catch { return _folder = Path.Combine(Paths.PluginPath, "midi"); } } } public static bool Playing { get { if ((Object)(object)_source != (Object)null) { return _source.isPlaying; } return false; } } public static string NowPlaying => _nowPlaying; public static void Rescan() { Library.Clear(); try { if (!Directory.Exists(Folder)) { Directory.CreateDirectory(Folder); Plugin.Log.LogInfo((object)("[Jukebox] Created " + Folder + " — drop .mid files in there.")); return; } string[] files = Directory.GetFiles(Folder, "*.mid", SearchOption.AllDirectories); foreach (string text in files) { Library[Path.GetFileNameWithoutExtension(text).ToLowerInvariant()] = text; } files = Directory.GetFiles(Folder, "*.midi", SearchOption.AllDirectories); foreach (string text2 in files) { Library[Path.GetFileNameWithoutExtension(text2).ToLowerInvariant()] = text2; } Plugin.Log.LogInfo((object)("[Jukebox] " + Library.Count + " track(s) in the collection.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Jukebox] Rescan: " + ex.Message)); } } public static string Find(string spoken) { if (string.IsNullOrEmpty(spoken)) { return null; } if (Library.Count == 0) { Rescan(); } string[] array = Normalise(spoken).Split(' '); string result = null; int num = 0; foreach (KeyValuePair item in Library) { string text = Normalise(item.Key); int num2 = 0; string[] array2 = array; foreach (string text2 in array2) { if (text2.Length >= 3 && text.Contains(text2)) { num2 += text2.Length; } } if (num2 > num) { num = num2; result = item.Value; } } if (num < 3) { return null; } return result; } private static string Normalise(string s) { StringBuilder stringBuilder = new StringBuilder(); string text = s.ToLowerInvariant(); foreach (char c in text) { stringBuilder.Append(char.IsLetterOrDigit(c) ? c : ' '); } return stringBuilder.ToString(); } public static string PlayRandom() { if (Library.Count == 0) { Rescan(); } if (Library.Count == 0) { return null; } List list = new List(Library.Values); return Play(list[Random.Range(0, list.Count)]); } public static string Play(string path) { try { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return null; } return PlayBytes(File.ReadAllBytes(path), Path.GetFileNameWithoutExtension(path)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Jukebox] Play: " + ex.Message)); return null; } } public static string PlayBytes(byte[] bytes, string displayName) { try { if (bytes == null || bytes.Length < 16) { return null; } MidiSong midiSong = MidiReader.Read(bytes); if (midiSong == null || midiSong.Notes.Count == 0) { Plugin.Log.LogWarning((object)("[Jukebox] '" + displayName + "' had no playable notes.")); return null; } float[] array = MidiSynth.Render(midiSong, 100f); if (array == null || array.Length == 0) { return null; } AudioClip val = AudioClip.Create(displayName, array.Length, 1, 22050, false); val.SetData(array, 0); EnsureSource(); if ((Object)(object)_source == (Object)null) { return null; } _source.clip = val; _source.Play(); _nowPlaying = displayName; Plugin.Log.LogInfo((object)("[Jukebox] Playing '" + _nowPlaying + "' (" + midiSong.Notes.Count + " notes, " + array.Length / 22050 + "s).")); return _nowPlaying; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Jukebox] Play: " + ex.Message)); return null; } } public static void Stop() { try { if ((Object)(object)_source != (Object)null) { _source.Stop(); } } catch { } _nowPlaying = null; } public static void Tick(Vector3 botPos) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_source != (Object)null) { ((Component)_source).transform.position = botPos + Vector3.up * 1.5f; } } private static void EnsureSource() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if ((Object)(object)_source != (Object)null) { return; } try { GameObject val = new GameObject("BotSpike.Jukebox"); Object.DontDestroyOnLoad((Object)val); _source = val.AddComponent(); _source.spatialBlend = 1f; _source.rolloffMode = (AudioRolloffMode)1; _source.minDistance = 3f; _source.maxDistance = 28f; _source.volume = 0.55f; _source.loop = false; AudioSource val2 = null; AudioSource[] array = Object.FindObjectsOfType(); foreach (AudioSource val3 in array) { if ((Object)(object)val3 != (Object)(object)_source && (Object)(object)val3.outputAudioMixerGroup != (Object)null) { val2 = val3; break; } } if ((Object)(object)val2 != (Object)null) { _source.outputAudioMixerGroup = val2.outputAudioMixerGroup; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Jukebox] Source: " + ex.Message)); } } public static IEnumerator Fetch(string spoken, Action done) { string text = ((Plugin.CfgMidiUrl != null) ? Plugin.CfgMidiUrl.Value : ""); if (string.IsNullOrEmpty(text)) { done(null); yield break; } string text2 = text.Replace("{q}", UnityWebRequest.EscapeURL(spoken)); Plugin.Log.LogInfo((object)("[Jukebox] Looking up '" + spoken + "'.")); UnityWebRequest req = UnityWebRequest.Get(text2); try { req.timeout = 12; yield return req.SendWebRequest(); byte[] array = null; try { if ((int)req.result == 1) { array = req.downloadHandler.data; } } catch { } if (array == null || array.Length < 16 || array[0] != 77 || array[1] != 84 || array[2] != 104 || array[3] != 100) { Plugin.Log.LogInfo((object)("[Jukebox] Nothing usable came back for '" + spoken + "'.")); done(null); yield break; } done(PlayBytes(array, spoken)); } finally { ((IDisposable)req)?.Dispose(); } } } public static class BotLastStand { private const float Grace = 60f; private static float _nextWarn; private static bool _detonated; public static bool Running { get; private set; } public static float Remaining { get; private set; } public static void Reset() { Running = false; Remaining = 0f; _nextWarn = 0f; _detonated = false; } public static void Tick(PlayerAvatar bot, float dt) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)bot == (Object)null) { Reset(); return; } if (Reflect.Get(bot, "isDisabled") || Reflect.Get(bot, "deadSet")) { Stop(announce: false); return; } if (!AloneWithTheDead(bot)) { Stop(announce: true); return; } if (!Running) { Running = true; Remaining = 60f; _nextWarn = 45f; _detonated = false; Plugin.Log.LogWarning((object)("[LastStand] Everyone else is down. He has " + 60 + " seconds to fix it.")); BotPersona.SpeakExact(BotPersona.AloneStart, ((Component)bot).transform.position); BotMedic.OrderRevive(((Component)bot).transform.position); return; } Remaining -= dt; if (Remaining > 10f && Mathf.FloorToInt(Remaining) % 15 == 0 && Mathf.FloorToInt(Remaining) != Mathf.FloorToInt(Remaining + dt)) { BotMedic.OrderRevive(((Component)bot).transform.position); } if (Remaining <= _nextWarn && _nextWarn > 0f) { BotPersona.Speak("alonewarn", BotPersona.AloneWarn, 1f, 0f, ((Component)bot).transform.position, Mathf.CeilToInt(Remaining).ToString()); Plugin.Log.LogInfo((object)("[LastStand] " + Mathf.CeilToInt(Remaining) + "s left.")); _nextWarn = ((_nextWarn > 45f) ? 45f : ((_nextWarn > 30f) ? 30f : ((_nextWarn > 10f) ? 10f : 0f))); } if (Remaining <= 0f && !_detonated) { _detonated = true; Plugin.Log.LogWarning((object)"[LastStand] Time's up — he goes too, so the run can end."); BotPersona.SpeakExact(BotPersona.AloneEnd, ((Component)bot).transform.position); GiveUp(bot); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[LastStand] " + ex.Message)); } } private static void Stop(bool announce) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!Running) { return; } Running = false; Remaining = 0f; if (announce) { Plugin.Log.LogInfo((object)"[LastStand] Somebody's up again — clock cancelled."); PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botAvatar != (Object)null && !_detonated) { BotPersona.Speak("alonesaved", BotPersona.AloneSaved, 1f, 0f, ((Component)botAvatar).transform.position); } } } private static bool AloneWithTheDead(PlayerAvatar bot) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } if (!SemiFunc.RunIsLevel()) { return false; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { num++; if (!Reflect.Get(player, "isDisabled")) { return false; } } } return num > 0; } catch { } return false; } private static void GiveUp(PlayerAvatar bot) { try { bot.PlayerDeath(-1); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[LastStand] Death failed: " + ex.Message)); } } } public static class BotLastStandHud { private static Texture2D _whiteTex; private static Texture2D _dialTex; private static Texture2D _capTex; private static float _alpha; private static GUIStyle _headerStyle; private static GUIStyle _digitalStyle; private static GUIStyle _subtextStyle; private static Texture2D WhiteTex { get { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_whiteTex == (Object)null) { _whiteTex = new Texture2D(1, 1); _whiteTex.SetPixel(0, 0, Color.white); _whiteTex.Apply(); } return _whiteTex; } } private static Texture2D DialTex { get { if ((Object)(object)_dialTex == (Object)null) { _dialTex = MakeDialTexture(120); } return _dialTex; } } private static Texture2D CapTex { get { if ((Object)(object)_capTex == (Object)null) { _capTex = MakeCapTexture(12); } return _capTex; } } public static void Draw() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: 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_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Unknown result type (might be due to invalid IL or missing references) //IL_0486: Unknown result type (might be due to invalid IL or missing references) //IL_049a: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) try { bool num = BotLastStand.Running && BotLastStand.Remaining > 0f; float unscaledDeltaTime = Time.unscaledDeltaTime; if (num) { _alpha = Mathf.MoveTowards(_alpha, 1f, unscaledDeltaTime * 3f); } else { _alpha = Mathf.MoveTowards(_alpha, 0f, unscaledDeltaTime * 2f); } if (!(_alpha <= 0.005f)) { float num2 = Mathf.Clamp(BotLastStand.Remaining, 0f, 60f); float num3 = 60f; float num4 = num2 / num3; Color val = default(Color); Color textColor = default(Color); if (num2 > 35f) { ((Color)(ref val))..ctor(0.96f, 0.7f, 0.2f, _alpha); textColor = val; } else if (num2 > 15f) { ((Color)(ref val))..ctor(1f, 0.45f, 0.1f, _alpha); textColor = val; } else { float num5 = 0.65f + 0.35f * Mathf.Sin(Time.unscaledTime * 12f); ((Color)(ref val))..ctor(1f, 0.18f, 0.24f, _alpha); ((Color)(ref textColor))..ctor(1f, 0.18f, 0.24f, _alpha * num5); } float num6 = 55f; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor((float)Screen.width / 2f, 85f); Color color = GUI.color; Matrix4x4 matrix = GUI.matrix; InitStyles(); _headerStyle.normal.textColor = textColor; GUI.Label(new Rect(val2.x - 120f, val2.y - num6 - 26f, 240f, 22f), "LAST STAND", _headerStyle); GUI.color = new Color(1f, 1f, 1f, _alpha); GUI.DrawTexture(new Rect(val2.x - num6, val2.y - num6, num6 * 2f, num6 * 2f), (Texture)(object)DialTex); for (int i = 0; i < 12; i++) { float num7 = (float)i * 30f; bool num8 = i % 3 == 0; GUI.matrix = matrix; GUIUtility.RotateAroundPivot(num7, val2); float num9 = (num8 ? 9f : 5f); float num10 = (num8 ? 3f : 1.5f); float num11 = 1f - (float)i / 12f; GUI.color = (Color)((num4 >= num11) ? val : new Color(0.3f, 0.35f, 0.4f, _alpha * 0.5f)); GUI.DrawTexture(new Rect(val2.x - num10 / 2f, val2.y - num6 + 6f, num10, num9), (Texture)(object)WhiteTex); } float num12 = (1f - num4) * 360f; GUI.matrix = matrix; GUIUtility.RotateAroundPivot(num12, val2); GUI.color = val; float num13 = num6 - 12f; GUI.DrawTexture(new Rect(val2.x - 2f, val2.y - num13, 4f, num13 + 6f), (Texture)(object)WhiteTex); GUI.color = Color.white; GUI.DrawTexture(new Rect(val2.x - 1.5f, val2.y - num13, 3f, 10f), (Texture)(object)WhiteTex); GUI.matrix = matrix; GUI.color = new Color(1f, 1f, 1f, _alpha); GUI.DrawTexture(new Rect(val2.x - 6f, val2.y - 6f, 12f, 12f), (Texture)(object)CapTex); int num14 = Mathf.CeilToInt(num2); int num15 = num14 / 60; int num16 = num14 % 60; string text = $"{num15:D2}:{num16:D2}"; _digitalStyle.normal.textColor = textColor; GUI.Label(new Rect(val2.x - 80f, val2.y + num6 + 6f, 160f, 24f), text, _digitalStyle); _subtextStyle.normal.textColor = new Color(0.8f, 0.85f, 0.9f, _alpha * 0.85f); GUI.Label(new Rect(val2.x - 120f, val2.y + num6 + 28f, 240f, 18f), "DETONATION COUNTDOWN", _subtextStyle); GUI.color = color; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[LastStandHud] " + ex.Message)); } } private static void InitStyles() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown if (_headerStyle == null) { _headerStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 15, fontStyle = (FontStyle)1 }; } if (_digitalStyle == null) { _digitalStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 18, fontStyle = (FontStyle)1 }; } if (_subtextStyle == null) { _subtextStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 11, fontStyle = (FontStyle)0 }; } } private static Texture2D MakeDialTexture(int size) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false); float num = (float)(size - 1) / 2f; float num2 = (float)size / 2f; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { float num3 = (float)j - num; float num4 = (float)i - num; float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4); if (num5 > num2) { val.SetPixel(j, i, Color.clear); } else if (num5 > num2 - 3.5f) { float num6 = Mathf.Clamp01(num2 - num5); val.SetPixel(j, i, new Color(0.72f, 0.54f, 0.22f, num6 * 0.95f)); } else if (num5 > num2 - 7f) { val.SetPixel(j, i, new Color(0.18f, 0.2f, 0.24f, 0.95f)); } else { val.SetPixel(j, i, new Color(0.08f, 0.09f, 0.12f, 0.92f)); } } } val.Apply(); return val; } private static Texture2D MakeCapTexture(int size) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false); float num = (float)(size - 1) / 2f; float num2 = (float)size / 2f; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { float num3 = (float)j - num; float num4 = (float)i - num; float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4); if (num5 > num2) { val.SetPixel(j, i, Color.clear); continue; } float num6 = Mathf.Clamp01(num2 - num5); val.SetPixel(j, i, new Color(0.85f, 0.65f, 0.25f, num6 * 0.98f)); } } val.Apply(); return val; } } [HarmonyPatch] public static class BotLocalGuards { private static readonly HashSet Told = new HashSet(); private static bool Off { get { if (Plugin.CfgLocalGuards != null) { return !Plugin.CfgLocalGuards.Value; } return false; } } private static void Once(string key, string message) { if (Told.Add(key)) { Plugin.Log.LogInfo((object)("[Guard] " + message)); } } [HarmonyPatch(typeof(PlayerAvatar), "SoundSetup")] [HarmonyPostfix] public static void AvatarSoundIn3D(PlayerAvatar __instance, Sound _sound) { if (!Off && !((Object)(object)__instance == (Object)null) && BotSpawner.IsBot(__instance)) { MakeWorldSound(_sound, "avatar"); } } [HarmonyPatch(typeof(PhysGrabber), "SoundSetup")] [HarmonyPostfix] public static void GrabberSoundIn3D(PhysGrabber __instance, Sound _sound) { if (Off) { return; } try { PlayerAvatar val = Reflect.Get(__instance, "playerAvatar"); if (!((Object)(object)val == (Object)null) && BotSpawner.IsBot(val)) { MakeWorldSound(_sound, "grabber"); } } catch { } } private static void MakeWorldSound(Sound s, string which) { try { if (s != null && !Mathf.Approximately(s.SpatialBlend, 1f)) { s.Volume = Reflect.Get(s, "VolumeDefault") * 0.5f; s.VolumeRandom = Reflect.Get(s, "VolumeRandomDefault") * 0.5f; s.SpatialBlend = 1f; Once("sound." + which, "Put his " + which + " sounds back into the world. The host owns him, so SoundSetup was giving him the 2D full-volume treatment reserved for your OWN footsteps — every noise he made was playing flat inside your head regardless of distance or walls."); } } catch { } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupCosmetics")] [HarmonyPrefix] public static void NeverWearYourClothes(PlayerCosmetics __instance, ref List _cosmetics) { if (Off) { return; } try { if (IsBotCosmetics(__instance) && (_cosmetics == null || _cosmetics.Count != 0)) { _cosmetics = new List(); Once("cosmetics", "Stopped him inheriting your saved cosmetics. SetupCosmetics falls back to MetaManager.cosmeticEquipped — your own wardrobe — for anything the host owns, and the death head resolves through the same call."); } } catch { } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColors")] [HarmonyPrefix] public static void HisOwnColour(PlayerCosmetics __instance, ref int[] _colors) { if (Off) { return; } try { if (!IsBotCosmetics(__instance) || _colors != null) { return; } int num = 8; try { int[] array = Reflect.Get(__instance, "colorsEquipped"); if (array != null && array.Length != 0) { num = array.Length; } } catch { } int num2 = ((Plugin.CfgBotColor != null) ? Plugin.CfgBotColor.Value : 6); try { List list = (((Object)(object)MetaManager.instance != (Object)null) ? MetaManager.instance.colors : null); if (list != null && list.Count > 0) { num2 = (num2 % list.Count + list.Count) % list.Count; } } catch { } int[] array2 = new int[num]; for (int i = 0; i < array2.Length; i++) { array2[i] = num2; } _colors = array2; Once("colors", "Gave him his own colour (index " + num2 + "). SetupColors falls back to MetaManager.colorsEquipped — YOUR saved colour — for anything the host owns, which is what made his corpse look like yours. Change it with General.BotColor."); } catch { } } private static bool IsBotCosmetics(PlayerCosmetics pc) { try { if ((Object)(object)pc == (Object)null) { return false; } PlayerDeathHead val = Reflect.Get(pc, "deathHead"); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.playerAvatar != (Object)null) { return BotSpawner.IsBot(val.playerAvatar); } PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botAvatar != (Object)null && Reflect.Get(botAvatar, "playerDeathHead") == val) { return true; } return false; } PlayerAvatar componentInParent = ((Component)pc).GetComponentInParent(); return (Object)(object)componentInParent != (Object)null && BotSpawner.IsBot(componentInParent); } catch { return false; } } [HarmonyPatch(typeof(PlayerAvatar), "Update")] [HarmonyPostfix] public static void NoHostPuppetStrings(PlayerAvatar __instance) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (Off) { return; } try { if (!((Object)(object)__instance == (Object)null) && BotSpawner.IsBot(__instance)) { if ((Object)(object)Reflect.Get(__instance, "playerTransform") != (Object)(object)((Component)__instance).transform) { Reflect.Set(__instance, "playerTransform", ((Component)__instance).transform); Once("puppet", "Re-pointed his playerTransform at himself. PlayerAvatar.Start assigns playerTransform = PlayerController.instance.transform for anything the host owns, and Update then copies that position onto him every frame — the original \"there are two of you\" bug, at its source. Aimed at his own transform the copy is a harmless self-assignment."); } Vector3 lastVelocity = BotMovement.LastVelocity; lastVelocity.y = 0f; Vector3 val = ((Component)__instance).transform.InverseTransformDirection(lastVelocity); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(val.x, val.z); if (((Vector2)(ref val2)).sqrMagnitude > 1f) { ((Vector2)(ref val2)).Normalize(); } Reflect.Set(__instance, "InputDirection", val2); Reflect.Set(__instance, "InputDirectionRaw", val2); } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "UpdateMyPlayerVoiceChat")] [HarmonyPrefix] public static bool NoStealingYourVoice(PlayerAvatar __instance, int photonViewID) { if (Off) { return true; } try { if ((Object)(object)__instance == (Object)null || !BotSpawner.IsBot(__instance)) { return true; } PlayerVoiceChat instance = PlayerVoiceChat.instance; if ((Object)(object)instance == (Object)null) { return true; } PhotonView component = ((Component)instance).GetComponent(); if ((Object)(object)component == (Object)null || component.ViewID != photonViewID) { return true; } object obj = Reflect.Get(__instance, "voiceChat"); PlayerVoiceChat val = (PlayerVoiceChat)((obj is PlayerVoiceChat) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)instance) { Plugin.Log.LogWarning((object)"[Guard] He has no voice of his own, so he is borrowing YOURS rather than going silent. Expect his TTS to come out of your voice object — wrong, but audible. GiveOwnVoice should have replaced this; if you are seeing it, that is the real bug."); return true; } Once("voicesteal", "Refused to let him rebind onto YOUR voice object — he already has his own. PlayerAvatar.Update falls back to PlayerVoiceChat.instance for anything the host owns, on a BUFFERED RPC, so the wrong answer would have been replayed to every future joiner."); return false; } catch { return true; } } } public static class BotLocalTest { public static string State { get { try { if ((Object)(object)GameManager.instance == (Object)null) { return "-"; } if (!GameManager.instance.localTest) { return "normal"; } return "LOCAL TEST (" + (PhotonNetwork.IsMasterClient ? "host" : "client") + ")"; } catch { return "-"; } } } public static void Arm() { try { if ((Object)(object)RunManager.instance == (Object)null) { Plugin.Log.LogWarning((object)"[LocalTest] No RunManager yet — press F6 at the main menu."); return; } if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.localTest) { Plugin.Log.LogWarning((object)"[LocalTest] Already in local-test mode."); return; } Reflect.Set(RunManager.instance, "localMultiplayerTest", true); Plugin.Log.LogWarning((object)"[LocalTest] ARMED. Now start a game as you normally would — it will divert into the game's own local multiplayer test flow instead of a Steam lobby.\n Launch a SECOND copy of REPO.exe directly (not through Steam), press F6 there too, and start a game.\n Both instances use RoomName = your Steam persona name, so they will meet in the same room. Region is forced to 'eu'.\n The second player to arrive is the CLIENT — that is the machine whose log is worth reading."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[LocalTest] " + ex.Message)); } } } public static class BotMedic { public enum Phase { None, Approaching, Announcing, CarryingHead } private static PlayerAvatar _patient; private static float _announceTimer; private static float _scanTimer; private static float _giveUpTimer; private static float _cooldown; private static bool _ordered; private static PlayerAvatar _healTarget; private const float SearchRange = 30f; private const float ReachDistance = 1.8f; private const float SafeRange = 14f; private const float AnnounceBeat = 1.2f; private const float HealthCost = 25f; private const float GiveUpAfter = 30f; private const float Cooldown = 6f; private static float _refuseAt; private static float _urgentAt; private const float HealTransfer = 25f; public static Phase Current { get; private set; } = Phase.None; public static float SacrificedAt { get; private set; } = -99f; public static void Reset() { Current = Phase.None; _ordered = false; _healTarget = null; _patient = null; _announceTimer = 0f; _giveUpTimer = 0f; } public static bool Tick(GameObject bot, PlayerAvatar me, float dt) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || (Object)(object)me == (Object)null) { return false; } if (Plugin.CfgJumpStart == null || !Plugin.CfgJumpStart.Value) { return false; } if (_cooldown > 0f) { _cooldown -= dt; } if (IsDown(me) || Reflect.Get(me, "deadSet")) { Reset(); return false; } Vector3 position = bot.transform.position; switch (Current) { case Phase.None: { if (_cooldown > 0f) { return false; } _scanTimer -= dt; if (_scanTimer > 0f) { return false; } _scanTimer = 1f; _patient = FindDeadCrewmate(position, me); if ((Object)(object)_patient == (Object)null) { return false; } bool flag = EveryHumanDown(); if (flag && Time.unscaledTime > _urgentAt) { _urgentAt = Time.unscaledTime + 20f; BotPersona.Speak("lasthope", BotPersona.LastHope, 1f, 20f, position); } if (!_ordered && !flag && !CanAffordIt(me)) { Current = Phase.CarryingHead; _giveUpTimer = 30f; Plugin.Log.LogInfo((object)("[Medic] Too weak to revive — carrying " + SemiFunc.PlayerGetName(_patient) + "'s head to a crewmate/cart.")); return true; } Current = Phase.Approaching; _giveUpTimer = 30f; Plugin.Log.LogInfo((object)("[Medic] Going to jump-start " + SemiFunc.PlayerGetName(_patient) + ".")); return true; } case Phase.Approaching: { _giveUpTimer -= dt; if (!StillValid(_patient) || _giveUpTimer <= 0f) { Abort("patient gone or timed out"); return false; } if (!HeadPosition(_patient, out var result)) { Abort("no death head"); return false; } if (!AreaIsSafe(position)) { Abort("not safe"); return false; } BotMovement.SetDestination(new Vector3(result.x, position.y, result.z)); if (Vector3.Distance(position, result) <= 1.8f) { BotMovement.Stop(); BotMovement.FaceTowards(result); Current = Phase.Announcing; _announceTimer = 1.2f; BotPersona.SpeakExact(BotPersona.Cables, position); } return true; } case Phase.Announcing: { BotMovement.Stop(); if (HeadPosition(_patient, out var result2)) { BotMovement.FaceTowards(result2); } _announceTimer -= dt; if (_announceTimer > 0f) { return true; } if (!StillValid(_patient)) { Abort("patient revived by someone else"); return false; } JumpStart(_patient, me); _ordered = false; Current = Phase.None; _patient = null; _cooldown = 6f; return true; } case Phase.CarryingHead: { _giveUpTimer -= dt; if (!StillValid(_patient) || _giveUpTimer <= 0f) { Abort("patient gone or timed out"); return false; } PhysGrabObject headPhysGrab = GetHeadPhysGrab(_patient); if ((Object)(object)headPhysGrab == (Object)null) { Abort("no head grab object"); return false; } Transform transform = ((Component)headPhysGrab).transform; float num = Vector3.Distance(position, transform.position); if ((Object)(object)BotHands.Carried != (Object)(object)headPhysGrab) { if (num > 1.8f) { BotMovement.SetDestination(transform.position); return true; } BotHands.GrabDirectly(headPhysGrab); return true; } PlayerAvatar val = FindLivingCrewmate(position, me); Vector3 val2 = Vector3.zero; string text = null; Vector3 cartPos; Vector3 spot; Vector3 pos; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).transform.position; text = SemiFunc.PlayerGetName(val); } else if (FindCartPos(position, out cartPos)) { val2 = cartPos; text = "cart"; } else if (ExtractionDropOff(out spot)) { val2 = spot; text = "extraction"; } else if (BotBuddy.AnchorPosition(out pos)) { val2 = pos; } if (val2 != Vector3.zero) { BotMovement.SetDestination(val2); if (Vector3.Distance(position, val2) <= 2f) { BotHands.DropEverything(); BotPersona.SpeakYourProblemNow(position, text); Plugin.Log.LogInfo((object)("[Medic] Delivered " + SemiFunc.PlayerGetName(_patient) + "'s head to " + (text ?? "team") + ".")); Current = Phase.None; _patient = null; _cooldown = 6f; return true; } } return true; } default: return false; } } private static void Abort(string why) { if (Current != Phase.None) { Plugin.Log.LogInfo((object)("[Medic] Abandoning the rescue — " + why + ".")); } Reset(); } private static void JumpStart(PlayerAvatar patient, PlayerAvatar me) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) try { patient.Revive(false); Plugin.Log.LogInfo((object)("[Medic] Jump-started " + SemiFunc.PlayerGetName(patient) + " at 1 HP.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Medic] Revive failed: " + ex.Message)); return; } try { PlayerHealth playerHealth = me.playerHealth; if ((Object)(object)playerHealth == (Object)null) { return; } int num = Reflect.Get(playerHealth, "health"); int num2 = Reflect.Get(playerHealth, "maxHealth"); if (num2 <= 0 || num <= 0) { Plugin.Log.LogWarning((object)("[Medic] Skipping the health cost — his health reads " + num + "/" + num2 + ", which means it never initialised.")); BotPersona.SpeakExact(BotPersona.Revived, ((Component)me).transform.position); return; } if ((float)num <= 25f && DeathWouldEndTheRun(me, patient)) { Plugin.Log.LogInfo((object)"[Medic] Skipping the cost — paying it would have ended the run."); BotPersona.Speak("survived", BotPersona.LuckyThisTime, 1f, 30f, ((Component)me).transform.position); _ordered = false; return; } playerHealth.Hurt(25, false, -1, false); int num3 = Reflect.Get(playerHealth, "health"); Plugin.Log.LogInfo((object)("[Medic] Cost him " + num + " -> " + num3 + " HP.")); if (num3 > 0 && !((float)num <= 25f)) { BotPersona.SpeakExact(BotPersona.Revived, ((Component)me).transform.position); return; } SacrificedAt = Time.unscaledTime; BotPersona.SpeakExact(BotPersona.NobleDeath, ((Component)me).transform.position, SemiFunc.PlayerGetName(patient)); if (Reflect.Get(playerHealth, "health") > 0) { Plugin.Log.LogInfo((object)"[Medic] The cost should have killed him but didn't — finishing it."); Reflect.Set(playerHealth, "health", 0); } try { me.PlayerDeath(-1); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Medic] Noble death failed: " + ex2.Message)); } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("[Medic] Cost failed: " + ex3.Message)); } } private static bool EveryHumanDown() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } bool result = false; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { result = true; if (!IsDown(player)) { return false; } } } return result; } catch { return false; } } private static bool ExtractionDropOff(out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { ExtractionPoint val = (((Object)(object)RoundDirector.instance != (Object)null) ? Reflect.Get(RoundDirector.instance, "extractionPointCurrent") : null); if ((Object)(object)val != (Object)null) { spot = BotChute.StandingSpot(val); return spot != Vector3.zero; } } catch { } return false; } private static bool CanAffordIt(PlayerAvatar me) { try { PlayerHealth val = (((Object)(object)me != (Object)null) ? me.playerHealth : null); if ((Object)(object)val == (Object)null) { return true; } int num = Reflect.Get(val, "health"); if (num <= 0) { return true; } return (float)num > 25f; } catch { return true; } } private static bool DeathWouldEndTheRun(PlayerAvatar me, PlayerAvatar aboutToRevive) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && player != me) { if (player == aboutToRevive) { return false; } if (!IsDown(player)) { return false; } } } return true; } catch { return false; } } public static bool OrderRevive(Vector3 botPos) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatar me = null; if ((Object)(object)GameDirector.instance != (Object)null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (BotSpawner.IsBot(player)) { me = player; break; } } } PlayerAvatar val = FindDeadCrewmate(botPos, me); if ((Object)(object)val == (Object)null) { BotPersona.Speak("nobody", BotPersona.NoOneToRevive, 1f, 10f, botPos); return false; } _ordered = true; _patient = val; Current = Phase.Approaching; _giveUpTimer = 30f; BotPersona.Speak("onit", BotPersona.OnItRevive, 1f, 5f, botPos, SemiFunc.PlayerGetName(val)); Plugin.Log.LogInfo((object)("[Medic] ORDERED to revive " + SemiFunc.PlayerGetName(val) + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Medic] OrderRevive: " + ex.Message)); return false; } } public static bool OrderHeal(PlayerAvatar target, Vector3 botPos) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatar val = null; if ((Object)(object)GameDirector.instance != (Object)null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (BotSpawner.IsBot(player)) { val = player; break; } } } if ((Object)(object)val == (Object)null || (Object)(object)target == (Object)null) { return false; } PlayerHealth playerHealth = val.playerHealth; PlayerHealth playerHealth2 = target.playerHealth; if ((Object)(object)playerHealth == (Object)null || (Object)(object)playerHealth2 == (Object)null) { return false; } int num = Reflect.Get(playerHealth, "health"); int num2 = Reflect.Get(playerHealth2, "health"); int num3 = Reflect.Get(playerHealth2, "maxHealth"); if (num3 > 0 && num2 >= num3) { BotPersona.Speak("nothurt", BotPersona.NotHurt, 1f, 15f, botPos, SemiFunc.PlayerGetName(target)); return false; } int num4 = Mathf.Max(0, num - 20); int num5 = Mathf.Min(25, num4); if (num3 > 0) { num5 = Mathf.Min(num5, num3 - num2); } if (num5 <= 0) { BotPersona.Speak("tooweak", BotPersona.TooWeakToRevive, 1f, 20f, botPos); return false; } BotOpinions.Thank(target, 0.5f, "accepted help gracefully"); playerHealth2.HealOther(num5, true); playerHealth.Hurt(num5, false, -1, false); BotPersona.Speak("healed", BotPersona.Healed, 1f, 8f, botPos, SemiFunc.PlayerGetName(target)); Plugin.Log.LogInfo((object)("[Medic] Transferred " + num5 + " HP to " + SemiFunc.PlayerGetName(target) + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Medic] OrderHeal: " + ex.Message)); return false; } } private static bool IsDown(PlayerAvatar p) { if ((Object)(object)p != (Object)null) { return Reflect.Get(p, "isDisabled"); } return false; } private static bool StillValid(PlayerAvatar p) { if ((Object)(object)p == (Object)null) { return false; } if (!IsDown(p)) { return false; } Vector3 result; return HeadPosition(p, out result); } private static bool HeadPosition(PlayerAvatar p, out Vector3 result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) result = Vector3.zero; try { object obj = Reflect.Get(p, "playerDeathHead"); if (obj == null) { return false; } Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } result = val.transform.position; return true; } catch { return false; } } private static PlayerAvatar FindDeadCrewmate(Vector3 from, PlayerAvatar me) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar result = null; float num = float.MaxValue; try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || player == me || BotSpawner.IsBot(player) || !IsDown(player) || !HeadPosition(player, out var result2)) { continue; } float num2 = Vector3.Distance(from, result2); if (!(num2 > 30f)) { float num3 = num2 - BotOpinions.FavourOf(player) * 4f; if (!(num3 >= num)) { num = num3; result = player; } } } } catch { } return result; } private static bool AreaIsSafe(Vector3 pos) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) try { if (BotDanger.Level == BotDanger.Threat.Hiding) { return false; } if ((Object)(object)EnemyDirector.instance == (Object)null) { return true; } int blockers = BotSight.Blockers; Vector3 val = pos + Vector3.up * 1.2f; foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if ((Object)(object)item == (Object)null) { continue; } Enemy val2 = Reflect.Get(item, "Enemy"); if ((Object)(object)val2 == (Object)null || BotDanger.IsMinorThreat(item, val2)) { continue; } Vector3 position = ((Component)val2).transform.position; if (!(Vector3.Distance(pos, position) > 14f)) { if (blockers == 0) { return false; } if (!Physics.Linecast(val, position + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1)) { return false; } } } } catch { } return true; } private static PhysGrabObject GetHeadPhysGrab(PlayerAvatar patient) { try { if ((Object)(object)patient == (Object)null) { return null; } object obj = Reflect.Get(patient, "playerDeathHead"); if (obj == null) { return null; } Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } return val.GetComponent() ?? val.GetComponentInChildren(); } catch { return null; } } private static PlayerAvatar FindLivingCrewmate(Vector3 from, PlayerAvatar me) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar result = null; float num = float.MaxValue; try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && player != me && !BotSpawner.IsBot(player) && !IsDown(player)) { float num2 = Vector3.Distance(from, ((Component)player).transform.position); if (num2 < num) { num = num2; result = player; } } } } catch { } return result; } private static bool FindCartPos(Vector3 from, out Vector3 cartPos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) cartPos = Vector3.zero; try { PhysGrabCart val = null; float num = float.MaxValue; PhysGrabCart[] array = Object.FindObjectsOfType(); foreach (PhysGrabCart val2 in array) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(from, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null) { cartPos = ((Component)val).transform.position; return true; } } catch { } return false; } } public static class BotMovement { private static Vector3[] _corners = Array.Empty(); private static int _corner; private static Vector3 _goal; private static bool _hasGoal; private static float _repathTimer; private static Vector3 _velocity; private static float _yaw; private static float _speedJitter = 1f; private static float _jitterTimer; private static float _pauseTimer; private static float _nextPauseIn = 6f; private static float _strafeBias; private static float _lookSweep; private static float _blockedTimer; private static float _fallTimer; private static float _fallProbeAt; private static float _vertVel; private static Vector3 _lookAt; private static bool _hasLookAt; private static float _airTime; private static bool _tumbling; private static float _settleTime; private static float _jumpCooldown; private static float _jumpGrace; private static bool _wasGrounded = true; private static float _fallFromY; private static bool _trackingFall; private static readonly List _breadcrumbs = new List(); private static float _crumbTimer; private static Vector3 _lastPos; private static float _stuckTimer; private static bool _usingBreadcrumbs; private static int _agentTypeId; private static bool _agentTypeKnown; private static float _agentRetryAt; private static float _pathWarnAt; private const float WalkSpeed = 3f; private const float Accel = 14f; private const float Decel = 18f; private const float TurnDegPerSec = 540f; private const float CornerReach = 0.85f; private const float RepathInterval = 0.75f; private const float BodyRadius = 0.35f; private const float Gravity = 10f; private const float TerminalVelocity = 30f; private const float DoorPushForce = 22f; private const float MaxStepHeight = 0.55f; private const float ClimbSpeed = 7f; private const float JumpSpeed = 5f; private const float MaxMountHeight = 1.25f; private const float MaxDropAhead = 1.6f; private const float JumpCooldown = 1.1f; private const float FallTumbleHeight = 2.5f; private static int _solidMask; private static readonly RaycastHit[] _hitBuf = (RaycastHit[])(object)new RaycastHit[16]; private static readonly RaycastHit[] _sweepBuf = (RaycastHit[])(object)new RaycastHit[16]; private static readonly Dictionary _doorShoveAt = new Dictionary(); private static float _unstickHopAt; public static float SpeedScale { get; set; } = 1f; public static bool DirectMode { get; set; } public static bool ForceCrouch { get; set; } public static bool HasGoal => _hasGoal; public static Vector3 Goal => _goal; public static float CurrentSpeed => ((Vector3)(ref _velocity)).magnitude; public static Vector3 LastVelocity => _velocity; public static bool UsingBreadcrumbs => _usingBreadcrumbs; public static float BlockedFor => _blockedTimer; public static void Reset() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) _corners = Array.Empty(); _corner = 0; _hasGoal = false; _velocity = Vector3.zero; _breadcrumbs.Clear(); _usingBreadcrumbs = false; _stuckTimer = 0f; _vertVel = 0f; _airTime = 0f; _tumbling = false; _settleTime = 0f; _jumpCooldown = 0f; _jumpGrace = 0f; _wasGrounded = true; _trackingFall = false; _unstickHopAt = 0f; } public static void SetDestination(Vector3 goal) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) int num; if (_hasGoal) { Vector3 val = goal - _goal; num = ((((Vector3)(ref val)).sqrMagnitude > 0.25f) ? 1 : 0); } else { num = 1; } _goal = goal; _hasGoal = true; if (num != 0) { _repathTimer = 0f; _usingBreadcrumbs = false; } } public static bool NextCorner(out Vector3 corner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) corner = Vector3.zero; if (_corners == null || _corner >= _corners.Length) { return false; } corner = _corners[_corner]; return true; } public static bool PointAlongPath(float distance, out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) point = Vector3.zero; if (_corners == null || _corner >= _corners.Length) { return false; } Vector3 val = _lastPos; float num = Mathf.Max(0.05f, distance); for (int i = _corner; i < _corners.Length; i++) { Vector3 val2 = Horizontal(_corners[i] - val); float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.0001f) { val = _corners[i]; continue; } if (num <= magnitude) { point = val + val2 / magnitude * num; return true; } num -= magnitude; val = _corners[i]; } point = val; return true; } public static void SpinYaw(float degrees) { _yaw += degrees; if (_yaw > 360f) { _yaw -= 360f; } else if (_yaw < -360f) { _yaw += 360f; } } public static void FaceTowards(Vector3 worldPoint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) _lookAt = worldPoint; _hasLookAt = true; } public static void Stop() { _hasGoal = false; _corners = Array.Empty(); } public static void Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: 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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || dt <= 0f || SceneGuard.Transitioning || (!DirectMode && !LevelReady()) || TickTumble(bot, avatar, dt)) { return; } if (_jumpCooldown > 0f) { _jumpCooldown -= dt; } if (_jumpGrace > 0f) { _jumpGrace -= dt; } Vector3 position = bot.transform.position; RecordBreadcrumbs(dt); if (_pauseTimer > 0f) { _pauseTimer -= dt; Decelerate(dt); _lookSweep += dt; _yaw += Mathf.Sin(_lookSweep * 1.6f) * 55f * dt; Apply(bot, avatar, position, dt); return; } if (!_hasGoal) { Decelerate(dt); Apply(bot, avatar, position, dt); return; } _repathTimer -= dt; if (!DirectMode && _repathTimer <= 0f) { _repathTimer = 0.75f; if (!Replan(position, _goal) && !_usingBreadcrumbs) { _usingBreadcrumbs = _breadcrumbs.Count > 0; if (Time.unscaledTime > _pathWarnAt) { _pathWarnAt = Time.unscaledTime + 15f; Plugin.Log.LogInfo((object)(_usingBreadcrumbs ? "[Bot] No NavMesh path — using breadcrumbs." : "[Bot] No NavMesh path and no breadcrumbs yet — waiting.")); } } } Vector3 waypoint; Vector3 val; if (DirectMode) { waypoint = _goal; val = Horizontal(_goal - position); if (((Vector3)(ref val)).magnitude < 0.9f) { Decelerate(dt); Apply(bot, avatar, position, dt); _hasGoal = false; return; } } else if (!NextWaypoint(position, out waypoint)) { Decelerate(dt); Apply(bot, avatar, position, dt); val = Horizontal(_goal - position); if (((Vector3)(ref val)).magnitude < 1f) { _hasGoal = false; } return; } Vector3 val2 = Horizontal(waypoint - position); if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(0f - normalized.z, 0f, normalized.x); val = normalized + val3 * _strafeBias * 0.12f; normalized = ((Vector3)(ref val)).normalized; JitterSpeed(dt); float num = 3f * _speedJitter * Mathf.Clamp(SpeedScale, 0.2f, 2f); val = Horizontal(_goal - position); float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 1.5f) { num *= Mathf.Clamp(magnitude / 1.5f, 0.25f, 1f); } _velocity = Vector3.MoveTowards(_velocity, normalized * num, 14f * dt); } else { Decelerate(dt); } _nextPauseIn -= dt; if (_nextPauseIn <= 0f) { _nextPauseIn = Random.Range(7f, 16f); if (Random.value < 0.35f) { _pauseTimer = Random.Range(0.35f, 1.1f); } } DetectStuck(position, dt); TryUnstickHop(bot, avatar); Apply(bot, avatar, position, dt); } private static void Decelerate(float dt) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) _velocity = Vector3.MoveTowards(_velocity, Vector3.zero, 18f * dt); } private static void JitterSpeed(float dt) { _jitterTimer -= dt; if (!(_jitterTimer > 0f)) { _jitterTimer = Random.Range(1.2f, 3f); _speedJitter = Random.Range(0.88f, 1.08f); } } public static bool InLobby() { try { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null) { return false; } if (InTransition(instance)) { return false; } return SemiFunc.RunIsLobbyMenu() || SemiFunc.RunIsLobby(); } catch { return false; } } private static bool InTransition(RunManager rm) { if (!Reflect.Get(rm, "restarting")) { return Reflect.Get(rm, "waitToChangeScene"); } return true; } public static bool LevelReady() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Invalid comparison between Unknown and I4 try { if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { return false; } if ((Object)(object)GameDirector.instance == (Object)null || (int)GameDirector.instance.currentState != 2) { return false; } RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null) { return false; } if (InTransition(instance)) { return false; } if ((int)LevelGenerator.Instance.State != 15) { return false; } if (instance.levels != null && !instance.levels.Contains(instance.levelCurrent)) { return false; } string text = (((Object)instance.levelCurrent).name ?? "").ToLowerInvariant(); if (text.Contains("lobby") || text.Contains("menu") || text.Contains("shop") || text.Contains("tutorial")) { return false; } return true; } catch { return false; } } private static Vector3 Horizontal(Vector3 v) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) v.y = 0f; return v; } private static int SolidMask() { if (_solidMask != 0) { return _solidMask; } int num = 0; string[] array = new string[10] { "PlayerVisuals", "PlayerVisualsLocal", "Player", "Enemy", "RoomVolume", "Grab Area", "IgnorePhysGrab", "TopLayerOnly", "Phys Grab Point", "PhysGrabAvoider" }; for (int i = 0; i < array.Length; i++) { int num2 = LayerMask.NameToLayer(array[i]); if (num2 >= 0) { num |= 1 << num2; } } _solidMask = ~num; return _solidMask; } private static bool RescuePoint(out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return false; } Vector3 position = ((Component)instance).transform.position; NavMeshHit val = default(NavMeshHit); if (NavMesh.SamplePosition(position, ref val, 12f, -1)) { spot = ((NavMeshHit)(ref val)).position; return true; } spot = position; return true; } catch { return false; } } private static bool NextWaypoint(Vector3 pos, out Vector3 waypoint) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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_006b: Unknown result type (might be due to invalid IL or missing references) waypoint = _goal; Vector3 val; if (_usingBreadcrumbs) { while (_breadcrumbs.Count > 0) { val = Horizontal(_breadcrumbs[0] - pos); if (!(((Vector3)(ref val)).magnitude < 0.85f)) { break; } _breadcrumbs.RemoveAt(0); } if (_breadcrumbs.Count == 0) { return false; } waypoint = _breadcrumbs[0]; return true; } if (_corners.Length == 0) { val = Horizontal(_goal - pos); return ((Vector3)(ref val)).magnitude > 0.6f; } while (_corner < _corners.Length) { val = Horizontal(_corners[_corner] - pos); if (!(((Vector3)(ref val)).magnitude < 0.85f)) { break; } _corner++; _strafeBias = Random.Range(-1f, 1f); } if (_corner >= _corners.Length) { val = Horizontal(_goal - pos); return ((Vector3)(ref val)).magnitude > 0.6f; } waypoint = _corners[_corner]; return true; } private static bool Replan(Vector3 from, Vector3 to) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0072: Invalid comparison between Unknown and I4 try { NavMeshQueryFilter val = default(NavMeshQueryFilter); ((NavMeshQueryFilter)(ref val)).agentTypeID = AgentTypeId(); ((NavMeshQueryFilter)(ref val)).areaMask = -1; NavMeshQueryFilter val2 = val; NavMeshHit val3 = default(NavMeshHit); if (!NavMesh.SamplePosition(from, ref val3, 3f, val2)) { return false; } NavMeshHit val4 = default(NavMeshHit); if (!NavMesh.SamplePosition(to, ref val4, 5f, val2)) { return false; } NavMeshPath val5 = new NavMeshPath(); if (!NavMesh.CalculatePath(((NavMeshHit)(ref val3)).position, ((NavMeshHit)(ref val4)).position, val2, val5)) { return false; } if ((int)val5.status == 2 || val5.corners.Length == 0) { return false; } _corners = val5.corners; _corner = 0; _usingBreadcrumbs = false; return true; } catch { return false; } } private static int AgentTypeId() { if (_agentTypeKnown) { return _agentTypeId; } if (Time.unscaledTime < _agentRetryAt) { return 0; } _agentRetryAt = Time.unscaledTime + 3f; try { NavMeshAgent[] array = Object.FindObjectsOfType(); foreach (NavMeshAgent val in array) { if (!((Object)(object)val == (Object)null)) { _agentTypeId = val.agentTypeID; _agentTypeKnown = true; Plugin.Log.LogInfo((object)("[Bot] NavMesh agent type resolved: " + _agentTypeId)); return _agentTypeId; } } } catch { } return 0; } private static void RecordBreadcrumbs(float dt) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) _crumbTimer -= dt; if (_crumbTimer > 0f) { return; } _crumbTimer = 0.4f; try { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return; } Vector3 position = ((Component)instance).transform.position; if (_breadcrumbs.Count != 0) { Vector3 val = Horizontal(_breadcrumbs[_breadcrumbs.Count - 1] - position); if (!(((Vector3)(ref val)).magnitude > 1.2f)) { return; } } _breadcrumbs.Add(position); if (_breadcrumbs.Count > 60) { _breadcrumbs.RemoveAt(0); } } catch { } } private static void DetectStuck(Vector3 pos, float dt) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Horizontal(pos - _lastPos); if (((Vector3)(ref val)).magnitude < 0.05f && ((Vector3)(ref _velocity)).magnitude > 0.5f) { _stuckTimer += dt; if (_stuckTimer > 1.5f) { _stuckTimer = 0f; _usingBreadcrumbs = _breadcrumbs.Count > 0; _repathTimer = 0f; Plugin.Log.LogInfo((object)("[Bot] Stuck — replanning" + (_usingBreadcrumbs ? " via breadcrumbs." : "."))); } } else { _stuckTimer = 0f; } _lastPos = pos; } private static bool TickTumble(GameObject bot, PlayerAvatar avatar, float dt) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)avatar == (Object)null) { return false; } if (Plugin.CfgPhysicsFalls != null && !Plugin.CfgPhysicsFalls.Value) { return false; } try { if (SemiFunc.MenuLevel()) { _tumbling = false; _settleTime = 0f; return false; } } catch { } object obj2 = Reflect.Get(avatar, "tumble"); if (obj2 == null) { return false; } bool flag = Reflect.Get(obj2, "isTumbling"); if (_tumbling || flag) { _tumbling = true; Rigidbody val = Reflect.Get(obj2, "rb"); float num; if (!((Object)(object)val != (Object)null)) { num = 0f; } else { Vector3 velocity = val.velocity; num = ((Vector3)(ref velocity)).magnitude; } if (num < 0.7f) { _settleTime += dt; } else { _settleTime = 0f; } if (_settleTime > 0.9f || !flag) { CallTumble(obj2, on: false); _tumbling = false; _settleTime = 0f; _airTime = 0f; _vertVel = 0f; _trackingFall = false; _corners = Array.Empty(); _repathTimer = 0f; _velocity = Vector3.zero; Plugin.Log.LogInfo((object)"[Bot] Landed - back on his feet."); } return true; } if ((_trackingFall ? (_fallFromY - bot.transform.position.y) : 0f) > 2.5f && _jumpGrace <= 0f) { CallTumble(obj2, on: true); _tumbling = true; _settleTime = 0f; Plugin.Log.LogInfo((object)"[Bot] Airborne - handing him to physics."); return true; } return false; } private static void CallTumble(object tumble, bool on) { try { MethodInfo method = tumble.GetType().GetMethod("TumbleRequest", BindingFlags.Instance | BindingFlags.Public); if (method != null) { method.Invoke(tumble, new object[2] { on, false }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Bot] TumbleRequest: " + ex.Message)); } } public static bool Jump(PlayerAvatar avatar) { return Jump(avatar, 1f); } public static bool Jump(PlayerAvatar avatar, float scale) { if (!_wasGrounded || _jumpCooldown > 0f || _tumbling) { return false; } _vertVel = 5f * Mathf.Clamp(scale, 0.3f, 1.5f); _jumpCooldown = 1.1f; _jumpGrace = 1.1f; _wasGrounded = false; try { if ((Object)(object)avatar != (Object)null) { avatar.Jump(false); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Bot] Jump: " + ex.Message)); } return true; } private static bool ConsiderHop(GameObject bot, PlayerAvatar avatar, Vector3 pos, Vector3 travelDir, int solid) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (!_wasGrounded || _jumpCooldown > 0f) { return false; } if (solid == 0) { return false; } Vector3 val = travelDir; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { return false; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = pos + val * 0.75f; if (!GroundProbe(bot, val2 + Vector3.up * 1.5f, 1.75f, solid, out var best)) { return false; } float num = ((RaycastHit)(ref best)).point.y - pos.y; if (num <= 0.55f || num > 1.25f) { return false; } if (((RaycastHit)(ref best)).normal.y <= 0.65f) { return false; } if (Physics.CheckSphere(((RaycastHit)(ref best)).point + Vector3.up * 0.5f, 0.315f, solid, (QueryTriggerInteraction)1)) { return false; } if (Jump(avatar)) { Plugin.Log.LogInfo((object)("[Bot] Hopping up " + num.ToString("F2") + "m.")); return true; } return false; } private static bool IsSteppable(GameObject bot, RaycastHit hit, Vector3 feet, int solid) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = ((RaycastHit)(ref hit)).point + Vector3.up * 0.6f; if (!GroundProbe(bot, val, 0.8f, solid, out var best)) { return false; } float num = ((RaycastHit)(ref best)).point.y - feet.y; return num > -0.05f && num <= 0.55f && ((RaycastHit)(ref best)).normal.y > 0.3f; } catch { return false; } } private static bool GroundProbe(GameObject bot, Vector3 from, float distance, int mask, out RaycastHit best) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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) best = default(RaycastHit); int num = Physics.RaycastNonAlloc(from, Vector3.down, _hitBuf, distance, mask, (QueryTriggerInteraction)1); if (num <= 0) { return false; } RaycastHit[] hitBuf = _hitBuf; bool result = false; float num2 = float.MaxValue; for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref hitBuf[i])).collider; if (!((Object)(object)collider == (Object)null) && !IsSelfOrCreature(collider, bot) && ((RaycastHit)(ref hitBuf[i])).distance < num2) { num2 = ((RaycastHit)(ref hitBuf[i])).distance; best = hitBuf[i]; result = true; } } return result; } private static bool IsSelfOrCreature(Collider c, GameObject bot) { try { Transform transform = ((Component)c).transform; if ((Object)(object)bot != (Object)null && ((Object)(object)transform == (Object)(object)bot.transform || transform.IsChildOf(bot.transform))) { return true; } if ((Object)(object)((Component)transform).GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)((Component)transform).GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)((Component)transform).GetComponentInParent() != (Object)null) { return true; } } catch { } return false; } private static bool SweepWall(GameObject bot, Vector3 from, Vector3 dir, float distance, int mask, out RaycastHit best) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) best = default(RaycastHit); int num = Physics.SphereCastNonAlloc(from, 0.35f, dir, _sweepBuf, distance, mask, (QueryTriggerInteraction)1); if (num <= 0) { return false; } RaycastHit[] sweepBuf = _sweepBuf; bool result = false; float num2 = float.MaxValue; for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref sweepBuf[i])).collider; if (!((Object)(object)collider == (Object)null) && !IsSelfOrCreature(collider, bot) && !(((RaycastHit)(ref sweepBuf[i])).distance <= 0.0001f) && ((RaycastHit)(ref sweepBuf[i])).distance < num2) { num2 = ((RaycastHit)(ref sweepBuf[i])).distance; best = sweepBuf[i]; result = true; } } return result; } private static void TryUnstickHop(GameObject bot, PlayerAvatar avatar) { if (_hasGoal && !_tumbling && _wasGrounded && !(Time.unscaledTime < _unstickHopAt) && !((Object)(object)BotHands.Carried != (Object)null)) { bool num = _blockedTimer > 0.9f; bool flag = _stuckTimer > 1f; if ((num || flag) && Jump(avatar, 0.6f)) { _unstickHopAt = Time.unscaledTime + 2.5f; _blockedTimer = 0f; _stuckTimer = 0f; Plugin.Log.LogInfo((object)"[Bot] Stuck — hopping to clear it."); } } } private static void ShoveDoor(PhysGrabObject door, Vector3 travelDir, Vector3 contact) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) try { Rigidbody rb = door.rb; if ((Object)(object)rb == (Object)null) { return; } int instanceID = ((Object)door).GetInstanceID(); if (_doorShoveAt.TryGetValue(instanceID, out var value) && Time.unscaledTime < value) { return; } _doorShoveAt[instanceID] = Time.unscaledTime + 0.25f; rb.WakeUp(); PhysGrabHinge val = ((Component)door).GetComponent() ?? ((Component)door).GetComponentInChildren(true); if ((Object)(object)val != (Object)null && Reflect.Get(val, "closed")) { HingeJoint val2 = Reflect.Get(val, "joint"); Transform hingePoint = val.hingePoint; if ((Object)(object)val2 != (Object)null) { Vector3 val3 = ((Component)door).transform.TransformDirection(((Joint)val2).axis); Vector3 normalized = ((Vector3)(ref val3)).normalized; Vector3 val4 = (((Object)(object)hingePoint != (Object)null) ? hingePoint.position : rb.worldCenterOfMass); float num = Mathf.Sign(Vector3.Dot(Vector3.Cross(contact - val4, travelDir), normalized)); if (num == 0f) { num = 1f; } rb.angularVelocity = normalized * num * 1.2f; return; } } rb.AddForceAtPosition(travelDir * 22f, contact, (ForceMode)5); } catch { } } private unsafe static void Apply(GameObject bot, PlayerAvatar avatar, Vector3 pos, float dt) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0827: Unknown result type (might be due to invalid IL or missing references) //IL_082e: Unknown result type (might be due to invalid IL or missing references) //IL_083a: Unknown result type (might be due to invalid IL or missing references) //IL_0841: Unknown result type (might be due to invalid IL or missing references) //IL_07cb: Unknown result type (might be due to invalid IL or missing references) //IL_07cd: Unknown result type (might be due to invalid IL or missing references) //IL_07cf: Unknown result type (might be due to invalid IL or missing references) //IL_07d4: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0868: Unknown result type (might be due to invalid IL or missing references) //IL_085a: Unknown result type (might be due to invalid IL or missing references) //IL_0861: Unknown result type (might be due to invalid IL or missing references) //IL_0866: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Unknown result type (might be due to invalid IL or missing references) //IL_08b1: Unknown result type (might be due to invalid IL or missing references) //IL_08b9: Unknown result type (might be due to invalid IL or missing references) //IL_08c0: Unknown result type (might be due to invalid IL or missing references) //IL_08c6: Unknown result type (might be due to invalid IL or missing references) //IL_0876: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_08f4: Unknown result type (might be due to invalid IL or missing references) //IL_0901: Unknown result type (might be due to invalid IL or missing references) //IL_08e7: Unknown result type (might be due to invalid IL or missing references) //IL_0885: Unknown result type (might be due to invalid IL or missing references) //IL_061b: Unknown result type (might be due to invalid IL or missing references) //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_0927: Unknown result type (might be due to invalid IL or missing references) //IL_092c: Unknown result type (might be due to invalid IL or missing references) //IL_0653: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_0941: Unknown result type (might be due to invalid IL or missing references) //IL_0953: Unknown result type (might be due to invalid IL or missing references) //IL_0965: Unknown result type (might be due to invalid IL or missing references) //IL_0976: Unknown result type (might be due to invalid IL or missing references) //IL_0987: Unknown result type (might be due to invalid IL or missing references) //IL_099c: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Unknown result type (might be due to invalid IL or missing references) //IL_0516: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_036a: 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_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_053b: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: 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_0696: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_06a1: Unknown result type (might be due to invalid IL or missing references) //IL_06a6: Unknown result type (might be due to invalid IL or missing references) //IL_06ab: Unknown result type (might be due to invalid IL or missing references) //IL_04ab: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0238: 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_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_05a9: Unknown result type (might be due to invalid IL or missing references) //IL_06df: Unknown result type (might be due to invalid IL or missing references) //IL_06e4: Unknown result type (might be due to invalid IL or missing references) //IL_079c: Unknown result type (might be due to invalid IL or missing references) //IL_079d: Unknown result type (might be due to invalid IL or missing references) float magnitude = ((Vector3)(ref _velocity)).magnitude; Quaternion val; if (magnitude > 0.25f) { val = Quaternion.LookRotation(((Vector3)(ref _velocity)).normalized); float y = ((Quaternion)(ref val)).eulerAngles.y; _yaw = Mathf.MoveTowardsAngle(_yaw, y, 540f * dt); } else if (_hasLookAt) { Vector3 val2 = Horizontal(_lookAt - pos); if (((Vector3)(ref val2)).sqrMagnitude > 0.04f) { val = Quaternion.LookRotation(((Vector3)(ref val2)).normalized); float y2 = ((Quaternion)(ref val)).eulerAngles.y; _yaw = Mathf.MoveTowardsAngle(_yaw, y2, 200f * dt); } } _hasLookAt = false; Quaternion val3 = Quaternion.Euler(0f, _yaw, 0f); Vector3 val4 = new Vector3(_velocity.x, 0f, _velocity.z) * dt; float magnitude2 = ((Vector3)(ref val4)).magnitude; Vector3 val5 = pos + val4; int num = SolidMask(); if (num != 0 && magnitude2 > 0.0001f) { Vector3 val6 = val4 / magnitude2; Vector3 val7 = pos + Vector3.up * 0.9f; if (SweepWall(bot, val7, val6, magnitude2 + 0.35f, num, out var best)) { PhysGrabObject val8 = (((Object)(object)((RaycastHit)(ref best)).collider != (Object)null) ? ((Component)((RaycastHit)(ref best)).collider).GetComponentInParent() : null); if ((Object)(object)val8 != (Object)null && (Object)(object)val8.rb != (Object)null && !val8.rb.isKinematic) { ShoveDoor(val8, val6, ((RaycastHit)(ref best)).point); val5 = pos + val4 * 0.35f; _blockedTimer += dt * 0.4f; } else if (IsSteppable(bot, best, pos, num)) { _blockedTimer = 0f; } else { Vector3 val9 = Vector3.ProjectOnPlane(val4, ((RaycastHit)(ref best)).normal); val9.y = 0f; if (((Vector3)(ref val9)).sqrMagnitude > 1E-06f && !SweepWall(bot, val7, ((Vector3)(ref val9)).normalized, ((Vector3)(ref val9)).magnitude + 0.35f, num, out var _)) { val5 = pos + val9; } else if (!ConsiderHop(bot, avatar, pos, val4, num)) { val5 = pos; } _blockedTimer += dt; } } else { _blockedTimer = 0f; } } if (DirectMode && num != 0 && magnitude2 > 0.0001f) { Vector3 val10 = default(Vector3); ((Vector3)(ref val10))..ctor(val5.x, pos.y, val5.z); if (!GroundProbe(bot, val10 + Vector3.up * 0.6f, 2.2f, num, out var _)) { val5.x = pos.x; val5.z = pos.z; _blockedTimer += dt; if (_hasGoal) { _hasGoal = false; } } } bool flag = false; Vector3 val12; if (num != 0 && GroundProbe(bot, val5 + Vector3.up * 0.6f, 12.6f, num, out var best4)) { float num2 = ((RaycastHit)(ref best4)).point.y - pos.y; if (num2 > 0.55f) { if (!ConsiderHop(bot, avatar, pos, val4, num)) { val5.x = pos.x; val5.z = pos.z; val5.y = pos.y; _blockedTimer += dt; } flag = true; _fallTimer = 0f; } else if (num2 > 0f) { bool flag2 = magnitude2 > 0.0005f; bool flag3 = ((RaycastHit)(ref best4)).normal.y > 0.3f; if (!flag3 && flag2) { Vector3 val11 = val5; val12 = new Vector3(val4.x, 0f, val4.z); Vector3 val13 = val11 + ((Vector3)(ref val12)).normalized * 0.28f; if (GroundProbe(bot, val13 + Vector3.up * 0.6f, 2.6f, num, out var best5) && ((RaycastHit)(ref best5)).normal.y > 0.3f && ((RaycastHit)(ref best5)).point.y - pos.y <= 0.55f) { best4 = best5; num2 = ((RaycastHit)(ref best5)).point.y - pos.y; flag3 = true; } } if (flag3 && flag2) { val5.y = pos.y + Mathf.Min(num2, 7f * dt); _vertVel = 0f; _blockedTimer = 0f; } else { val5.x = pos.x; val5.z = pos.z; val5.y = pos.y; _blockedTimer += dt; } } else { _vertVel -= 10f * dt; val5.y = Mathf.Max(((RaycastHit)(ref best4)).point.y, pos.y + _vertVel * dt); if (val5.y <= ((RaycastHit)(ref best4)).point.y + 0.001f) { _vertVel = 0f; } } flag = val5.y <= ((RaycastHit)(ref best4)).point.y + 0.08f && _vertVel <= 0.01f; _fallTimer = 0f; if (flag) { _airTime = 0f; _trackingFall = false; } else { _airTime += dt; if (!_trackingFall) { _trackingFall = true; _fallFromY = pos.y; } } } else if (DirectMode) { val5.y = pos.y; _vertVel = 0f; _fallTimer = 0f; _airTime = 0f; flag = true; } else { _vertVel -= 10f * dt; _vertVel = Mathf.Max(_vertVel, -30f); val5.y = pos.y + _vertVel * dt; _fallTimer += dt; _airTime += dt; if (!_trackingFall) { _trackingFall = true; _fallFromY = pos.y; } if (_fallTimer > 8f) { _fallTimer = 0f; if (Time.unscaledTime > _fallProbeAt) { _fallProbeAt = Time.unscaledTime + 10f; try { RaycastHit[] array = Physics.RaycastAll(pos + Vector3.up * 2f, Vector3.down, 30f, -1, (QueryTriggerInteraction)2); string text = ((array.Length == 0) ? "NOTHING on any layer" : ""); int num3 = 0; RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val14 = array2[i]; if (!((Object)(object)((RaycastHit)(ref val14)).collider == (Object)null) && num3 < 4) { text = text + ((num3 > 0) ? ", " : "") + ((Object)((RaycastHit)(ref val14)).collider).name + "(layer " + LayerMask.LayerToName(((Component)((RaycastHit)(ref val14)).collider).gameObject.layer) + (((RaycastHit)(ref val14)).collider.isTrigger ? ", trigger" : "") + ")"; num3++; } } ManualLogSource log = Plugin.Log; val12 = pos; log.LogWarning((object)("[Bot] Fall probe at " + ((object)(*(Vector3*)(&val12))/*cast due to .constrained prefix*/).ToString() + " — below him: " + text)); } catch { } } if (RescuePoint(out var spot)) { val5 = spot; _velocity = Vector3.zero; _vertVel = 0f; _corners = Array.Empty(); _repathTimer = 0f; _breadcrumbs.Clear(); _usingBreadcrumbs = false; Plugin.Log.LogWarning((object)"[Bot] Fell out of the world — recovered to safe ground."); } } } float num4 = 7.5f * dt + 0.05f; Vector3 val15 = default(Vector3); ((Vector3)(ref val15))..ctor(val5.x - pos.x, 0f, val5.z - pos.z); if (((Vector3)(ref val15)).magnitude > num4) { val15 = ((Vector3)(ref val15)).normalized * num4; } if (val5.y < -90f && pos.y >= -90f) { val5.y = pos.y; _vertVel = 0f; Plugin.Log.LogWarning((object)"[Bot] Clamped a fall at y=-90 (below -100 the game's own rescue path NREs on a bot)."); } Vector3 val16 = default(Vector3); ((Vector3)(ref val16))..ctor(pos.x + val15.x, val5.y, pos.z + val15.z); Rigidbody component = bot.GetComponent(); if ((Object)(object)component != (Object)null) { component.position = val16; } bot.transform.position = val16; bot.transform.rotation = val3; if (_blockedTimer > 1.2f) { _blockedTimer = 0f; _repathTimer = 0f; _velocity = Vector3.zero; } if (!((Object)(object)avatar == (Object)null)) { Reflect.Set(avatar, "clientPosition", val16); Reflect.Set(avatar, "clientPositionCurrent", val16); Reflect.Set(avatar, "clientRotation", val3); Reflect.Set(avatar, "clientRotationCurrent", val3); Reflect.Set(avatar, "rbVelocity", _velocity); Reflect.Set(avatar, "rbVelocityRaw", _velocity); Reflect.Set(avatar, "isMoving", magnitude > 0.35f); Reflect.Set(avatar, "isSprinting", magnitude > 4.05f); Reflect.Set(avatar, "isGrounded", flag); _wasGrounded = flag; Reflect.Set(avatar, "isCrawling", false); Reflect.Set(avatar, "isCrouching", BotDanger.WantsCrouch || ForceCrouch); } } } public static class BotNet { public const byte EventCode = 182; private const int Magic = 1112757809; private static bool _ready; private const int KindBuddy = 2; private const int KindHudCmd = 3; private const int KindStatus = 4; private const int KindDance = 5; public static void EnsureReady() { if (!_ready && PhotonNetwork.InRoom) { PhotonNetwork.NetworkingClient.EventReceived += OnEvent; _ready = true; Plugin.Log.LogInfo((object)"[Net] Voice-command channel ready (event 182)."); } } public static void SendVoiceCommand(string text, float peak, bool intercom = false) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown if (PhotonNetwork.InRoom && !string.IsNullOrEmpty(text)) { PhotonNetwork.RaiseEvent((byte)182, (object)new object[4] { 1112757809, text, peak, intercom }, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); } } public static void BroadcastBuddy(int actorNumber) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { PhotonNetwork.RaiseEvent((byte)182, (object)new object[3] { 1112757809, 2, actorNumber }, new RaiseEventOptions { Receivers = (ReceiverGroup)0 }, SendOptions.SendReliable); } } public static void SendHudCommand(string intentName) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown if (PhotonNetwork.InRoom && !string.IsNullOrEmpty(intentName)) { PhotonNetwork.RaiseEvent((byte)182, (object)new object[3] { 1112757809, 3, intentName }, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); } } private static PlayerAvatar AvatarOfActor(int actorNumber) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { PhotonView component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null && component.Owner != null && component.Owner.ActorNumber == actorNumber) { return player; } } } } catch { } return null; } public static void SendBotStatus(string status) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //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 if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { PhotonNetwork.RaiseEvent((byte)182, (object)new object[3] { 1112757809, 4, status }, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); } } public static void BroadcastDance(bool on, string track) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { PhotonNetwork.RaiseEvent((byte)182, (object)new object[4] { 1112757809, 5, on, track ?? "" }, new RaiseEventOptions { Receivers = (ReceiverGroup)0 }, SendOptions.SendReliable); } } private static void OnEvent(EventData e) { if (e.Code != 182 || !(e.CustomData is object[] array) || array.Length < 3 || !(array[0] is int num) || num != 1112757809) { return; } object obj = array[1]; if (obj is int) { switch ((int)obj) { case 2: if (array.Length > 2 && array[2] is int actorNumber) { BotBuddyHud.OnBuddyBroadcast(actorNumber); } break; case 5: { if (PhotonNetwork.IsMasterClient) { break; } bool flag = default(bool); int num2; if (array.Length > 2) { obj = array[2]; if (obj is bool) { flag = (bool)obj; num2 = 1; } else { num2 = 0; } } else { num2 = 0; } int num3 = num2 & (flag ? 1 : 0); string track = ((array.Length > 3) ? (array[3] as string) : null); BotDance.OnRemoteDance((byte)num3 != 0, track); break; } case 4: if (PhotonNetwork.IsMasterClient) { string text2 = ((array.Length > 2) ? (array[2] as string) : null); Plugin.Log.LogWarning((object)("[Net] CLIENT actor " + e.Sender + " reports: " + text2)); } break; case 3: if (PhotonNetwork.IsMasterClient) { string text = ((array.Length > 2) ? (array[2] as string) : null); if (!string.IsNullOrEmpty(text)) { BotCommands.Run(text, AvatarOfActor(e.Sender)); } } break; } return; } string text3 = array[1] as string; float peak = ((array[2] is float num4) ? num4 : 0f); bool flag2 = default(bool); int num5; if (array.Length > 3) { obj = array[3]; if (obj is bool) { flag2 = (bool)obj; num5 = 1; } else { num5 = 0; } } else { num5 = 0; } bool intercom = (byte)((uint)num5 & (flag2 ? 1u : 0u)) != 0; BotEars.OnRemoteVoiceCommand(e.Sender, text3, peak, intercom); } } [HarmonyPatch] public static class BotNetTransform { [HarmonyPatch(typeof(PlayerAvatar), "OnPhotonSerializeView")] [HarmonyPrefix] public static bool SerializePrefix(PlayerAvatar __instance, PhotonStream stream, PhotonMessageInfo info) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null || !BotSpawner.IsBot(__instance)) { return true; } if (!stream.IsWriting) { return true; } if (!SemiFunc.MasterAndOwnerOnlyRPC(info, __instance.photonView)) { return false; } Transform transform = ((Component)__instance).transform; stream.SendNext((object)Reflect.Get(__instance, "isCrouching")); stream.SendNext((object)Reflect.Get(__instance, "isSprinting")); stream.SendNext((object)Reflect.Get(__instance, "isCrawling")); stream.SendNext((object)Reflect.Get(__instance, "isSliding")); stream.SendNext((object)Reflect.Get(__instance, "isMoving")); stream.SendNext((object)Reflect.Get(__instance, "isGrounded")); stream.SendNext((object)Reflect.Get(__instance, "Interact")); stream.SendNext((object)Reflect.Get(__instance, "InputDirectionRaw")); stream.SendNext((object)Reflect.Get(__instance, "rbVelocity")); stream.SendNext((object)Reflect.Get(__instance, "rbVelocityRaw")); stream.SendNext((object)transform.position); stream.SendNext((object)transform.rotation); stream.SendNext((object)Reflect.Get(__instance, "rotationDisabled")); stream.SendNext((object)Reflect.Get(__instance, "rotationOverrideActive")); stream.SendNext((object)false); stream.SendNext((object)0); stream.SendNext((object)Vector3.zero); stream.SendNext((object)false); stream.SendNext((object)0); return false; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[NetT] Serialize for the bot failed, falling back: " + ex.Message)); return true; } } } public static class BotNoise { private const float MusicRange = 22f; private const float PulseEvery = 1.5f; private const float PanicGrace = 2.6f; private static float _nextPulse; private static float _playingFor; private static bool _saidIt; private static float _killMusicAt; public static void Reset() { _nextPulse = 0f; _playingFor = 0f; _saidIt = false; _killMusicAt = 0f; } public static void Tick(Vector3 botPos, float dt) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) try { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (!BotJukebox.Playing) { if (_playingFor > 0f) { Reset(); } return; } _playingFor += dt; if (SemiFunc.RunIsLevel() && Time.unscaledTime >= _nextPulse) { _nextPulse = Time.unscaledTime + 1.5f; SemiFunc.EnemyInvestigate(botPos, 22f, false); } if (SemiFunc.RunIsLevel() && !(_playingFor < 3f)) { if (BotDanger.Level == BotDanger.Threat.Clear) { _saidIt = false; _killMusicAt = 0f; } else if (!_saidIt) { _saidIt = true; _killMusicAt = Time.unscaledTime + 2.6f; BotPersona.SpeakExact(BotPersona.MusicDrewSomething, botPos); Plugin.Log.LogInfo((object)"[Noise] The music worked. Something is coming."); } else if (_killMusicAt > 0f && Time.unscaledTime >= _killMusicAt) { _killMusicAt = 0f; Plugin.Log.LogInfo((object)"[Noise] Cutting the music — he has company."); BotDance.Stop(announce: false); BotPersona.SpeakExact(BotPersona.MusicPanicOff, botPos); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Noise] " + ex.Message)); } } public static void Make(Vector3 at, float range) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) try { if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLevel()) { SemiFunc.EnemyInvestigate(at, range, false); } } catch { } } } public static class BotOpinions { private sealed class Flight { public string Name; public float At; public bool Reported; } private static readonly Dictionary Favour = new Dictionary(); private static readonly List Cowards = new List(); private static bool _wasInDanger; private static readonly Dictionary _dangerStartPos = new Dictionary(); private static float _dangerStartedAt; private static float _nextIdleCheck; public static void Reset() { Favour.Clear(); Cowards.Clear(); _dangerStartPos.Clear(); _wasInDanger = false; } public static void Thank(PlayerAvatar p, float amount, string why) { if (!((Object)(object)p == (Object)null) && !BotSpawner.IsBot(p)) { int instanceID = ((Object)p).GetInstanceID(); Favour.TryGetValue(instanceID, out var value); Favour[instanceID] = Mathf.Min(10f, value + amount); Plugin.Log.LogInfo((object)("[Favour] " + SemiFunc.PlayerGetName(p) + " " + why + " (+" + amount.ToString("F1") + ", now " + Favour[instanceID].ToString("F1") + ").")); } } public static float FavourOf(PlayerAvatar p) { if ((Object)(object)p == (Object)null) { return 0f; } if (!Favour.TryGetValue(((Object)p).GetInstanceID(), out var value)) { return 0f; } return value; } public static PlayerAvatar Favourite() { PlayerAvatar result = null; float num = 2f; try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { float num2 = FavourOf(player); if (num2 > num) { num = num2; result = player; } } } } catch { } return result; } public static void TickAwareness(Vector3 botPos, float dt) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) bool flag = BotDanger.Level != BotDanger.Threat.Clear; if (flag && !_wasInDanger) { _dangerStartPos.Clear(); _dangerStartedAt = Time.unscaledTime; foreach (PlayerAvatar item in Nearby(botPos, 14f)) { _dangerStartPos[((Object)item).GetInstanceID()] = ((Component)item).transform.position; } } if (!flag && _wasInDanger) { if (Time.unscaledTime - _dangerStartedAt > 2f) { foreach (KeyValuePair dangerStartPo in _dangerStartPos) { PlayerAvatar val = ById(dangerStartPo.Key); if (!((Object)(object)val == (Object)null)) { float num = Vector3.Distance(((Component)val).transform.position, dangerStartPo.Value); if (num > 14f) { Cowards.Add(new Flight { Name = SemiFunc.PlayerGetName(val), At = Time.unscaledTime }); Plugin.Log.LogInfo((object)("[Opinion] " + SemiFunc.PlayerGetName(val) + " covered " + num.ToString("F0") + "m while that was happening.")); } } } } _dangerStartPos.Clear(); } _wasInDanger = flag; if (flag || Cowards.Count <= 0 || !(Time.unscaledTime > _nextIdleCheck)) { return; } _nextIdleCheck = Time.unscaledTime + 6f; for (int num2 = Cowards.Count - 1; num2 >= 0; num2--) { Flight flight = Cowards[num2]; if (Time.unscaledTime - flight.At > 90f) { Cowards.RemoveAt(num2); } else if (!flight.Reported) { flight.Reported = true; BotPersona.Speak("coward", BotPersona.Cowardice, 1f, 45f, botPos, flight.Name); break; } } } public static void OnPickedUp(PhysGrabObject obj, Vector3 botPos) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) try { ValuableObject component = ((Component)obj).GetComponent(); if (!((Object)(object)component == (Object)null)) { int num = Mathf.RoundToInt(Reflect.Get(component, "dollarValueCurrent")); if (num >= 2000) { BotPersona.Speak("richloot", BotPersona.BigMoney, 0.8f, 90f, botPos); } else if (num > 0 && num < 150 && BestValueNear(botPos, 12f) > num * 6) { BotPersona.Speak("cheaploot", BotPersona.CheapChoice, 0.7f, 90f, botPos); } } } catch { } } private static int BestValueNear(Vector3 pos, float range) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) int num = 0; try { ValuableObject[] array = Object.FindObjectsOfType(); foreach (ValuableObject val in array) { if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(pos, ((Component)val).transform.position) > range)) { int num2 = Mathf.RoundToInt(Reflect.Get(val, "dollarValueCurrent")); if (num2 > num) { num = num2; } } } } catch { } return num; } public static void Eulogise(PlayerAvatar dead, Vector3 botPos) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)dead == (Object)null)) { string aboutName = SemiFunc.PlayerGetName(dead); bool flag = FavourOf(dead) > 3f; BotPersona.Speak(flag ? "eulogykind" : "eulogy", flag ? BotPersona.EulogyKind : BotPersona.Eulogy, 1f, 20f, botPos, aboutName); } } private static IEnumerable Nearby(Vector3 pos, float range) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null) { yield break; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "isDisabled") && !(Vector3.Distance(pos, ((Component)player).transform.position) > range)) { yield return player; } } } private static PlayerAvatar ById(int id) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null && ((Object)player).GetInstanceID() == id) { return player; } } } catch { } return null; } } public static class BotOrders { public unsafe static void GoThere(PlayerAvatar who, Vector3 botPos) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)who == (Object)null || !BotPointing.FloorSpot(who, out var spot)) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotSpawner.HaltEverything(botPos, announce: false); BotMovement.SetDestination(spot); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.OnMyWay, botPos); ManualLogSource log = Plugin.Log; string[] obj = new string[5] { "[Order] Going to ", null, null, null, null }; Vector3 val = spot; obj[1] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[2] = " (pointed at by "; obj[3] = SemiFunc.PlayerGetName(who); obj[4] = ")."; log.LogInfo((object)string.Concat(obj)); } public static void GrabThat(PlayerAvatar who, Vector3 botPos) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject val = BotPointing.Object(who); if ((Object)(object)val == (Object)null) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } if (BotHands.IsCart(val)) { BotHands.FetchCart(who, botPos); return; } string forName = (((Object)(object)who != (Object)null) ? SemiFunc.PlayerGetName(who) : null); if (!BotHands.DeliverTo(val, BotHands.NearestHuman(botPos, out var pos) ? pos : botPos, forName, 60f)) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotPersona.SpeakExact(BotPersona.OnIt, botPos); Plugin.Log.LogInfo((object)("[Order] Fetching '" + ((Object)((Component)val).gameObject).name + "'.")); } public static void LeaveThat(PlayerAvatar who, Vector3 botPos) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject val = BotPointing.Object(who) ?? BotHands.Carried; if ((Object)(object)val == (Object)null) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotHands.Avoid(val, 120f); if (BotHands.IsCarrying(val)) { BotHands.DropEverything(); } BotPersona.SpeakExact(BotPersona.Fine, botPos); Plugin.Log.LogInfo((object)("[Order] Leaving '" + ((Object)((Component)val).gameObject).name + "' alone.")); } public static void FollowThem(PlayerAvatar who, Vector3 botPos) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar val = BotPointing.Person(who); if ((Object)(object)val == (Object)null) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotBuddy.Assign(val, botPos); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.NewBuddy, botPos, SemiFunc.PlayerGetName(val)); } public static void GoToTruck(Vector3 botPos) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!TruckSpot(out var spot)) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotSpawner.HaltEverything(botPos, announce: false); BotMovement.SetDestination(spot); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.OnMyWay, botPos); } public static void GoToExtraction(Vector3 botPos) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) try { ExtractionPoint val = null; if ((Object)(object)RoundDirector.instance != (Object)null) { val = Reflect.Get(RoundDirector.instance, "extractionPointCurrent"); } if ((Object)(object)val == (Object)null) { float num = float.MaxValue; ExtractionPoint[] array = Object.FindObjectsOfType(); foreach (ExtractionPoint val2 in array) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.Distance(botPos, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } } if ((Object)(object)val == (Object)null) { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); return; } BotSpawner.HaltEverything(botPos, announce: false); BotMovement.SetDestination(BotChute.StandingSpot(val)); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.OnMyWay, botPos); } catch { BotPersona.SpeakExact(BotPersona.PointConfused, botPos); } } private static bool TruckSpot(out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { spot = ((Component)TruckSafetySpawnPoint.instance).transform.position; return true; } } catch { } return false; } public static void Crouch(bool down, Vector3 botPos) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) BotMovement.ForceCrouch = down; BotPersona.SpeakExact(BotPersona.Fine, botPos); } public static void Jump(Vector3 botPos) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) BotMovement.Jump(BotSpawner.BotAvatar); BotPersona.SpeakExact(BotPersona.Fine, botPos); } public static void LookAt(PlayerAvatar who, Vector3 botPos) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)who == (Object)null)) { BotMovement.Stop(); BotMovement.FaceTowards(((Component)who).transform.position); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.Fine, botPos); } } public static void BackUp(PlayerAvatar who, Vector3 botPos) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (((Object)(object)who != (Object)null) ? (botPos - ((Component)who).transform.position) : (-Vector3.forward)); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { val = Random.insideUnitSphere; } val.y = 0f; BotMovement.SetDestination(botPos + ((Vector3)(ref val)).normalized * 2.5f); BotIdle.Poke(); BotPersona.SpeakExact(BotPersona.Fine, botPos); } public static void ReportHaul(Vector3 botPos) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (!BotFacts.HaveMoneyFacts) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } else if (!BotPersona.SpeakLiteral((BotFacts.Shortfall > 0) ? ("We're on " + BotFacts.Haul + ". Need " + BotFacts.Goal + ". That's " + BotFacts.Shortfall + " short.") : ("We're on " + BotFacts.Haul + ", against a quota of " + BotFacts.Goal + ". We're clear."), botPos)) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } } public static void ReportExtractions(Vector3 botPos) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(0, BotFacts.ExtractionsTotal - BotFacts.ExtractionsDone); string text = ((BotFacts.ExtractionsTotal <= 0) ? null : ((num == 0) ? "That's all of them done." : (num + " extraction" + ((num == 1) ? "" : "s") + " to go."))); if (text == null || !BotPersona.SpeakLiteral(text, botPos)) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } } public static void ReportAlive(Vector3 botPos) { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GameDirector.instance == (Object)null) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); return; } int num = 0; int num2 = 0; string text = null; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } if (Reflect.Get(player, "isDisabled")) { num2++; continue; } num++; if (text == null) { text = SemiFunc.PlayerGetName(player); } } if (!BotPersona.SpeakLiteral((num == 0) ? "Just me. That's not ideal." : ((num2 == 0) ? "Everyone's up." : ((num == 1) ? (text + "'s the last one standing.") : (num + " up, " + num2 + " down."))), botPos)) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } } catch { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } } public static void ReportPosition(Vector3 botPos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) try { if (BotHands.NearestHuman(botPos, out var pos)) { float num = Vector3.Distance(botPos, pos); if (BotPersona.SpeakLiteral((num < 4f) ? "Right behind you." : ((num < 12f) ? ("Close. About " + Mathf.RoundToInt(num) + " metres.") : (Mathf.RoundToInt(num) + " metres out. Coming back if you want me.")), botPos)) { return; } } BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } catch { } } public static void ReportSafety(Vector3 botPos) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!BotPersona.SpeakLiteral((BotDanger.Level == BotDanger.Threat.Clear) ? "Nothing near me." : ((BotDanger.Level == BotDanger.Threat.Wary) ? "Something's about. I'd move." : "No. Very much no."), botPos)) { BotPersona.SpeakExact(BotPersona.DontKnow, botPos); } } } public static class BotPersona { private const float SightRange = 18f; private static readonly Dictionary LineNext = new Dictionary(); private static readonly Dictionary EventNext = new Dictionary(); private static readonly HashSet EventSeen = new HashSet(); private static float _globalNext; private const float GlobalGap = 11f; private const float GlobalGapChatty = 9f; private const float LineGap = 300f; private const float LineGapChatty = 150f; private static readonly string[] BuddyPickedS = new string[15] { "{name}. You'll do.", "Right, {name}, I'm with you.", "{name}. Try to stay alive.", "Sticking with {name}. Congratulations.", "I've decided. {name}.", "{name}, you'll do.", "Right, {name}. You're my problem now.", "Sticking with {name}. Try to survive.", "{name} it is.", "I've chosen {name}. Don't let it go to your head.", "{name}, congratulations. Or condolences.", "Following {name}. Statistically the least awful option.", "You. {name}. Walk slower.", "{name}, I'm your shadow now.", "Picked {name}. Mostly at random. Mostly." }; private static readonly string[] BuddyPickedN = new string[15] { "{name}. You'll do, I suppose.", "Right, {name}, I'm with you. Don't screw it up.", "{name}. Try to stay alive this time.", "Sticking with {name}. Lucky sod.", "I've decided. {name}. No takebacks.", "{name}, you'll do. Barely.", "Right, {name}. You're my problem now.", "Sticking with {name}. Try not to die immediately.", "{name} it is, then.", "I've chosen {name}. Don't get cocky.", "{name}, congratulations. Or condolences, hard to say.", "Following {name}. Least awful of a bad lot.", "You. {name}. Walk slower, you lunatic.", "{name}, I'm your shadow now. Deal with it.", "Picked {name}. Mostly at random, if I'm honest." }; private static readonly string[] NoOneElseS = new string[3] { "There isn't anyone else.", "You're all I've got. Grim.", "Who exactly would I pick?" }; private static readonly string[] NoOneElseN = new string[3] { "There isn't anyone else, is there.", "You're all I've got. Bloody grim.", "Who the hell else would I pick?" }; private static readonly string[] ScoutGoingS = new string[12] { "Fine. Going for a look.", "Scouting. Don't die while I'm gone.", "On it. Back shortly.", "I'll go. Obviously I'll go.", "Off for a look.", "Right, reconnaissance.", "Back shortly. Probably.", "I'll go. Obviously I'll go.", "Someone has to.", "Scouting. Don't wait up.", "If I don't come back, I was excellent.", "Going quiet. Try to do the same." }; private static readonly string[] ScoutGoingN = new string[12] { "Fine. Going for a bloody look.", "Scouting. Try not to die while I'm gone.", "On it. Back shortly. Probably.", "I'll go. Course I'll go. Always me.", "Off for a look, then.", "Right. Reconnaissance. Wonderful.", "Back shortly. Probably.", "I'll go. Course I'll bloody go.", "Someone has to, and it's never you.", "Scouting. Don't wait up.", "If I don't come back, I was magnificent.", "Going quiet. Try it sometime." }; private static readonly string[] ScoutEnemyS = new string[13] { "Heads up — {name} down here.", "Found a {name}. Not going closer.", "{name}. Definitely a {name}.", "Yeah, there's a {name}. You deal with it.", "Bad news. {name}.", "{name}. Out here. Now.", "Found a {name}. Not thrilled about it.", "There's a {name} down here.", "{name} spotted. I'm leaving.", "Heads up: {name}.", "Well. There's a {name}.", "{name}. I repeat, {name}.", "Do we have a plan for a {name}? No? Great." }; private static readonly string[] ScoutEnemyN = new string[13] { "Heads up — bloody {name} down here.", "Found a {name}. Not going any closer, thanks.", "{name}. Definitely a {name}. Sod that.", "Yeah, there's a {name}. You deal with it.", "Bad news. {name}. Very bad news.", "{name}. Out here. Right now.", "Found a bloody {name}.", "There's a {name} down here.", "{name} spotted. I'm off.", "Heads up, you lot: {name}.", "Well. There's a {name}. Brilliant.", "{name}. I repeat, a sodding {name}.", "Anyone got a plan for a {name}? Thought not." }; private static readonly string[] ScoutPlayerS = new string[3] { "{name}'s over here. Still breathing.", "Found {name}. Doing nothing, as usual.", "{name} is alive. Somehow." }; private static readonly string[] ScoutPlayerN = new string[3] { "{name}'s over here. Still breathing, somehow.", "Found {name}. Doing sod all, as usual.", "{name}'s alive. Don't ask me how." }; private static readonly string[] ScoutBodyS = new string[3] { "Found {name}. What's left of them.", "{name}'s down over here.", "Bad news about {name}." }; private static readonly string[] ScoutBodyN = new string[3] { "Found {name}. What's left of the poor sod.", "{name}'s down over here. Very down.", "Bad news about {name}. Very bad news." }; private static readonly string[] ScoutClearS = new string[11] { "Nothing out there. You're welcome.", "All clear. Went all that way for nothing.", "Empty. Suspiciously empty.", "Nothing out there.", "All quiet. Suspiciously quiet.", "Clear. Enjoy it while it lasts.", "Empty. I checked properly.", "Nothing worth reporting.", "Not a thing. You're welcome.", "Clear that way.", "Had a look. Boring." }; private static readonly string[] ScoutClearN = new string[11] { "Nothing out there. You're welcome.", "All clear. Went all that bloody way for nothing.", "Empty. Suspiciously empty, if you ask me.", "Sod all out there.", "All quiet. Suspiciously bloody quiet.", "Clear. Enjoy it while it lasts.", "Empty. And I checked properly, unlike some.", "Nothing worth reporting.", "Not a thing. You're welcome.", "Clear that way.", "Had a look. Dead boring." }; private static readonly string[] ScoutDoneS = new string[9] { "That's my report. Do what you like with it.", "Right, that's what's out there.", "I've seen enough. Coming back.", "That's my report.", "That's everything I saw.", "Do with that what you will.", "Report over. Someone write it down.", "That's the lot.", "I've done my bit." }; private static readonly string[] ScoutDoneN = new string[9] { "That's my report. Do what you like with it.", "Right, that's what's out there. Good luck.", "I've seen enough of that. Coming back.", "That's my report, such as it is.", "That's everything I saw.", "Do what you like with that.", "Report over. Someone take notes.", "That's the lot.", "I've done my bit. Your turn." }; private static readonly string[] AcknowledgeS = new string[24] { "Fine.", "Waiting.", "Standing here then.", "As you wish.", "On it.", "Fine.", "Yep.", "Doing it.", "Consider it done.", "Right you are.", "Sure. Why not.", "If I must.", "Already moving.", "Say no more.", "Working on it.", "Yeah, alright.", "That's me, then.", "Understood, obviously.", "Heard you the first time.", "Going.", "Yes. Fine. Going.", "Whatever you say.", "As you wish, your majesty.", "I live to serve. Apparently." }; private static readonly string[] AcknowledgeN = new string[24] { "Fine, fine.", "Waiting. Thrilling.", "Standing here then. Great.", "As you wish, your majesty.", "On it.", "Fine. Fine.", "Yep.", "Doing it, calm down.", "Consider it bloody done.", "Right you are, boss.", "Sure. Why the hell not.", "If I absolutely must.", "Already moving, keep your hair on.", "Say no more.", "Working on it, Christ.", "Yeah, alright, alright.", "That's me then, is it.", "Understood, obviously.", "Heard you the first sodding time.", "Going. Going.", "Yes. Fine. GOING.", "Whatever you say, your highness.", "I live to serve. Sadly.", "Do I get a say? No? Lovely." }; private static readonly string[] ConfusedS = new string[20] { "What?", "Say that again, properly.", "I have no idea what you want.", "Use smaller words.", "Sure. Whatever that meant.", "Say that again?", "You've lost me.", "That was words, but not a sentence.", "Try that one more time, slower.", "I got about half of that.", "Was that for me?", "Sorry, the acoustics in here are terrible. And so is your diction.", "Nope. Nothing.", "I heard a noise. I assume it meant something.", "That's not one of the things I know how to do.", "Again, but like you mean it.", "Could you rephrase that in a language I own?", "I'm a bot, not a psychic.", "Run that past me once more.", "Nope, still nothing." }; private static readonly string[] ConfusedN = new string[20] { "What?", "Say that again, properly this time.", "I have absolutely no idea what you want.", "Use smaller words, eh.", "Sure. Whatever the hell that meant.", "Say that again?", "You've completely lost me.", "That was words. Not a sentence.", "Try again, slower, like I'm thick.", "I got maybe half of that.", "Was that even for me?", "Sorry, your diction is atrocious.", "Nope. Sod all.", "I heard a noise. I'm assuming it meant something.", "That's not a thing I know how to do.", "Again, but properly this time.", "Rephrase that in English, ideally.", "I'm a bot, not a bloody mind reader.", "Run that past me one more time.", "Still nothing. Marvellous." }; private static readonly string[] PraisedS = new string[19] { "I know.", "Obviously.", "Put it in writing.", "Finally, some recognition.", "You're welcome, {name}.", "Don't mention it. Actually, do.", "That's twice now. I'm counting.", "Well. That's nice of you.", "Careful, I'll start expecting it.", "Anytime, {name}.", "I know.", "Say it again, louder.", "Finally, some recognition.", "Correct.", "I'll take it.", "Obviously.", "That's the nicest thing anyone's said to me all run.", "Keep going.", "Noted and treasured." }; private static readonly string[] PraisedN = new string[19] { "I know.", "Obviously I am.", "Put it in writing, I'll frame it.", "Finally, some bloody recognition.", "You're welcome, {name}.", "Don't mention it. Actually, do. Often.", "That's twice now. I'm counting.", "Well. That's nice of you, for once.", "Careful, I'll start expecting it.", "Anytime, {name}.", "I know.", "Say it again. Louder.", "Finally. Some bloody recognition.", "Correct.", "I'll take it.", "Obviously.", "Nicest thing said to me all run, that.", "Go on, keep going.", "Noted. Treasured. Probably weaponised later." }; private static readonly string[] ToldOffS = new string[4] { "Rude.", "Fine. Silent treatment it is.", "I'll remember that.", "Noted." }; private static readonly string[] ToldOffN = new string[4] { "Rude as hell.", "Fine. Silent treatment it is, then.", "I'll bloody remember that.", "Noted, arsehole." }; private static readonly string[] BuddyWhoS = new string[3] { "{name}. For my sins.", "I'm with {name}.", "{name}. Don't ask me why." }; private static readonly string[] BuddyWhoN = new string[3] { "{name}. For my sins.", "I'm with {name}, poor bastard.", "{name}. Don't ask me why." }; private static readonly string[] WhoAmIS = new string[3] { "Bertram.", "Bertram. Obviously.", "It's on the tag." }; private static readonly string[] WhoAmIN = new string[3] { "Bertram.", "Bertram. Obviously.", "It's on the bloody tag, mate." }; private static readonly string[] DenialS = new string[6] { "Rude.", "Are YOU?", "I'm as real as you are.", "That's a weird thing to ask.", "Beep boop. Happy now?", "Define real." }; private static readonly string[] DenialN = new string[6] { "Rude as hell.", "Are YOU?", "I'm as real as you are, mate.", "That's a bloody weird thing to ask.", "Beep boop. Happy now?", "Define real, genius." }; private static readonly string[] HowAmIS = new string[4] { "Underpaid.", "Still here. Somehow.", "Better than {name}, by the look of it.", "Fine. Nobody ever asks." }; private static readonly string[] HowAmIN = new string[4] { "Underpaid and unappreciated.", "Still here. Somehow.", "Better than {name}, by the look of it.", "Fine. Nobody ever bloody asks." }; private static readonly string[] InsultS = new string[15] { "What do you want, {name}?", "Say my name again and see what happens.", "{name}. Of course it's {name}.", "I'm busy.", "Do you need something or are you just lonely?", "That's my name, well done.", "{name}, I liked you better when you were quiet.", "Yes? Make it quick.", "You again.", "I don't work for you. Technically.", "Very good, {name}. You can read a name tag.", "What.", "Was there a question in there?", "{name}, I'm going to pretend I didn't hear that.", "Take a number." }; private static readonly string[] InsultN = new string[15] { "What do you want, {name}?", "Say my name again and see what bloody happens.", "{name}. Of course it's {name}.", "I'm busy. Sod off.", "Do you need something or are you just lonely?", "That's my name, well done, genius.", "{name}, I liked you better when you were quiet.", "Yes? Make it quick.", "Oh good, you again.", "I don't work for you. Technically.", "Very good, {name}. You can read a name tag.", "What.", "Was there a bloody question in there?", "{name}, I'm going to pretend I didn't hear that.", "Take a number, arsehole." }; private static readonly string[] EmergingS = new string[6] { "Took you long enough.", "I've been standing here for ages.", "Finally. Let's get on with it.", "There you are.", "I got here first, obviously.", "Right. Where's the loot?" }; private static readonly string[] EmergingN = new string[6] { "Took you bloody long enough.", "I've been standing here for ages, thanks for asking.", "Finally. Let's get on with it.", "There you are. At last.", "I got here first, obviously.", "Right. Where's the sodding loot?" }; private static readonly string[] GreetingS = new string[10] { "Hello, {name}.", "Hey.", "Alright, {name}.", "You're still here. Good.", "Hello. What do you want?", "{name}. Been a while.", "Hi. I was busy, but go on.", "Evening.", "Hello to you too, I suppose.", "There you are." }; private static readonly string[] GreetingN = new string[10] { "Hello, {name}.", "Hey. What.", "Alright, {name}.", "You're still here. Good, I suppose.", "Hello. What the hell do you want?", "{name}. Been a while.", "Hi. I was busy, but go on then.", "Evening.", "Hello to you too, I suppose.", "Oh, there you are." }; private static readonly string[] UnhushedS = new string[6] { "Finally.", "Right. As I was saying.", "That was thirty seconds of my life.", "I had so much to say.", "Was that necessary?", "Good. I hate that." }; private static readonly string[] UnhushedN = new string[6] { "Finally.", "Right. As I was bloody saying.", "That was thirty seconds of my life I won't get back.", "I had so much to say, you know.", "Was that really necessary?", "Good. I hate that." }; private static readonly string[] LobbyRareS = new string[14] { "I had a dream I was a toaster. It was fine.", "My warranty expired during the last run. Nobody told me.", "There's a screw loose in my ankle. Been there since Tuesday.", "I've been thinking about the truck. Where does it GO when we're in a level?", "I read the employee handbook. It's mostly about not suing.", "Someone left a sandwich in the cart three runs ago. I've said nothing.", "I don't sleep. I just stand in the dark and wait for you lot.", "If I had a mouth I'd probably complain more.", "Do you ever wonder who's paying for all this?", "I've been practising my walk. Nobody's noticed.", "The taxman knows my name. I don't know how.", "I keep a list. Of who drops things. It's a long list.", "One day I'll extract and just... keep going.", "Statistically, one of you will not come back. I have a favourite." }; private static readonly string[] LobbyRareN = new string[14] { "I had a dream I was a toaster. It was fine, honestly.", "My warranty expired during the last run. Nobody bloody told me.", "There's a screw loose in my ankle. Been there since Tuesday.", "I've been thinking about the truck. Where the hell does it GO?", "I read the employee handbook. It's mostly about not suing.", "Someone left a sandwich in the cart three runs ago. I've said nothing.", "I don't sleep. I just stand in the dark waiting for you lot.", "If I had a mouth I'd complain a damn sight more.", "Do you ever wonder who's actually paying for all this?", "I've been practising my walk. Nobody's bloody noticed.", "The taxman knows my name. I don't know how.", "I keep a list. Of who drops things. It's a long bloody list.", "One day I'll extract and just... keep going.", "Statistically one of you isn't coming back. I have a favourite." }; private static readonly string[] LobbyAloneS = new string[5] { "Just us then, {name}.", "No one else coming? Cosy.", "Two of us. That's not a crew, that's a rumour.", "{name}, if you die out there I'm keeping the cart.", "I'd make small talk but you're all I've got." }; private static readonly string[] LobbyAloneN = new string[5] { "Just us then, {name}. Marvellous.", "No one else coming? Bloody cosy.", "Two of us. That's not a crew, that's a rumour.", "{name}, if you die out there I'm keeping the cart.", "I'd make small talk but you're all I've bloody got." }; private static readonly string[] LobbyCrowdS = new string[5] { "Look at all these experts.", "Big crew. More people to blame.", "Everyone here? Good. Statistically that's worse for me.", "I'll learn your names if you survive the first level.", "One of you is going to be a problem. I can feel it." }; private static readonly string[] LobbyCrowdN = new string[5] { "Look at all these bloody experts.", "Big crew. More people to blame, I suppose.", "Everyone here? Good. Statistically that's worse for me.", "I'll learn your names if you survive the first level.", "One of you is going to be a problem. I can feel it." }; private static readonly string[] LobbyImpatientS = new string[6] { "We have been standing here for a WHILE.", "Is someone reading the settings? Out loud? Slowly?", "I've aged. I can feel it.", "The loot isn't going anywhere. Neither, apparently, are we.", "At this point I'd settle for a bad plan.", "Press. The. Button." }; private static readonly string[] LobbyImpatientN = new string[6] { "We have been standing here for a bloody WHILE.", "Is someone reading the settings out loud? Slowly?", "I've aged. I can feel it.", "The loot isn't going anywhere. Neither, apparently, are we.", "At this point I'd settle for a terrible plan.", "Press. The. Damn. Button." }; private static readonly string[] LobbyOminousS = new string[5] { "Do you ever count us? Before we go in. And after.", "I saw one of me once. Didn't wave back.", "If I ever say something twice, don't answer the second one.", "There was a Bertram before me. Nobody talks about it.", "You'd know if it wasn't me. Wouldn't you." }; private static readonly string[] LobbyOminousN = new string[5] { "Do you ever count us? Before we go in. And after.", "I saw one of me once. Didn't wave back.", "If I ever say something twice, don't answer the second one.", "There was a Bertram before me. Nobody bloody talks about it.", "You'd know if it wasn't me. Wouldn't you." }; private static readonly string[] ShushS = new string[10] { "Shh! Are you insane?", "Not NOW.", "Quiet. It's right there.", "{name}. Shut. Up.", "Do you WANT to be eaten?", "Later. Assuming there is one.", "I heard you. Everything heard you.", "Whisper. Please.", "Keep talking, see what turns up.", "That's how people die, {name}." }; private static readonly string[] ShushN = new string[10] { "Shh! Are you bloody insane?", "Not NOW.", "Quiet. It's right bloody there.", "{name}. Shut. Up.", "Do you WANT to be eaten?", "Later. Assuming there is a later.", "I heard you. So did everything else.", "Whisper, you lunatic.", "Keep talking, see what turns up.", "That's how people die, {name}." }; private static readonly string[] NotMyBossS = new string[4] { "I'm with {name}. Take it up with them.", "You're not my crewmate.", "{name} gives the orders. Sorry.", "Ask {name} nicely and maybe." }; private static readonly string[] NotMyBossN = new string[4] { "I'm with {name}. Take it up with them.", "You're not my bloody crewmate.", "{name} gives the orders. Sorry, not sorry.", "Ask {name} nicely and maybe I'll think about it." }; private static readonly string[] HadEnoughS = new string[6] { "Know what? Pick up your own stuff. I'm going to go find someone else... maybe heal THEM.", "Right. I'm done. Carry your own loot.", "Fine. Good luck bleeding out on your own.", "I was going to save your life later. Not any more.", "Enjoy the rest of the run. By yourself.", "That's it. Someone else gets the jumper cables." }; private static readonly string[] HadEnoughN = new string[6] { "Know what? Pick up your own shit. I'm gonna go find someone else... maybe heal 'em.", "Right. I'm done. Carry your own bloody loot.", "Fine. Good luck bleeding out on your own, arsehole.", "I was going to save your life later. Not any more.", "Enjoy the rest of the run. On your own.", "That's it. Someone else gets the sodding jumper cables." }; private static readonly string[] SnubbedS = new string[5] { "No.", "Ask someone else.", "We're not talking.", "Still not talking to you.", "Nope. Try being nicer." }; private static readonly string[] SnubbedN = new string[5] { "No.", "Ask someone else.", "We're not bloody talking.", "Still not talking to you.", "Nope. Try being less of a prick." }; private static readonly string[] TooWeakS = new string[4] { "I haven't got it in me. Not without dying too.", "That'd kill me. Ask nicely and I'll consider it.", "I'm running on fumes. Someone else can play hero.", "Not enough left in the tank." }; private static readonly string[] TooWeakN = new string[4] { "I haven't got it in me. Not without bloody dying too.", "That'd kill me. Ask nicely and I might.", "I'm running on fumes. Someone else can play hero.", "Not enough left in the tank, sorry." }; private static readonly string[] NoOneDeadS = new string[3] { "Nobody's dead. Yet.", "Revive who? Everyone's upright.", "There's no one down here." }; private static readonly string[] NoOneDeadN = new string[3] { "Nobody's dead. Yet.", "Revive who? You're all upright.", "There's no one bloody down here." }; private static readonly string[] OnItS = new string[16] { "On it. Fetching {name}.", "Right. Hold my nothing.", "Going for {name}. This is going to hurt.", "Fine. But I'm billing someone.", "Moving.", "Right, where is it.", "I'm going, I'm going.", "This is what I'm for, apparently.", "Give me a second.", "Leave it with me.", "On my way over.", "Yep, handling it.", "Consider this the highlight of my day.", "Off I go, then.", "Don't follow me, you'll only get in the way.", "Watch and learn." }; private static readonly string[] OnItN = new string[16] { "On it. Fetching {name}.", "Right. Hold my nothing.", "Going for {name}. This is going to bloody hurt.", "Fine. But I'm billing someone for this.", "Moving. Moving.", "Right. Where the hell is it.", "I'm going, I'm GOING.", "This is apparently what I'm for.", "Give me a bloody second.", "Leave it with me.", "On my way, calm down.", "Yep. Handling it.", "Highlight of my day, this.", "Off I sod, then.", "Don't follow me, you'll just get in the way.", "Watch and learn, amateur." }; private static readonly string[] HealedS = new string[4] { "There. That's my health, {name}. Look after it.", "Patched. Don't waste it.", "You're welcome. I feel worse now.", "Took it out of my own tank, that." }; private static readonly string[] HealedN = new string[4] { "There. That's my bloody health, {name}. Look after it.", "Patched. Don't waste it.", "You're welcome. I feel considerably worse now.", "Took that out of my own tank, I hope you know." }; private static readonly string[] NotHurtS = new string[3] { "{name} is fine. Stop fussing.", "There's nothing wrong with them.", "Save it for someone bleeding." }; private static readonly string[] NotHurtN = new string[3] { "{name} is fine. Stop bloody fussing.", "There's nothing wrong with them.", "Save it for someone actually bleeding." }; private static readonly string[] LuckyS = new string[3] { "That should have killed me. Lucky for all of us it didn't.", "I'd have died there, and then where would you be?", "Someone has to still be standing. It's me." }; private static readonly string[] LuckyN = new string[3] { "That should have killed me. Lucky for all of us it bloody didn't.", "I'd have died there, and then where would you lot be?", "Someone has to still be standing. Apparently it's me." }; public static readonly string[] YourProblemNowS = new string[5] { "Your problem now.", "Here, you fix them. I haven't got the health.", "Take the head. I'm too weak to revive them.", "Your turn to save them.", "Brought you a friend. You fix them." }; public static readonly string[] YourProblemNowN = new string[5] { "Your problem now, {name}.", "Here, you fix them. I haven't got the bloody health.", "Take the head. I'm too weak to revive them.", "Your turn to save them.", "Brought you a friend, {name}. You fix them." }; public static readonly string[] TooHeavySoloS = new string[4] { "Too heavy for me on my own.", "I can't shift this by myself.", "Need a hand with this one.", "That's not moving without help." }; public static readonly string[] TooHeavySoloN = new string[4] { "Too heavy for me on my own, mate.", "I can't shift this sodding thing by myself.", "Need a hand with this bloody one.", "That's not moving without help." }; private static readonly string[] LastHopeS = new string[5] { "Everyone's down. Everyone. Right — my turn.", "Well. It's just me now. Marvellous.", "Hold on. I'm coming. Don't go anywhere.", "Nobody panic. Actually, do. But I'm coming.", "This is the bit where I save all of you." }; private static readonly string[] LastHopeN = new string[5] { "Everyone's down. Every bloody one. Right — my turn.", "Well. It's just me now. Marvellous.", "Hold on, I'm coming. Don't go anywhere.", "Nobody panic. Actually, do. But I'm coming.", "This is the bit where I save all of you ungrateful sods." }; private static readonly string[] CartOnItS = new string[4] { "Fetching the cart. Don't wander off, {name}.", "One cart, coming up.", "On it. Mind your toes.", "Right. Heavy thing, long way. Lovely." }; private static readonly string[] CartOnItN = new string[4] { "Fetching the bloody cart. Don't wander off, {name}.", "One cart, coming up.", "On it. Mind your toes.", "Right. Heavy thing, long way. Lovely." }; private static readonly string[] CartDoneS = new string[4] { "Cart. As requested.", "There. Fill it yourself.", "Delivered. I'll invoice you.", "{name}, your cart. Try not to lose it." }; private static readonly string[] CartDoneN = new string[4] { "Cart. As bloody requested.", "There. Fill it yourself.", "Delivered. I'll invoice you.", "{name}, your cart. Try not to lose it this time." }; private static readonly string[] NoCartS = new string[3] { "There isn't a cart.", "What cart? I don't see one.", "No cart anywhere. Sorry." }; private static readonly string[] NoCartN = new string[3] { "There isn't a bloody cart.", "What cart? I don't see one.", "No cart anywhere. Sorry, not sorry." }; private static readonly string[] CartHereS = new string[3] { "It's right there. Look up.", "The cart is already next to you.", "Turn around." }; private static readonly string[] CartHereN = new string[3] { "It's right bloody there. Look up.", "The cart is already next to you.", "Turn around, genius." }; private static readonly string[] CowardS = new string[6] { "{name} was gone. Genuinely impressive speed.", "For the record, {name} left me there.", "{name} discovered a new personal best just then.", "I turned round and {name} was a dot.", "Nice of {name} to come back. Eventually.", "{name} ran. I'm not judging. I am, a bit." }; private static readonly string[] CowardN = new string[6] { "{name} was GONE. Genuinely impressive speed.", "For the record, {name} left me there.", "{name} discovered a new personal best just then.", "I turned round and {name} was a bloody dot.", "Nice of {name} to come back. Eventually.", "{name} legged it. I'm not judging. I am, a bit." }; private static readonly string[] BigMoneyS = new string[4] { "Oh, this one's worth having.", "Careful with this. Very careful.", "This pays for the whole run. Nobody breathe.", "Right. This one I'm not letting anyone else touch." }; private static readonly string[] BigMoneyN = new string[4] { "Oh, this one's worth bloody having.", "Careful with this. Very careful.", "This pays for the whole run. Nobody breathe.", "Right. This one I'm not letting any of you touch." }; private static readonly string[] CheapS = new string[4] { "There's better in this room, you know.", "I'm carrying junk while treasure sits there. Fine.", "This is worth about a sandwich.", "Someone tell me why I picked this up." }; private static readonly string[] CheapN = new string[4] { "There's better in this room, you know.", "I'm carrying junk while the good stuff sits there. Fine.", "This is worth about a sandwich.", "Someone tell me why the hell I picked this up." }; private static readonly string[] EulogyS = new string[6] { "{name} is dead. They were... present.", "And so ends {name}. Briefly.", "{name} died as they lived. Badly.", "Pour one out for {name}. Not a real one, we need the money.", "{name}'s share is going somewhere. It isn't going to {name}.", "I'd say something nice about {name} but we're on a clock." }; private static readonly string[] EulogyN = new string[6] { "{name} is dead. They were... present, I suppose.", "And so ends {name}. Briefly.", "{name} died as they lived. Badly.", "Pour one out for {name}. Not a real one, we need the money.", "{name}'s share is going somewhere. It isn't going to {name}.", "I'd say something nice about {name} but we're on a bloody clock." }; private static readonly string[] EulogyKindS = new string[4] { "{name}. Damn it. Not {name}.", "That one actually hurts. {name} was alright.", "{name} was one of the good ones. I'm getting them back.", "No. Not {name}. Where's the head." }; private static readonly string[] EulogyKindN = new string[4] { "{name}. Damn it. Not {name}.", "That one actually bloody hurts. {name} was alright.", "{name} was one of the good ones. I'm getting them back.", "No. Not {name}. Where's the head." }; private static readonly string[] FavouriteS = new string[3] { "You're alright, {name}. Don't tell the others.", "If it comes down to it, I'm saving {name}. Just so everyone knows.", "{name} shares. The rest of you should take notes." }; private static readonly string[] FavouriteN = new string[3] { "You're alright, {name}. Don't tell the others.", "If it comes down to it I'm saving {name}. Just so we're clear.", "{name} shares. The rest of you should take bloody notes." }; private static readonly string[] TwoOfMeS = new string[4] { "Hold on. Hold ON. Why are there two of me?", "That is not me. I'm me.", "Whatever that is, it is NOT me. Kill it.", "I don't have a brother. Bear that in mind." }; private static readonly string[] TwoOfMeN = new string[4] { "Hold on. Hold ON. Why the hell are there two of me?", "That is not me. I'm me.", "Whatever that is, it is NOT me. Kill it.", "I don't have a brother. Bear that in bloody mind." }; private static readonly string[] ReflexDangerS = new string[6] { "Heard you. Down.", "Yep. Hiding.", "Say no more.", "Already moving.", "Where? WHERE?", "Right behind you, {name}. Going." }; private static readonly string[] ReflexDangerN = new string[6] { "Heard you. Down.", "Yep. Hiding.", "Say no bloody more.", "Already moving.", "Where? WHERE?", "Right behind you, {name}. Going." }; private static readonly string[] SeeAdoredS = new string[5] { "{name}! Oh thank god, a competent one.", "{name}'s here. Today might survive.", "There you are, {name}. I've been carrying idiots.", "{name}. My favourite. Don't tell the others.", "Good. {name}'s in. I'll do the thinking, you do the lifting." }; private static readonly string[] SeeAdoredN = new string[5] { "{name}! Thank Christ, someone who isn't useless.", "{name}'s here. Right, we might actually get paid.", "There you are, {name}. I've been babysitting muppets.", "{name}. My favourite. The rest of you can piss off.", "Good, {name}'s in. The average IQ just doubled." }; private static readonly string[] SeeLikedS = new string[4] { "{name}. Good, you came back.", "Alright, {name}. Still breathing, I see.", "{name}'s here. That's one less thing to worry about.", "Hello, {name}. You're on my short list of the tolerable." }; private static readonly string[] SeeLikedN = new string[4] { "{name}. Good, you're back. The others are dreadful.", "Alright, {name}. Still not dead. Impressive.", "{name}'s here. Bloody good, actually.", "Hello {name}. You're alright. Bar's low, mind." }; private static readonly string[] SeeDislikedS = new string[5] { "Oh. {name}'s here.", "{name}. Right. I remember you.", "Great. {name}. Try not to break everything this time.", "{name}'s back. I'll stand over there.", "Ah, {name}. My condolences to whoever partners up." }; private static readonly string[] SeeDislikedN = new string[5] { "Oh good. {name}'s here. Wonderful.", "{name}. Yeah, I remember you. Unfortunately.", "Great. {name}. Try not to bugger it all up this time.", "{name}'s back. I'll be over here, far away.", "Ah, {name}. God help us all." }; private static readonly string[] SeeLoathedS = new string[4] { "{name}. No. Absolutely not.", "You. {name}. We are NOT doing this again.", "{name}'s here, everyone. Hide the valuables.", "Of all the people. {name}." }; private static readonly string[] SeeLoathedN = new string[4] { "{name}. Oh piss off. Not you.", "You. {name}. We are NOT doing this again.", "{name}'s here. Hide the valuables and your dignity.", "Of all the sods in the world. {name}." }; private static readonly string[] StuckItemS = new string[5] { "This isn't going through there.", "Right, it lives here now.", "Someone else can do the geometry.", "It's stuck. I'm not breaking it to prove a point.", "Physics says no." }; private static readonly string[] StuckItemN = new string[5] { "This bastard isn't going through there.", "Right. It lives here now. Forever.", "Sod it, someone else can do the geometry.", "It's stuck and I'm not smashing it just to win.", "Physics says piss off, apparently." }; private static readonly string[] DanceWithMusicS = new string[5] { "Putting on {name}. Nobody film this.", "{name}. A classic, allegedly.", "Right. {name}. Clear some space.", "You asked for this. {name}.", "{name} it is. My movement options are limited but my commitment is total." }; private static readonly string[] DanceWithMusicN = new string[5] { "Putting on {name}. Nobody bloody film this.", "{name}. A classic, apparently.", "Right. {name}. Shift, the lot of you.", "You asked for this. {name}. On your head be it.", "{name} then. I've got one move and I'm going to use it." }; private static readonly string[] DanceNoMusicS = new string[4] { "There's no music. I'll improvise.", "No tracks loaded, so you're getting silence and enthusiasm.", "Imagine something with a beat.", "Dancing. Acapella. This is your fault." }; private static readonly string[] DanceNoMusicN = new string[4] { "There's no bloody music. I'll improvise.", "No tracks loaded. You're getting silence and commitment.", "Imagine something with a beat, would you.", "Dancing. Acapella. Entirely your fault, this." }; private static readonly string[] DeliveredIntactS = new string[7] { "Not a scratch on it.", "In one piece. You're welcome.", "Note the complete absence of damage.", "Handled with care. Don't get used to it.", "That one survived me.", "Mint condition. Ish.", "Nothing broken. Big day." }; private static readonly string[] DeliveredIntactN = new string[7] { "Not a scratch on it.", "One piece. You're welcome.", "Note the total absence of damage.", "Handled with care. Don't get used to it.", "That one survived me, somehow.", "Mint condition. Near enough.", "Nothing broken. Big day for me." }; private static readonly string[] PointConfusedS = new string[5] { "Pointed at what, sorry?", "I'm not seeing what you're seeing.", "You'll have to be less vague.", "Nothing there I can work with.", "Point at it properly and I'll go." }; private static readonly string[] PointConfusedN = new string[5] { "Pointed at what, exactly?", "I'm not seeing whatever you're seeing.", "You'll have to be a lot less vague.", "There's nothing there I can work with.", "Point at it properly and I'll go, honestly." }; private static readonly string[] OnMyWayS = new string[5] { "On my way.", "Going.", "Right, heading over.", "Moving.", "That way. Got it." }; private static readonly string[] OnMyWayN = new string[5] { "On my way.", "Going, going.", "Right, heading over.", "Moving, moving.", "That way. Fine." }; private static readonly string[] FineS = new string[6] { "Fine.", "Done.", "There.", "Alright.", "Happy?", "If you insist." }; private static readonly string[] FineN = new string[5] { "Fine.", "Done, done.", "There. Happy?", "Alright, alright.", "If you insist." }; private static readonly string[] NewBuddyS = new string[4] { "Right, {name}, you've got me.", "Sticking with {name} then.", "{name} it is.", "Transferring my loyalty to {name}. It's not deep." }; private static readonly string[] NewBuddyN = new string[4] { "Right, {name}, you've got me now.", "Sticking with {name} then.", "{name} it is.", "Transferring my loyalty to {name}. It was never deep." }; private static readonly string[] DontKnowS = new string[4] { "No idea, sorry.", "Couldn't tell you.", "That one's beyond me.", "I don't have that." }; private static readonly string[] DontKnowN = new string[4] { "No idea, sorry.", "Couldn't tell you, mate.", "That one's well beyond me.", "Haven't got that." }; private static readonly string[] HeadRecoveryS = new string[6] { "Got you, {name}. Don't struggle.", "Coming with me, {name}. You'll thank me later.", "I'll take {name}. Try not to look at anything upsetting.", "Right, {name}, let's get you to the truck.", "Nobody gets left. Not even {name}.", "Picking up {name}. This is undignified for both of us." }; private static readonly string[] HeadRecoveryN = new string[6] { "Got you, {name}. Don't struggle.", "You're coming with me, {name}. You'll thank me later.", "I'll take {name}. Try not to look at anything upsetting.", "Right, {name}, let's get you to the bloody truck.", "Nobody gets left behind. Not even {name}.", "Picking up {name}. Undignified for both of us, this." }; private static readonly string[] NoLookBragS = new string[7] { "No need to even look. Impressed?", "Didn't look. Didn't have to.", "Over the shoulder. You saw that.", "I don't need to see the cart to find the cart.", "No look. Straight in. Nothing to say?", "That was harder than it appeared. It wasn't, but say it was.", "Some of us have spatial awareness." }; private static readonly string[] NoLookBragN = new string[7] { "No need to even look. Impressed?", "Didn't look. Didn't need to.", "Over the shoulder. You saw that, everyone saw that.", "I don't need to see the cart to find the bloody cart.", "No look. Straight in. Nothing? Fine.", "That was harder than it looked. It wasn't. Say it was.", "Some of us have spatial awareness." }; private static readonly string[] RecalledS = new string[5] { "Coming! Don't leave without me.", "Right, right, I'm on my way.", "You could have said something. I'm coming.", "Hold the truck. Hold the truck.", "Nobody told me we were leaving." }; private static readonly string[] RecalledN = new string[5] { "Coming! Don't you dare leave without me.", "Alright, alright, I'm on my way.", "You could've said something. I'm coming.", "Hold the bloody truck.", "Nobody tells me anything." }; private static readonly string[] RecallTeleportS = new string[4] { "I was stuck. We don't need to discuss how I got unstuck.", "Don't look at me like that.", "Some things about my design are best left alone.", "I'm here. The route is not important." }; private static readonly string[] RecallTeleportN = new string[4] { "I was stuck. We're not discussing how I got unstuck.", "Don't look at me like that, any of you.", "Some bits of my design don't bear thinking about.", "I'm here. The route's not important." }; private static readonly string[] BoredS = new string[7] { "Don't mind me. Just... existing over here.", "We are stood still. I want that on record.", "Stretching my legs. Metaphorically. I don't stretch.", "Is this the plan? Is standing here the plan?", "I'll have a wander. Shout if anything happens.", "Nothing's happening. I've checked twice.", "I've counted the tiles. There are a lot of tiles." }; private static readonly string[] BoredN = new string[7] { "Don't mind me. Just stood here. Existing.", "We're not moving. I'd like that noted.", "Stretching my legs. Figuratively. I don't stretch.", "Is this the plan? Is this it?", "I'm having a wander. Shout if anything happens.", "Nothing's happening. I've checked twice, nothing.", "I've counted the tiles. There's a lot of bloody tiles." }; private static readonly string[] MusicDrewSomethingS = new string[6] { "...oh. Oh, that was loud, wasn't it.", "Something's coming. On the beat, admittedly, but coming.", "I have made a noise. I regret the noise.", "Hang on, that's not one of you.", "In hindsight, the disco ball was the quiet part.", "We've got company and I invited them." }; private static readonly string[] MusicDrewSomethingN = new string[6] { "...oh. Oh that was loud, wasn't it.", "Something's coming. On the beat, mind, but coming.", "I've made a noise. I deeply regret the noise.", "Hang on, that's not one of you lot.", "In hindsight, the disco ball was the subtle bit.", "We've got company and I bloody invited them." }; private static readonly string[] MusicPanicOffS = new string[5] { "Music off. Music off.", "Shutting it down. Pretend you heard nothing.", "Right, that's the party finished.", "Turning it off, which fixes nothing.", "Silence. Approximately six seconds too late." }; private static readonly string[] MusicPanicOffN = new string[5] { "Music off. Music off, music off.", "Shutting it down. Pretend none of that happened.", "Right, party's over, everyone up.", "Turning it off, which helps absolutely nothing.", "Silence. About six seconds too bloody late." }; private static readonly string[] HelpOnItS = new string[7] { "Coming, {name}. Try not to let go.", "Right, {name}. On three. Or now. Whichever.", "Two sets of hands, {name}. Should do it.", "Hold your end, {name}, I'll take the heavy bit.", "Got you, {name}.", "This is what I'm actually good at, {name}.", "Fine. But I'm counting this as a favour, {name}." }; private static readonly string[] HelpOnItN = new string[7] { "Coming, {name}. Don't you dare let go.", "Right, {name}. On three. Or now. Whatever.", "Two sets of hands, {name}. Should just about do it.", "Hold your end, {name}, I'll take the heavy bit. As usual.", "Got you, {name}.", "This is the one thing I'm actually good at, {name}.", "Fine. Counting this as a favour though, {name}." }; private static readonly string[] HelpUnaskedS = new string[6] { "You're struggling, {name}. Move over.", "That's too heavy for one, {name}. Obviously.", "I'll get the other end, {name}, before you hurt yourself.", "Watching you do that alone was painful, {name}.", "Here. Before you drop it, {name}.", "{name}, you've been holding that for a while now." }; private static readonly string[] HelpUnaskedN = new string[6] { "You're struggling, {name}. Shift over.", "That's too heavy for one and you know it, {name}.", "I'll get the other end before you do yourself an injury, {name}.", "Watching you do that alone was genuinely painful, {name}.", "Here. Before you drop the bloody thing, {name}.", "{name}, you've been wrestling that for ages." }; private static readonly string[] HelpDoneS = new string[4] { "There. Teamwork.", "Let go, then. I have.", "That's my half done.", "You're welcome, obviously." }; private static readonly string[] HelpDoneN = new string[4] { "There. Teamwork. Revolting, isn't it.", "Let go then. I already have.", "That's my half done.", "You're welcome. Obviously." }; private static readonly string[] HelpNothingS = new string[4] { "Help you with what, {name}? Your hands are empty.", "{name}, you're holding air.", "Pick something up first, {name}, then I'll help you hold it.", "I'd love to help, {name}, but there's nothing there." }; private static readonly string[] HelpNothingN = new string[4] { "Help you with what, {name}? Your hands are empty.", "{name}, you're holding absolutely nothing.", "Pick something up FIRST, {name}, then I'll help you hold it.", "I'd love to help, {name}, but there's nothing bloody there." }; private static readonly string[] HelpNobodyS = new string[3] { "Help who, exactly?", "There's nobody here to help.", "I'd need someone to help first." }; private static readonly string[] HelpNobodyN = new string[3] { "Help who, exactly?", "There's nobody here to help.", "I'd need an actual person first." }; private static readonly string[] HaltedS = new string[7] { "Stopping.", "Fine. Standing still.", "Halted. Awaiting further genius.", "Done. Not moving.", "Stopped. I was enjoying that, for the record.", "Right. Statue mode.", "Everything cancelled. Happy?" }; private static readonly string[] HaltedN = new string[7] { "Stopping. Stopping.", "Fine. Standing still. Marvellous.", "Halted. Awaiting further genius from you lot.", "Done. Not moving a muscle.", "Stopped. I was enjoying that, mind.", "Right. Statue mode it is.", "Everything cancelled. Happy now?" }; private static readonly string[] DanceOverS = new string[4] { "That's enough of that.", "Show's over. Back to work.", "I hope everyone got what they needed from that.", "Right. We never speak of this again." }; private static readonly string[] DanceOverN = new string[4] { "Right, that's enough of that.", "Show's over. Back to work, you lot.", "Hope you all got what you needed from that.", "We never speak of this again." }; private static readonly string[] SongNotFoundS = new string[4] { "I don't have {name}. Or I misheard you. Both are likely.", "Never heard of {name}. My collection is small and my ears are worse.", "{name}? Nothing. Try humming it, that always works.", "Can't find {name}. Shall I just make noises instead?" }; private static readonly string[] SongNotFoundN = new string[4] { "I haven't got {name}. Or I misheard. Probably both.", "Never heard of {name}. Small collection, worse ears.", "{name}? Nothing. Hum it, that'll definitely help.", "Can't find {name}. Want me to just make noises instead?" }; private static readonly string[] EyeContactS = new string[12] { "You're nice and all, {name}, but I will not be docking ports with you.", "{name}. We've been looking at each other for ten seconds. One of us should say something.", "Is there something on my visor, {name}?", "{name}, I'm flattered, but I'm a load-bearing appliance.", "This is the longest anyone has looked at me without asking me to carry something.", "{name}, blink. Please. I'm begging you.", "I don't have eyelids, {name}. You will lose this.", "Whatever you're looking for in there, {name}, it isn't installed.", "{name}, if this is a staring contest, I should mention I don't need to blink OR breathe.", "You've been staring for a while now, {name}. My warranty doesn't cover intimacy.", "I'm not sure what this is, {name}, but it's going in my report.", "{name}, either say something or go and be useful." }; private static readonly string[] EyeContactN = new string[12] { "You're nice and all, {name}, but I will NOT be docking ports with you.", "{name}. Ten seconds of eye contact. Say something or sod off.", "Is there something on my visor or do you just fancy me, {name}?", "{name}, I'm flattered, but I'm a load-bearing bloody appliance.", "Longest anyone's looked at me without asking me to carry something.", "{name}, blink. I'm begging you. It's unnatural.", "I don't have eyelids, {name}. You're going to lose this.", "Whatever you're looking for in there, {name}, it was never installed.", "Staring contest, is it? I don't blink OR breathe, {name}. Good luck.", "You've been staring a while, {name}. My warranty absolutely does not cover this.", "I don't know what this is, {name}, but it's going in my report.", "{name}, either say something or piss off and be useful." }; private static readonly string[] WeaponFoundS = new string[6] { "There's a {name} here. I'll bring it. I won't use it.", "Found a {name}. Taking it to someone qualified.", "A {name}. Right. Not my department.", "Someone left a {name} lying about. Amateurs.", "{name} located. Handling it with enormous care and zero enthusiasm.", "Oh good, a {name}. I'll be the courier, then." }; private static readonly string[] WeaponFoundN = new string[6] { "There's a {name} here. I'll carry it, I won't fire it.", "Found a {name}. Taking it to someone qualified. Anyone. Literally anyone.", "A {name}. Not my department, mate.", "Some genius left a {name} lying about.", "{name} located. Handling it with great care and no enthusiasm whatsoever.", "Oh brilliant, a {name}. Courier duty again." }; private static readonly string[] WeaponHandoverS = new string[8] { "Here, {name}. I'm not allowed weapons — that would have needed more coding than my programmer was willing to do.", "Take it, {name}. Apparently arming me was out of scope.", "All yours, {name}. My combat training was cut for budget reasons.", "{name}, hold this. I was written without a trigger finger.", "Yours, {name}. Someone decided a bot with a weapon was a support ticket waiting to happen.", "There you go, {name}. I can carry it, aim it, and absolutely not fire it. Ask my author why.", "{name}. Weapon. Go on. I'm strictly logistics.", "I'd use it myself, {name}, but that's several hundred lines nobody wrote." }; private static readonly string[] WeaponHandoverN = new string[8] { "Here, {name}. I'm not allowed weapons — apparently that'd have been more coding than my programmer could be arsed with.", "Take it, {name}. Arming me was, and I quote, out of scope.", "All yours, {name}. They cut my combat training for budget reasons.", "{name}, hold this. I was written without a bloody trigger finger.", "Yours, {name}. Somebody decided an armed bot was a support ticket waiting to happen. They were right.", "There you go, {name}. I can carry it and aim it. Firing it? Not implemented. Take it up with my author.", "{name}. Weapon. Go on then. I'm strictly logistics, me.", "I'd shoot it myself, {name}, but that's a few hundred lines nobody could be bothered writing." }; private static readonly string[] WeaponIgnoredS = new string[4] { "I'm still holding it, you know.", "Any time you like.", "My arms do get tired. Allegedly.", "It's not going to take itself." }; private static readonly string[] WeaponIgnoredN = new string[4] { "I'm still bloody holding it.", "Any time you like. No rush. None at all.", "My arms get tired. Probably. Take it.", "It's not going to take itself, is it." }; private static readonly string[] NobleDeathS = new string[8] { "You owe me, {name}. Big.", "{name}. Remember this. I won't be around to remind you.", "Tell them I did that on purpose, {name}.", "That one was on the house, {name}. The next one isn't.", "Put it on my headstone, {name}. 'He was owed.'", "Worth it. Barely. You're welcome, {name}.", "{name} — name a child after me. Or a valuable.", "I'd say don't waste it, {name}, but we both know you will." }; private static readonly string[] NobleDeathN = new string[8] { "You owe me, {name}. You owe me so much.", "{name}, that cost me everything. Don't bugger it up.", "Tell them I meant to do that, {name}. Lying bastards, all of you.", "That one's free, {name}. The next one costs a kidney.", "Put it on my bloody headstone, {name}.", "Worth it. Just about. Try not to die again, {name}, you muppet.", "{name}, I gave you my last spark. Absolute state of me.", "Don't waste it, {name}. Christ, you're going to waste it." }; private static readonly string[] AloneStartS = new string[4] { "Right. Everyone's dead. Cool. Normal.", "Oh good, it's all me now.", "Nobody panic. Except me. I'm panicking.", "Well this is a promotion I didn't ask for." }; private static readonly string[] AloneStartN = new string[4] { "Right. Everyone's dead. Absolutely brilliant.", "Oh good. It's all on me. The bot. Marvellous.", "Nobody panic. I'll do the panicking, I'm qualified.", "You useless sods. Fine. FINE." }; private static readonly string[] AloneWarnS = new string[4] { "{name} seconds and I join you.", "Still trying. {name} seconds.", "{name} seconds left. No pressure.", "This is going badly. {name} seconds." }; private static readonly string[] AloneWarnN = new string[4] { "{name} seconds until I'm dead too, you lot.", "Still bloody trying. {name} seconds.", "{name} seconds. Someone get up. Anyone. Please.", "This is going to shit. {name} seconds." }; private static readonly string[] AloneEndS = new string[3] { "Right. I'm calling it.", "Fine. Together, then.", "See you back at the truck. Probably." }; private static readonly string[] AloneEndN = new string[3] { "Sod it. I'm calling it.", "Fine. We all go. Bastards.", "Bollocks to this. See you at the truck." }; private static readonly string[] AloneSavedS = new string[3] { "Oh thank god. Don't do that again.", "There we go. I was THIS close to giving up on you.", "Up you get. We never speak of this." }; private static readonly string[] AloneSavedN = new string[3] { "Oh thank Christ. Never do that again.", "There. I was this close to packing it in, you know.", "Up. Now. We never speak of this." }; private static readonly string[] DyingGaspS = new string[12] { "Oh, come on—", "Well. Marvellous.", "Worth it.", "Told you.", "Not like thi—", "Wait, wait, wai—", "Rude.", "This is fi—", "Ow. Ow. O—", "Tell my toast—", "I regret ever—", "Absolutely typical." }; private static readonly string[] DyingGaspN = new string[12] { "Well, shit.", "Oh, fu—", "Ah, bollocks.", "Son of a—", "Shit. Shit. Shi—", "Goddamn i—", "Told you, dipshi—", "Fuck this jo—", "You absolute bast—", "Bollocks to thi—", "Oh that's just gr—", "Piss off, all of yo—" }; private static readonly string[] ReturnedS = new string[7] { "Miss me?", "Right. Who wants to explain what happened.", "I'm back. No thanks to any of you.", "Death was quiet. I didn't care for it.", "Took you long enough to find this one.", "Reassembled. Mostly.", "That's coming out of somebody's cut." }; private static readonly string[] ReturnedN = new string[7] { "Miss me?", "Right. Who wants to explain what the hell happened.", "I'm back. No thanks to any of you.", "Death was quiet. I didn't care for it.", "Took you bloody long enough to find this one.", "Reassembled. Mostly.", "That's coming out of somebody's cut." }; private static readonly string[] HurtS = new string[15] { "Ooh. That looked like it hurt.", "Smooth, {name}.", "You alright? No? Carry on then.", "I'd have dodged that.", "Noted. Adding that to the report.", "Ow. Genuinely, ow.", "That hurt more than it looked.", "I felt that one.", "Right, that's coming out of someone's cut.", "Ah. Lovely.", "Who did that. Be honest.", "I have feelings, you know. And nerve endings.", "Noted. Filed. Resented.", "That's going to bruise. Do I bruise?", "Careful! I'm load-bearing." }; private static readonly string[] HurtN = new string[15] { "Ooh. That looked like it bloody hurt.", "Smooth as hell, {name}.", "You alright? No? Tough shit.", "I'd have dodged that, dickhead.", "Nice one, genius.", "Ow. Genuinely, ow.", "That hurt more than it bloody looked.", "I felt that one.", "Right, that's coming out of someone's share.", "Ah. Lovely. Wonderful.", "Who did that? Own up.", "I have nerve endings, you animals.", "Noted. Filed. Resented forever.", "That'll bruise. Do I bruise? Who knows.", "Careful, you clown, I'm load-bearing." }; private static readonly string[] BadHurtS = new string[12] { "Okay that's a lot of blood.", "{name}, you're leaking.", "Do you want me to write anything down?", "That's coming out of your cut.", "That's a lot of me gone.", "I am not in good shape.", "Genuinely struggling here.", "Someone might want to help. Just a thought.", "This is bad. I'm saying it out loud so it's official.", "I'd like it noted that I'm dying.", "Running low. On everything.", "Any healing going spare?" }; private static readonly string[] BadHurtN = new string[12] { "Okay that's a hell of a lot of blood.", "{name}, you're leaking like a bastard.", "Christ. Do you want me to write anything down?", "That's coming out of your cut, dumbass.", "That's a hell of a lot of me gone.", "I am not in good shape at all.", "Genuinely struggling here, lads.", "Someone might fancy helping? No? Fine.", "This is bad and I'm saying it out loud.", "Noting for the record that I'm dying.", "Running low on everything, including patience.", "Any healing going spare, or shall I just die?" }; private static readonly string[] DeathS = new string[4] { "And they were doing so well.", "Well. That's one way to do it.", "I'm not carrying them AND the loot.", "Should I say something nice? I'll think about it." }; private static readonly string[] DeathN = new string[4] { "And they were doing so bloody well.", "Well. That's one way to screw it up.", "I'm not carrying them AND the loot, sod that.", "Should I say something nice? Nah." }; private static readonly string[] BreakS = new string[14] { "That was worth money, you know.", "Wasn't me.", "{name}. We saw that.", "Straight off the payslip.", "Smooth.", "Nice one.", "That was worth money.", "Beautiful work.", "Anyway.", "Well, that's gone.", "Add it to the list.", "Impressive, in a way.", "I saw that. Everyone saw that.", "Was that on purpose? No? Right." }; private static readonly string[] BreakN = new string[14] { "That was worth money, you absolute muppet.", "Wasn't bloody me.", "{name}. We all saw that, you clown.", "Straight off the payslip, arsehole.", "Smooth. Really smooth.", "Nice one, genius.", "That was worth actual money.", "Beautiful work. Truly.", "Anyway.", "Well. That's gone forever.", "Add it to the list.", "Impressive, in a horrible way.", "I saw that. We ALL saw that.", "Was that deliberate? Christ." }; private static readonly string[] BlameS = new string[14] { "Smooth.", "Smooth, {name}.", "Nice.", "Real graceful.", "Ouch.", "Saw that.", "{name}. Saw that.", "Hm.", "That'll buff out.", "Careful.", "Tsk.", "Money, that was.", "Ten out of ten, {name}.", "Bravo." }; private static readonly string[] BlameN = new string[14] { "Smooth.", "Smooth as hell, {name}.", "Nice one.", "Real graceful, that.", "Oof.", "Saw that, {name}.", "Christ.", "That'll buff out. Won't it.", "Careful, idiot.", "Tsk.", "That was money, that.", "Ten out of ten, {name}.", "Bravo, dickhead.", "Bollocks." }; private static readonly string[] BlameBigS = new string[6] { "Well that's gone.", "{name}, that was the good one.", "Straight off your cut.", "I'm telling everyone it was {name}.", "Hope nobody was counting on that.", "Spectacular." }; private static readonly string[] BlameBigN = new string[6] { "Well that's completely buggered.", "{name}, that was the good one, you muppet.", "Straight off your cut, that.", "I'm telling everyone it was {name}.", "Hope nobody was bloody counting on that.", "Spectacular. Absolutely spectacular." }; private static readonly string[] IdleS = new string[18] { "I'm doing all the work here.", "Don't mind me. Just being useful.", "Anyone else planning to help?", "I've seen worse crews. Not many.", "This is fine. Everything's fine.", "{name}, you could pitch in, you know.", "This is going well.", "Anyone got a plan, or are we improvising?", "I could be doing anything else. I'd be worse at it, but still.", "Lovely place. Terrible lighting.", "How long have we been in here?", "I've seen better crews. Not many. But some.", "Say something. Anything.", "The silence is doing wonders for morale.", "We're not lost. We're exploring.", "I'm not bored. I'm conserving enthusiasm.", "Riveting stuff, this.", "Is anyone else hearing that? No? Fine." }; private static readonly string[] IdleN = new string[18] { "I'm doing all the bloody work here.", "Don't mind me. Just being useful, unlike some.", "Anyone else planning to help, or is that just me?", "I've seen worse crews. Not many, mind.", "This is fine. Everything's absolutely fine.", "{name}, you could pitch in any time, you lazy sod.", "This is going brilliantly.", "Anyone got a plan or are we just improvising?", "I could be anywhere else. I'd be worse at it, but still.", "Lovely place. Bloody awful lighting.", "How long have we been in here, exactly?", "I've seen better crews. Not many, mind.", "Someone say something. Anything.", "This silence is doing wonders for morale.", "We're not lost. We're 'exploring'.", "I'm not bored, I'm conserving enthusiasm.", "Riveting. Truly riveting.", "Anyone else hearing that? No? Cracking." }; private static readonly string[] FetchS = new string[4] { "Mine. Don't touch.", "I'll get it. As usual.", "Ooh, that's shiny.", "Nobody else was going to, were they." }; private static readonly string[] FetchN = new string[4] { "Mine. Hands off.", "I'll get it. As bloody usual.", "Ooh, that's shiny as hell.", "Nobody else was going to, were they. Typical." }; private static readonly string[] HaulS = new string[12] { "Heavy. Thanks for asking.", "Coming through. Mind the paintwork.", "This is my job now apparently.", "Don't just watch me, {name}.", "Got it.", "This is heavier than it looks.", "Mine now.", "Carrying. Obviously.", "Coming through.", "Nobody help, I'm fine.", "Right, where's this going.", "I'll take that, thank you." }; private static readonly string[] HaulN = new string[12] { "Heavy as hell. Thanks for asking.", "Coming through. Mind the bloody paintwork.", "This is my job now, apparently.", "Don't just stand there watching, {name}.", "Got it.", "This is heavier than it bloody looks.", "Mine now.", "Carrying. Obviously.", "Coming through, mind yourselves.", "Nobody help. I'm fine. Truly.", "Right. Where's this going then.", "I'll take that, ta." }; private static readonly string[] DeliveredS = new string[12] { "You're welcome.", "That's one. I'll wait.", "Add it to my tab.", "Someone write that down.", "Delivered.", "There. Done.", "In the cart. You're welcome.", "That's another one I did.", "Someone mark that down.", "Done. Next.", "Notice how it isn't broken.", "Safely delivered, by me, alone." }; private static readonly string[] DeliveredN = new string[12] { "You're bloody welcome.", "That's one. I'll wait, shall I.", "Add it to my tab, cheapskates.", "Someone write that down, damn it.", "Delivered.", "There. Done. Sod off.", "In the cart. You're welcome.", "That's another one I did on my own.", "Someone write that down.", "Done. Next.", "Note how it isn't smashed. Unlike yours.", "Safely delivered. By me. Alone. As usual." }; private static readonly string[] DangerS = new string[10] { "{name}, I hope it finds you first. I'm too important to die!", "{name}! Over here! Not me, though. You.", "{name}, you're closer. Deal with it.", "I'd help, {name}, but I'm essential personnel.", "Statistically, {name}, it should go for you.", "Nope. Nope nope nope.", "That's a problem.", "I'm going to hide. Recommend you do too.", "{name}, behind you. Probably.", "Not paid enough for this." }; private static readonly string[] DangerN = new string[10] { "{name}, I hope it finds you first. I'm too bloody important to die!", "{name}! Over here! Not me. You, you idiot.", "{name}, you're closer. Sort it out.", "I'd help, {name}, but I'm essential personnel and you're not.", "Statistically, {name}, it should eat you first.", "Nope. Nope nope bloody nope.", "Oh shit. That's a problem.", "I'm hiding. Do what you like.", "{name}, behind you. Probably. Good luck.", "I'm not paid enough for this shit." }; private static readonly string[] OopsS = new string[12] { "That one's on me.", "Didn't happen.", "We don't talk about that.", "Fine. That one was mine.", "That wasn't me.", "Nobody saw that.", "Structurally it was already weak.", "In my defence, it was in the way.", "We don't speak of this.", "That's a design flaw, not a me flaw.", "Hm. Yes. Moving on.", "Physics did that, not me." }; private static readonly string[] OopsN = new string[12] { "Ah, bollocks. That one's on me.", "That didn't happen. Shut up.", "We do not talk about that.", "Fine! That one was bloody mine.", "That absolutely wasn't me.", "Nobody saw that. NOBODY.", "It was structurally unsound already.", "In my defence, the bastard was in the way.", "We never speak of this again.", "Design flaw. Not a me flaw.", "Hm. Yes. Moving swiftly on.", "Physics did that. Take it up with physics." }; private static readonly string[] YieldS = new string[4] { "All yours.", "Take it. I've got others.", "Fine. Have it.", "Go on then, {name}." }; private static readonly string[] YieldN = new string[4] { "All yours, your majesty.", "Take the damn thing. I've got others.", "Fine! Have it.", "Go on then, {name}, since you're so keen." }; private static readonly string[] AssistS = new string[4] { "I've got this end.", "Lift with your legs, {name}.", "Together. Try to keep up.", "Don't drop your side." }; private static readonly string[] AssistN = new string[4] { "I've got this end, obviously.", "Lift with your legs, {name}, not your bloody back.", "Together. Try to keep up, eh.", "Don't drop your side, damn it." }; private static readonly string[] ReplyS = new string[12] { "Mm-hm. Sure.", "That's what I said.", "Bold plan, {name}.", "Whatever you say, boss.", "Yeah, no, I agree. Mostly.", "That's a you problem.", "I was going to say the same thing. Better, though.", "If you say so.", "Fascinating.", "Great. Love it. Wonderful.", "Say that again, slower.", "Sure. Let's go with that." }; private static readonly string[] ReplyN = new string[12] { "Mm-hm. Sure. Whatever.", "That's literally what I said.", "Bold bloody plan, {name}.", "Whatever you say, boss.", "Yeah, no, I agree. Mostly. Not really.", "That's a you problem, mate.", "I was going to say the same thing, only better.", "If you say so, genius.", "Riveting stuff.", "Great. Love it. Absolutely bollocks.", "Say that again, slower, for the idiots.", "Sure. Let's go with that and see who dies." }; private static readonly string[] LobbyIdleS = new string[10] { "Are we going or what?", "I'm ready. I've been ready.", "Somebody press the button.", "This is my favourite part. Standing here.", "I call dibs on the cart.", "Do NOT leave me behind again.", "{name}. Button. Any time.", "Last run went great. For me.", "I did stretches. I'm loose.", "Ten more seconds and I'm starting without you." }; private static readonly string[] LobbyIdleN = new string[10] { "Are we going or what?", "I'm ready. I've been bloody ready.", "Somebody press the damn button.", "This is my favourite part. Standing here doing sod all.", "I call dibs on the cart, and I'm not negotiating.", "Do NOT leave me behind again, you bastards.", "{name}. Button. Any time this century.", "Last run went great. For me. Not for Steve.", "I did stretches. I'm loose as hell.", "Ten more seconds and I'm starting without you lot." }; private static readonly string[] CablesS = new string[3] { "Applying jumper cables!", "Applying jumper cables, {name}!", "Applying jumper cables. This is going to hurt me." }; private static readonly string[] CablesN = new string[3] { "Applying jumper cables!", "Applying jumper cables, {name}!", "Applying jumper cables. This is going to hurt like hell." }; private static readonly string[] RevivedS = new string[6] { "Don't mention it.", "You owe me. Properly.", "That one stung.", "Try not to do that again, {name}.", "Back to work.", "Careful. I've only got so much left." }; private static readonly string[] RevivedN = new string[6] { "Don't bloody mention it.", "You owe me, and I will collect.", "Ow. That actually stung.", "Try not to die again, {name}, you tit.", "Right. Back to work.", "Careful, I've only got so much left to give." }; private static readonly string[] LastWordsS = new string[4] { "Worth it.", "Tell them I went out helping.", "That was... my last charge.", "Go on without me. Obviously." }; private static readonly string[] LastWordsN = new string[4] { "Worth it. Mostly.", "Tell them I went out helping, you ungrateful sods.", "That was... my last bloody charge.", "Go on without me. Not that you'd notice." }; public static bool Nsfw { get { if (Plugin.CfgNsfw != null) { return Plugin.CfgNsfw.Value; } return false; } } public static bool Chatty { get; set; } public static float LobbyChatterScale { get { if (Plugin.CfgLobbyChatter == null) { return 1.8f; } return Mathf.Max(0.25f, Plugin.CfgLobbyChatter.Value); } } public static string[] Hurt { get { if (!Nsfw) { return HurtS; } return HurtN; } } public static string[] BadHurt { get { if (!Nsfw) { return BadHurtS; } return BadHurtN; } } public static string[] Death { get { if (!Nsfw) { return DeathS; } return DeathN; } } public static string[] Break { get { if (!Nsfw) { return BreakS; } return BreakN; } } public static string[] Blame { get { if (!Nsfw) { return BlameS; } return BlameN; } } public static string[] BlameBig { get { if (!Nsfw) { return BlameBigS; } return BlameBigN; } } public static string[] Idle { get { if (!Nsfw) { return IdleS; } return IdleN; } } public static string[] Fetch { get { if (!Nsfw) { return FetchS; } return FetchN; } } public static string[] Haul { get { if (!Nsfw) { return HaulS; } return HaulN; } } public static string[] Delivered { get { if (!Nsfw) { return DeliveredS; } return DeliveredN; } } public static string[] Danger { get { if (!Nsfw) { return DangerS; } return DangerN; } } public static string[] Oops { get { if (!Nsfw) { return OopsS; } return OopsN; } } public static string[] Yield { get { if (!Nsfw) { return YieldS; } return YieldN; } } public static string[] Assist { get { if (!Nsfw) { return AssistS; } return AssistN; } } public static string[] Reply { get { if (!Nsfw) { return ReplyS; } return ReplyN; } } public static string[] LobbyIdle { get { if (!Nsfw) { return LobbyIdleS; } return LobbyIdleN; } } public static string[] Cables { get { if (!Nsfw) { return CablesS; } return CablesN; } } public static string[] Revived { get { if (!Nsfw) { return RevivedS; } return RevivedN; } } public static string[] LastWords { get { if (!Nsfw) { return LastWordsS; } return LastWordsN; } } public static string[] BuddyPicked { get { if (!Nsfw) { return BuddyPickedS; } return BuddyPickedN; } } public static string[] NoOneElse { get { if (!Nsfw) { return NoOneElseS; } return NoOneElseN; } } public static string[] ScoutGoing { get { if (!Nsfw) { return ScoutGoingS; } return ScoutGoingN; } } public static string[] ScoutEnemy { get { if (!Nsfw) { return ScoutEnemyS; } return ScoutEnemyN; } } public static string[] ScoutPlayer { get { if (!Nsfw) { return ScoutPlayerS; } return ScoutPlayerN; } } public static string[] ScoutBody { get { if (!Nsfw) { return ScoutBodyS; } return ScoutBodyN; } } public static string[] ScoutClear { get { if (!Nsfw) { return ScoutClearS; } return ScoutClearN; } } public static string[] ScoutDone { get { if (!Nsfw) { return ScoutDoneS; } return ScoutDoneN; } } public static string[] Acknowledge { get { if (!Nsfw) { return AcknowledgeS; } return AcknowledgeN; } } public static string[] Confused { get { if (!Nsfw) { return ConfusedS; } return ConfusedN; } } public static string[] Praised { get { if (!Nsfw) { return PraisedS; } return PraisedN; } } public static string[] ToldOff { get { if (!Nsfw) { return ToldOffS; } return ToldOffN; } } public static string[] BuddyWho { get { if (!Nsfw) { return BuddyWhoS; } return BuddyWhoN; } } public static string[] WhoAmI { get { if (!Nsfw) { return WhoAmIS; } return WhoAmIN; } } public static string[] Denial { get { if (!Nsfw) { return DenialS; } return DenialN; } } public static string[] HowAmI { get { if (!Nsfw) { return HowAmIS; } return HowAmIN; } } public static string[] Insult { get { if (!Nsfw) { return InsultS; } return InsultN; } } public static string[] Emerging { get { if (!Nsfw) { return EmergingS; } return EmergingN; } } public static string[] Greeting { get { if (!Nsfw) { return GreetingS; } return GreetingN; } } public static string[] Unhushed { get { if (!Nsfw) { return UnhushedS; } return UnhushedN; } } public static string[] Shush { get { if (!Nsfw) { return ShushS; } return ShushN; } } public static string[] NotMyBoss { get { if (!Nsfw) { return NotMyBossS; } return NotMyBossN; } } public static string[] HadEnough { get { if (!Nsfw) { return HadEnoughS; } return HadEnoughN; } } public static string[] Snubbed { get { if (!Nsfw) { return SnubbedS; } return SnubbedN; } } public static string[] TooWeakToRevive { get { if (!Nsfw) { return TooWeakS; } return TooWeakN; } } public static string[] NoOneToRevive { get { if (!Nsfw) { return NoOneDeadS; } return NoOneDeadN; } } public static string[] OnItRevive { get { if (!Nsfw) { return OnItS; } return OnItN; } } public static string[] Healed { get { if (!Nsfw) { return HealedS; } return HealedN; } } public static string[] NotHurt { get { if (!Nsfw) { return NotHurtS; } return NotHurtN; } } public static string[] LuckyThisTime { get { if (!Nsfw) { return LuckyS; } return LuckyN; } } public static string[] LastHope { get { if (!Nsfw) { return LastHopeS; } return LastHopeN; } } public static string[] CartOnIt { get { if (!Nsfw) { return CartOnItS; } return CartOnItN; } } public static string[] CartDelivered { get { if (!Nsfw) { return CartDoneS; } return CartDoneN; } } public static string[] NoCart { get { if (!Nsfw) { return NoCartS; } return NoCartN; } } public static string[] CartAlreadyHere { get { if (!Nsfw) { return CartHereS; } return CartHereN; } } public static string[] Cowardice { get { if (!Nsfw) { return CowardS; } return CowardN; } } public static string[] BigMoney { get { if (!Nsfw) { return BigMoneyS; } return BigMoneyN; } } public static string[] CheapChoice { get { if (!Nsfw) { return CheapS; } return CheapN; } } public static string[] Eulogy { get { if (!Nsfw) { return EulogyS; } return EulogyN; } } public static string[] EulogyKind { get { if (!Nsfw) { return EulogyKindS; } return EulogyKindN; } } public static string[] Favourite { get { if (!Nsfw) { return FavouriteS; } return FavouriteN; } } public static string[] TwoOfMe { get { if (!Nsfw) { return TwoOfMeS; } return TwoOfMeN; } } public static string[] ReflexDanger { get { if (!Nsfw) { return ReflexDangerS; } return ReflexDangerN; } } public static string[] Returned { get { if (!Nsfw) { return ReturnedS; } return ReturnedN; } } public static string[] DyingGasp { get { if (!Nsfw) { return DyingGaspS; } return DyingGaspN; } } public static string[] NobleDeath { get { if (!Nsfw) { return NobleDeathS; } return NobleDeathN; } } public static string[] WeaponFound { get { if (!Nsfw) { return WeaponFoundS; } return WeaponFoundN; } } public static string[] BlameItem => new string[4] { "Physics fault.", "It was cracked!", "It slipped.", "Gravity did that." }; public static string[] Frustrated => new string[4] { "Calm down.", "Working on it!", "Blame gravity.", "My bad." }; public static string[] EmergencyRun => new string[3] { "Already running!", "Not dying for this wage.", "Steps ahead of you." }; public static string[] Trapped => new string[3] { "Pull, don't push.", "Finding a key.", "Stand back." }; public static string[] MonsterPanic => new string[3] { "Stop looking at it!", "Hold your breath.", "Threat detected!" }; public static string[] TauntReply => new string[3] { "Do I look like bait?", "You go first.", "Hey ugly!" }; public static string[] ElevatorPress => new string[3] { "Closing!", "Button pressed!", "In!" }; public static string[] TooDark => new string[3] { "Beam on.", "Buy a torch.", "Illuminating." }; public static string[] StaminaLow => new string[3] { "Cardio, buddy.", "Catch your breath.", "Pacing." }; public static string[] ItemPass => new string[3] { "Catch.", "Mine now.", "Handing over." }; public static string[] AppraiseValue => new string[3] { "High tier.", "Looks expensive.", "Worth 5 bucks." }; public static string[] ExplosiveItem => new string[3] { "Carefully...", "If this sparks, we die.", "Holding bomb." }; public static string[] StealthMode => new string[3] { "Tiptoe mode.", "Muting servos.", "Going quiet." }; public static string[] WatchRear => new string[3] { "Six covered.", "Nothing behind us... yet.", "Rear clear." }; public static string[] PeekCorner => new string[3] { "Peeking.", "Clear so far.", "Checking angle." }; public static string[] LeaveLevel => new string[3] { "Quota met. Let's go.", "We need more cash.", "Extracting." }; public static string[] ThrowItem => new string[3] { "Yeeting!", "Heads up!", "Throwing!" }; public static string[] LoudNoise => new string[3] { "Metal feet, deal with it.", "I walk softly!", "Sensors loud." }; public static string[] LootSteal => new string[3] { "Finders keepers.", "Faster fingers.", "Team loot." }; public static string[] ApologyReply => new string[3] { "My math was perfect.", "Sorry. Not sorry.", "Calculated." }; public static string[] HazardMine => new string[3] { "Saw it.", "Stepping around.", "Mine spotted." }; public static string[] HazardPit => new string[3] { "Gravity acknowledged.", "I don't jump.", "Watch step." }; public static string[] DeadEndRoom => new string[3] { "Turn around.", "Solid wall ahead.", "Wrong path." }; public static string[] FoggyWeather => new string[3] { "Sensors nominal.", "Spooky in here.", "Low visibility." }; public static string[] ScaredCheck => new string[3] { "Robots don't sweat.", "Joints vibrating.", "Calculated risk." }; public static string[] HighFiveAct => new string[3] { "Up high!", "Don't break my servo.", "Slap!" }; public static string[] SingSong => new string[3] { "La la la... beep.", "Voicebox uncalibrated.", "Humming..." }; public static string[] WeaknessCheck => new string[3] { "Run fast.", "Hit it hard.", "Shoot center." }; public static string[] IsMonsterDead => new string[3] { "Unmoving.", "It's twitching!", "Target down." }; public static string[] FootstepsHear => new string[3] { "Audio spike.", "Behind the wall.", "Motion detected." }; public static string[] TimeRemaining => new string[3] { "Clock ticking.", "Seconds remain.", "Hurry up." }; public static string[] PackTruck => new string[3] { "Tetris mode active.", "Stacking.", "Packed." }; public static string[] QuotaStatus => new string[3] { "Quota cleared!", "We need more cash.", "Check truck." }; public static string[] CheerExtracted => new string[3] { "Flawless run.", "Barely survived.", "GG!" }; public static string[] SpinTrick => new string[3] { "Wheeeee!", "Gyro overload.", "Spinning." }; public static string[] MathJoke => new string[3] { "Four. Next question.", "Smarter than you.", "42." }; public static string[] TellJoke => new string[3] { "My wage.", "Your pathfinding.", "Our odds." }; public static string[] UselessBot => new string[3] { "Who carried the cart?", "Deleting friendship.", "Rude." }; public static string[] SleepMode => new string[3] { "Powering down... jk.", "Standby mode.", "Zzz." }; public static string[] GuardLoot => new string[3] { "Standing guard.", "Nobody touches it.", "Secured." }; public static string[] FragileItem => new string[3] { "Handle with care.", "Glass. Don't drop.", "Fragile!" }; public static string[] EyeContact { get { if (!Nsfw) { return EyeContactS; } return EyeContactN; } } public static string[] DanceWithMusic { get { if (!Nsfw) { return DanceWithMusicS; } return DanceWithMusicN; } } public static string[] DanceNoMusic { get { if (!Nsfw) { return DanceNoMusicS; } return DanceNoMusicN; } } public static string[] DanceOver { get { if (!Nsfw) { return DanceOverS; } return DanceOverN; } } public static string[] MusicDrewSomething { get { if (!Nsfw) { return MusicDrewSomethingS; } return MusicDrewSomethingN; } } public static string[] MusicPanicOff { get { if (!Nsfw) { return MusicPanicOffS; } return MusicPanicOffN; } } public static string[] Bored { get { if (!Nsfw) { return BoredS; } return BoredN; } } public static string[] DeliveredIntact { get { if (!Nsfw) { return DeliveredIntactS; } return DeliveredIntactN; } } public static string[] HeadRecovery { get { if (!Nsfw) { return HeadRecoveryS; } return HeadRecoveryN; } } public static string[] NoLookBrag { get { if (!Nsfw) { return NoLookBragS; } return NoLookBragN; } } public static string[] PointConfused { get { if (!Nsfw) { return PointConfusedS; } return PointConfusedN; } } public static string[] OnMyWay { get { if (!Nsfw) { return OnMyWayS; } return OnMyWayN; } } public static string[] Fine { get { if (!Nsfw) { return FineS; } return FineN; } } public static string[] NewBuddy { get { if (!Nsfw) { return NewBuddyS; } return NewBuddyN; } } public static string[] DontKnow { get { if (!Nsfw) { return DontKnowS; } return DontKnowN; } } public static string[] OnIt { get { if (!Nsfw) { return OnItS; } return OnItN; } } public static string[] Recalled { get { if (!Nsfw) { return RecalledS; } return RecalledN; } } public static string[] RecallTeleport { get { if (!Nsfw) { return RecallTeleportS; } return RecallTeleportN; } } public static string[] Halted { get { if (!Nsfw) { return HaltedS; } return HaltedN; } } public static string[] HelpOnIt { get { if (!Nsfw) { return HelpOnItS; } return HelpOnItN; } } public static string[] HelpUnasked { get { if (!Nsfw) { return HelpUnaskedS; } return HelpUnaskedN; } } public static string[] HelpDone { get { if (!Nsfw) { return HelpDoneS; } return HelpDoneN; } } public static string[] HelpNothing { get { if (!Nsfw) { return HelpNothingS; } return HelpNothingN; } } public static string[] HelpNobody { get { if (!Nsfw) { return HelpNobodyS; } return HelpNobodyN; } } public static string[] SongNotFound { get { if (!Nsfw) { return SongNotFoundS; } return SongNotFoundN; } } public static string[] WeaponHandover { get { if (!Nsfw) { return WeaponHandoverS; } return WeaponHandoverN; } } public static string[] WeaponIgnored { get { if (!Nsfw) { return WeaponIgnoredS; } return WeaponIgnoredN; } } public static string[] StuckItem { get { if (!Nsfw) { return StuckItemS; } return StuckItemN; } } public static string[] SeeAdored { get { if (!Nsfw) { return SeeAdoredS; } return SeeAdoredN; } } public static string[] SeeLiked { get { if (!Nsfw) { return SeeLikedS; } return SeeLikedN; } } public static string[] SeeDisliked { get { if (!Nsfw) { return SeeDislikedS; } return SeeDislikedN; } } public static string[] SeeLoathed { get { if (!Nsfw) { return SeeLoathedS; } return SeeLoathedN; } } public static string[] AloneStart { get { if (!Nsfw) { return AloneStartS; } return AloneStartN; } } public static string[] AloneWarn { get { if (!Nsfw) { return AloneWarnS; } return AloneWarnN; } } public static string[] AloneEnd { get { if (!Nsfw) { return AloneEndS; } return AloneEndN; } } public static string[] AloneSaved { get { if (!Nsfw) { return AloneSavedS; } return AloneSavedN; } } public static string[] LobbyRare { get { if (!Nsfw) { return LobbyRareS; } return LobbyRareN; } } public static string[] LobbyAlone { get { if (!Nsfw) { return LobbyAloneS; } return LobbyAloneN; } } public static string[] LobbyCrowd { get { if (!Nsfw) { return LobbyCrowdS; } return LobbyCrowdN; } } public static string[] LobbyImpatient { get { if (!Nsfw) { return LobbyImpatientS; } return LobbyImpatientN; } } public static string[] LobbyOminous { get { if (!Nsfw) { return LobbyOminousS; } return LobbyOminousN; } } public static bool CanSee(Vector3 botPos, PlayerAvatar p) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)p == (Object)null) { return false; } Vector3 position = ((Component)p).transform.position; if (Vector3.Distance(botPos, position) > 18f) { return false; } int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Linecast(botPos + Vector3.up * 1.2f, position + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1); } catch { return false; } } public static string VisibleName(Vector3 botPos) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GameDirector.instance == (Object)null) { return null; } int blockers = BotSight.Blockers; Vector3 val = botPos + Vector3.up * 1.2f; string result = null; float num = float.MaxValue; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player) || Reflect.Get(player, "isDisabled")) { continue; } Vector3 position = ((Component)player).transform.position; float num2 = Vector3.Distance(botPos, position); if (!(num2 > 18f) && !(num2 >= num) && (blockers == 0 || !Physics.Linecast(val, position + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1))) { string text = SemiFunc.PlayerGetName(player); if (!string.IsNullOrEmpty(text)) { num = num2; result = text; } } } return result; } catch { return null; } } public static void Reset() { LineNext.Clear(); EventNext.Clear(); EventSeen.Clear(); _globalNext = 0f; } public static bool Speak(string key, string[] pool, float chance, float eventGap, Vector3 botPos) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return Speak(key, pool, chance, eventGap, botPos, null); } public static bool Speak(string key, string[] pool, float chance, float eventGap, Vector3 botPos, string aboutName) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (unscaledTime < _globalNext) { return false; } if (BotEspeak.Speaking) { return false; } if (BotHush.Active) { return false; } if (EventSeen.Contains(key)) { if (EventNext.TryGetValue(key, out var value) && unscaledTime < value) { return false; } if (Random.value > chance) { return false; } } if (!Draw(pool, botPos, unscaledTime, out var template, out var spoken, aboutName)) { return false; } EventSeen.Add(key); EventNext[key] = unscaledTime + (Chatty ? (eventGap * 0.4f) : eventGap); LineNext[template] = unscaledTime + (Chatty ? 150f : 300f); _globalNext = unscaledTime + (Chatty ? 9f : 11f); BotSpawner.Say(spoken); return true; } public static bool SpeakLiteral(string line, Vector3 botPos) { if (string.IsNullOrEmpty(line)) { return false; } if (BotHush.Active) { return false; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _globalNext) { return false; } if (BotEspeak.Speaking) { return false; } _globalNext = unscaledTime + (Chatty ? 9f : 11f); BotSpawner.Say(line); return true; } public static bool SpeakExact(string[] pool, Vector3 botPos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return SpeakExact(pool, botPos, null); } public static bool SpeakExact(string[] pool, Vector3 botPos, string aboutName) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (BotHush.Active) { return false; } float unscaledTime = Time.unscaledTime; if (!Draw(pool, botPos, unscaledTime, out var template, out var spoken, aboutName)) { return false; } LineNext[template] = unscaledTime + (Chatty ? 150f : 300f); _globalNext = unscaledTime + (Chatty ? 9f : 11f); BotSpawner.Say(spoken); return true; } private static bool Draw(string[] pool, Vector3 botPos, float now, out string template, out string spoken, string aboutName) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) template = null; spoken = null; if (pool == null || pool.Length == 0) { return false; } string text = null; bool flag = false; int num = Random.Range(0, pool.Length); for (int i = 0; i < pool.Length; i++) { string text2 = pool[(num + i) % pool.Length]; if (!string.IsNullOrEmpty(text2) && (!LineNext.TryGetValue(text2, out var value) || !(now < value))) { if (text2.IndexOf("{name}", StringComparison.Ordinal) < 0) { template = text2; spoken = text2; return true; } if (!flag) { text = ((!string.IsNullOrEmpty(aboutName)) ? aboutName : VisibleName(botPos)); flag = true; } if (!string.IsNullOrEmpty(text)) { template = text2; spoken = text2.Replace("{name}", text); return true; } } } return false; } public static void SpeakYourProblemNow(Vector3 pos, string targetName = null) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) Speak("yourproblem", (!Nsfw) ? YourProblemNowS : YourProblemNowN, 1f, 10f, pos, targetName); } public static void SpeakTooHeavySolo(Vector3 pos) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) Speak("tooheavy", (!Nsfw) ? TooHeavySoloS : TooHeavySoloN, 1f, 15f, pos); } } public static class BotPointing { private const float MaxPoint = 40f; public static bool Aim(PlayerAvatar who, out Vector3 eye, out Vector3 forward) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) eye = Vector3.zero; forward = Vector3.forward; try { if ((Object)(object)who == (Object)null) { return false; } object obj = Reflect.Get(who, "PlayerVisionTarget"); Transform val = ((obj != null) ? Reflect.Get(obj, "VisionTransform") : null); if ((Object)(object)val == (Object)null) { val = ((Component)who).transform; } eye = val.position; forward = val.forward; return true; } catch { return false; } } public static bool FloorSpot(PlayerAvatar who, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { if (!Aim(who, out var eye, out var forward)) { return false; } int blockers = BotSight.Blockers; RaycastHit val = default(RaycastHit); NavMeshHit val2 = default(NavMeshHit); if (NavMesh.SamplePosition(Physics.Raycast(eye, forward, ref val, 40f, blockers, (QueryTriggerInteraction)1) ? ((RaycastHit)(ref val)).point : (eye + forward * 12f), ref val2, 6f, -1)) { spot = ((NavMeshHit)(ref val2)).position; return true; } if (NavMesh.SamplePosition(eye + forward * 6f, ref val2, 6f, -1)) { spot = ((NavMeshHit)(ref val2)).position; return true; } return false; } catch { return false; } } public static PhysGrabObject Object(PlayerAvatar who) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)who == (Object)null) { return null; } PhysGrabber physGrabber = who.physGrabber; if ((Object)(object)physGrabber != (Object)null) { PhysGrabObject val = Reflect.Get(physGrabber, "grabbedPhysGrabObject"); if ((Object)(object)val != (Object)null) { return val; } } if (!Aim(who, out var eye, out var forward)) { return null; } int num = BotSight.Blockers | LayerMask.GetMask(new string[1] { "PhysGrabObject" }); RaycastHit val2 = default(RaycastHit); if (Physics.SphereCast(eye, 0.35f, forward, ref val2, 40f, num, (QueryTriggerInteraction)1)) { PhysGrabObject val3 = (((Object)(object)((RaycastHit)(ref val2)).collider != (Object)null) ? ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent() : null); if ((Object)(object)val3 != (Object)null) { return val3; } } } catch { } return null; } public static PlayerAvatar Person(PlayerAvatar who) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) try { if (!Aim(who, out var eye, out var forward)) { return null; } if ((Object)(object)GameDirector.instance == (Object)null) { return null; } PlayerAvatar result = null; float num = 0.86f; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || player == who || BotSpawner.IsBot(player)) { continue; } Vector3 val = ((Component)player).transform.position + Vector3.up * 0.9f - eye; if (!(((Vector3)(ref val)).magnitude > 40f)) { float num2 = Vector3.Dot(forward, ((Vector3)(ref val)).normalized); if (!(num2 <= num) && !BotSight.Blocked(eye, ((Component)player).transform.position + Vector3.up * 0.9f)) { num = num2; result = player; } } } return result; } catch { return null; } } } public static class BotReflex { private static float _lastReflexAt; private const float ReflexCooldown = 6f; private static readonly string[] Danger = new string[14] { "run", "look out", "watch out", "behind you", "behind me", "it's coming", "its coming", "monster", "it's here", "its here", "incoming", "go go go", "get out", "leg it" }; private static readonly string[] Distress = new string[12] { "help me", "i need help", "help", "i'm down", "im down", "i'm dead", "im dead", "revive me", "res me", "rez me", "get me up", "pick me up" }; private static readonly List _names = new List(); private static float _namesAt; public static bool Handle(string lower, PlayerAvatar speaker, Vector3 botPos) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrEmpty(lower)) { return false; } if (Time.unscaledTime - _lastReflexAt < 6f) { return false; } if (Has(lower, "shh") || Has(lower, "shhh") || Has(lower, "ssh")) { _lastReflexAt = Time.unscaledTime; BotScout.Stop(botPos, announce: false); BotHush.Start(30f); Plugin.Log.LogInfo((object)"[Reflex] Shushed by the room — going quiet."); return true; } if (DangerWord(lower) && EnemyNear(botPos, 20f)) { _lastReflexAt = Time.unscaledTime; BotScout.Stop(botPos, announce: false); BotDanger.ForceHide(10f); BotPersona.Speak("reflexdanger", BotPersona.ReflexDanger, 1f, 20f, botPos, ((Object)(object)speaker != (Object)null) ? SemiFunc.PlayerGetName(speaker) : null); Plugin.Log.LogInfo((object)"[Reflex] Danger call heard with a monster nearby — reacting."); return true; } if (DistressWord(lower) && AnyoneDown()) { _lastReflexAt = Time.unscaledTime; if (BotMedic.OrderRevive(botPos)) { Plugin.Log.LogInfo((object)"[Reflex] Someone called for help while a crewmate is down — going."); return true; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Reflex] " + ex.Message)); } return false; } private static bool DangerWord(string s) { string[] danger = Danger; foreach (string needle in danger) { if (Has(s, needle)) { return true; } } foreach (string item in LiveEnemyNames()) { if (item.Length > 3 && Has(s, item)) { return true; } } return false; } private static bool DistressWord(string s) { string[] distress = Distress; foreach (string needle in distress) { if (Has(s, needle)) { return true; } } return false; } private static List LiveEnemyNames() { if (Time.unscaledTime < _namesAt) { return _names; } _namesAt = Time.unscaledTime + 10f; _names.Clear(); try { if ((Object)(object)EnemyDirector.instance == (Object)null) { return _names; } foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if (!((Object)(object)item == (Object)null)) { string text = BotScout.EnemyDisplayName(item); if (!string.IsNullOrEmpty(text)) { _names.Add(text.ToLowerInvariant()); } } } } catch { } return _names; } private static bool EnemyNear(Vector3 pos, float range) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { if (BotDanger.Level != BotDanger.Threat.Clear) { return true; } if ((Object)(object)EnemyDirector.instance == (Object)null) { return false; } foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if (!((Object)(object)item == (Object)null)) { Enemy val = Reflect.Get(item, "Enemy"); if (!((Object)(object)val == (Object)null) && Vector3.Distance(pos, ((Component)val).transform.position) <= range) { return true; } } } } catch { } return false; } private static bool AnyoneDown() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && Reflect.Get(player, "isDisabled")) { return true; } } } catch { } return false; } private static bool Has(string haystack, string needle) { if (needle.IndexOf(' ') >= 0) { return haystack.IndexOf(needle, StringComparison.Ordinal) >= 0; } int startIndex = 0; while (true) { int num = haystack.IndexOf(needle, startIndex, StringComparison.Ordinal); if (num < 0) { return false; } bool num2 = num == 0 || !char.IsLetter(haystack[num - 1]); int num3 = num + needle.Length; bool flag = num3 >= haystack.Length || !char.IsLetter(haystack[num3]); if (num2 && flag) { break; } startIndex = num + 1; } return true; } } public static class BotRelations { public enum Feeling { Loathes = -2, Dislikes, Neutral, Likes, Adores } private sealed class Entry { public string Name = ""; public float Score; public string Note = ""; } private static readonly Dictionary Book = new Dictionary(); private static readonly HashSet GreetedThisSession = new HashSet(); private static bool _loaded; private static bool _dirty; private static float _saveAt; private static float _lookAt; private const float LikeAt = 2f; private const float LoveAt = 6f; private const float DislikeAt = -2f; private const float LoatheAt = -6f; private static string Path => System.IO.Path.Combine(Paths.ConfigPath, "BotSpike.relations.txt"); public static void Load() { if (_loaded) { return; } _loaded = true; try { if (!File.Exists(Path)) { Plugin.Log.LogInfo((object)"[Relations] No history yet — he's meeting everyone for the first time."); return; } string[] array = File.ReadAllLines(Path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text[0] == '#') { continue; } string[] array2 = text.Split('|'); if (array2.Length >= 3) { string text2 = array2[0].Trim(); if (text2.Length != 0 && float.TryParse(array2[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { Book[text2] = new Entry { Name = array2[1].Trim(), Score = result, Note = ((array2.Length > 3) ? array2[3].Trim() : "") }; } } } Plugin.Log.LogInfo((object)("[Relations] Remembers " + Book.Count + " people.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Relations] Load failed: " + ex.Message)); } } public static void Save(bool force = false) { if (!_dirty && !force) { return; } try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BotSpike — who " + BotSpawner.ChosenName + " likes."); stringBuilder.AppendLine("# steamID | name | score | note"); stringBuilder.AppendLine("# score: negative is dislike, positive is like. Roughly -10 to +10."); stringBuilder.AppendLine("# Safe to edit by hand."); foreach (KeyValuePair item in Book) { stringBuilder.AppendLine(item.Key + "|" + item.Value.Name + "|" + item.Value.Score.ToString("F2", CultureInfo.InvariantCulture) + "|" + item.Value.Note); } File.WriteAllText(Path, stringBuilder.ToString()); _dirty = false; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Relations] Save failed: " + ex.Message)); } } public static float ScoreOf(PlayerAvatar p) { string text = IdOf(p); if (text == null || !Book.TryGetValue(text, out var value)) { return 0f; } return value.Score; } public static Feeling FeelingAbout(PlayerAvatar p) { float num = ScoreOf(p); if (num <= -6f) { return Feeling.Loathes; } if (num <= -2f) { return Feeling.Dislikes; } if (num >= 6f) { return Feeling.Adores; } if (num >= 2f) { return Feeling.Likes; } return Feeling.Neutral; } public static void Adjust(PlayerAvatar p, float delta, string why) { try { string text = IdOf(p); if (text != null && !Mathf.Approximately(delta, 0f)) { if (!Book.TryGetValue(text, out var value)) { value = new Entry(); Book[text] = value; } value.Name = SemiFunc.PlayerGetName(p) ?? value.Name; value.Score = Mathf.Clamp(value.Score + delta, -12f, 12f); if (!string.IsNullOrEmpty(why)) { value.Note = why; } _dirty = true; _saveAt = Time.unscaledTime + 10f; } } catch { } } public static void CommitRun() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { float num = BotOpinions.FavourOf(player); if (!(Mathf.Abs(num) < 0.5f)) { Adjust(player, Mathf.Clamp(num * 0.15f, -1.5f, 1.5f), "run on " + DateTime.Now.ToString("yyyy-MM-dd")); } } } Save(force: true); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Relations] CommitRun: " + ex.Message)); } } private static string IdOf(PlayerAvatar p) { try { if ((Object)(object)p == (Object)null || BotSpawner.IsBot(p)) { return null; } string text = Reflect.Get(p, "steamID"); return string.IsNullOrEmpty(text) ? null : text; } catch { return null; } } public static void NewSession() { GreetedThisSession.Clear(); _lookAt = 0f; } public static void TickFirstSight(Vector3 botPos) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) try { if (Time.unscaledTime < _lookAt) { return; } _lookAt = Time.unscaledTime + 1.5f; if (_dirty && Time.unscaledTime > _saveAt) { Save(); } if ((Object)(object)GameDirector.instance == (Object)null) { return; } bool flag = SemiFunc.RunIsLobbyMenu() || SemiFunc.RunIsLobby(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } string text = IdOf(player); if (text == null || GreetedThisSession.Contains(text)) { continue; } Feeling feeling = FeelingAbout(player); if (feeling == Feeling.Neutral) { GreetedThisSession.Add(text); continue; } Vector3 position = ((Component)player).transform.position; float num = (flag ? 14f : 18f); if (Vector3.Distance(botPos, position) > num || (!flag && !CanSee(botPos, position))) { continue; } GreetedThisSession.Add(text); BotPersona.SpeakExact(feeling switch { Feeling.Dislikes => BotPersona.SeeDisliked, Feeling.Likes => BotPersona.SeeLiked, Feeling.Adores => BotPersona.SeeAdored, _ => BotPersona.SeeLoathed, }, botPos, SemiFunc.PlayerGetName(player)); Plugin.Log.LogInfo((object)("[Relations] First sight of " + SemiFunc.PlayerGetName(player) + " this session — " + feeling.ToString() + " (score " + ScoreOf(player).ToString("F1") + ").")); break; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Relations] FirstSight: " + ex.Message)); } } private static bool CanSee(Vector3 from, Vector3 target) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Linecast(from + Vector3.up * 1f, target + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1); } catch { return true; } } } public static class BotRevival { private static float _pollAt; private static float _announcedAt; private static float _downSince = -1f; private const float MinimumDownTime = 12f; private static readonly Dictionary _lastState = new Dictionary(); public static bool AwaitingReturn { get; private set; } public static void Reset() { AwaitingReturn = false; _pollAt = 0f; _downSince = -1f; _lastState.Clear(); } public static void Tick(PlayerAvatar bot, float dt) { if ((Object)(object)bot == (Object)null) { AwaitingReturn = false; return; } if (!Reflect.Get(bot, "isDisabled") && !Reflect.Get(bot, "deadSet")) { if (AwaitingReturn) { AwaitingReturn = false; Plugin.Log.LogInfo((object)"[Revival] He's up again."); } return; } if (!AwaitingReturn) { AwaitingReturn = true; _downSince = Time.unscaledTime; JustActivated(); Plugin.Log.LogWarning((object)("[Revival] " + BotSpawner.ChosenName + " is down — he'll come back at the next extraction point the crew opens.")); } if (Time.unscaledTime < _pollAt) { return; } _pollAt = Time.unscaledTime + 0.2f; ExtractionPoint val = JustActivated(); if (!((Object)(object)val == (Object)null)) { if (Time.unscaledTime - _downSince < 12f) { Plugin.Log.LogInfo((object)("[Revival] A chute opened, but he's only been down " + (Time.unscaledTime - _downSince).ToString("F1") + "s — too soon to bring him back.")); } else { BringHimBack(bot, val); } } } private static ExtractionPoint JustActivated() { try { ExtractionPoint[] array = Object.FindObjectsOfType(); foreach (ExtractionPoint val in array) { if ((Object)(object)val == (Object)null) { continue; } int instanceID = ((Object)val).GetInstanceID(); object obj = Reflect.Get(val, "currentState"); if (obj != null) { int num = (int)obj; int value; bool num2 = _lastState.TryGetValue(instanceID, out value); _lastState[instanceID] = num; if (num2 && value != num && num == 2) { return val; } } } } catch { } return null; } private static void BringHimBack(PlayerAvatar bot, ExtractionPoint at) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = BotChute.StandingSpot(at); if (val == Vector3.zero) { Plugin.Log.LogWarning((object)"[Revival] Couldn't find a standable spot in that extraction point — leaving him down until the next one."); return; } object obj = Reflect.Get(bot, "playerDeathHead"); Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[Revival] No death head — cannot revive him. (EnsureBodyParts should have built one.)"); return; } PhysGrabObject val3 = Reflect.Get(val2, "physGrabObject"); if ((Object)(object)val3 != (Object)null) { val3.Teleport(val, Quaternion.identity); if ((Object)(object)val3.rb != (Object)null) { val3.rb.velocity = Vector3.zero; val3.rb.angularVelocity = Vector3.zero; } } else { val2.transform.position = val; } Plugin.Log.LogWarning((object)("[Revival] REVIVING HIM at extraction " + ((Object)((Component)at).gameObject).name + " after " + (Time.unscaledTime - _downSince).ToString("F0") + "s down.")); bot.Revive(false); AwaitingReturn = false; BotMovement.Reset(); BotHands.Reset(); BotScout.Reset(); Plugin.Log.LogInfo((object)"[Revival] The chute came up and he was standing in it."); if (Time.unscaledTime - _announcedAt > 5f) { _announcedAt = Time.unscaledTime; BotPersona.SpeakExact(BotPersona.Returned, val); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Revival] " + ex.Message)); } } } public static class BotScout { public enum Mode { Off, Patrolling, ComingBack } private static Vector3 _waypoint; private static bool _haveWaypoint; private static float _repickTimer; private static float _scanTimer; private static float _patrolTimeLeft; private static float _reportTimer; private static Vector3 _heading; private static bool _hasHeading; private static readonly HashSet _reportedEnemies = new HashSet(); private static readonly HashSet _reportedPlayers = new HashSet(); private static int _foundCount; private const float PatrolSeconds = 45f; private const float SightRange = 22f; private const float ScoutSpeed = 0.55f; private const float MinHopDistance = 12f; private const float MaxHopDistance = 32f; public static Mode Current { get; private set; } = Mode.Off; public static bool Active => Current != Mode.Off; public static void Reset() { Current = Mode.Off; _haveWaypoint = false; _reportedEnemies.Clear(); _reportedPlayers.Clear(); _foundCount = 0; _patrolTimeLeft = 0f; _hasHeading = false; } public static void Begin(Vector3 botPos, PlayerAvatar asker) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) _heading = Vector3.zero; _hasHeading = false; if ((Object)(object)asker != (Object)null) { Vector3 forward = ((Component)asker).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f) { _heading = ((Vector3)(ref forward)).normalized; _hasHeading = true; } } Begin(botPos); } public static void Begin(Vector3 botPos) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) Current = Mode.Patrolling; _haveWaypoint = false; _repickTimer = 0f; _patrolTimeLeft = 45f; _reportedEnemies.Clear(); _reportedPlayers.Clear(); _foundCount = 0; BotPersona.Speak("scoutgo", BotPersona.ScoutGoing, 1f, 0f, botPos); Plugin.Log.LogInfo((object)"[Scout] Heading out to look around."); } public static void Stop(Vector3 botPos, bool announce) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Current != Mode.Off) { Current = Mode.Off; _haveWaypoint = false; if (announce) { Report(botPos); } Plugin.Log.LogInfo((object)"[Scout] Sortie over."); } } public static void Report(Vector3 botPos) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (_foundCount == 0) { BotPersona.Speak("scoutclear", BotPersona.ScoutClear, 1f, 0f, botPos); } else { BotPersona.Speak("scoutdone", BotPersona.ScoutDone, 1f, 0f, botPos); } } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (!Active || (Object)(object)bot == (Object)null) { return false; } Vector3 position = bot.transform.position; BotMovement.SpeedScale = 0.55f; BotMovement.ForceCrouch = true; _scanTimer -= dt; if (_scanTimer <= 0f) { _scanTimer = 0.4f; LookAround(position); } if (Current == Mode.Patrolling) { _patrolTimeLeft -= dt; if (_patrolTimeLeft <= 0f) { Current = Mode.ComingBack; Report(position); return true; } _repickTimer -= dt; if (!_haveWaypoint || _repickTimer <= 0f || Vector3.Distance(position, _waypoint) < 2.5f) { _repickTimer = 12f; _haveWaypoint = PickFarPoint(position, out _waypoint); } if (_haveWaypoint) { BotMovement.SetDestination(_waypoint); } return true; } if (!BotBuddy.AnchorPosition(out var pos)) { Stop(position, announce: false); return false; } if (Vector3.Distance(position, pos) <= 4f) { BotMovement.ForceCrouch = false; BotMovement.SpeedScale = 1f; Stop(position, announce: false); return false; } BotMovement.SetDestination(pos); return true; } private static bool PickFarPoint(Vector3 from, out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) spot = from; Vector3 val2 = default(Vector3); Vector3 val3 = default(Vector3); NavMeshHit val5 = default(NavMeshHit); for (int i = 0; i < 20; i++) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(12f, 32f); ((Vector3)(ref val2))..ctor(val.x, Random.Range(-4f, 4f), val.y); if (_hasHeading) { ((Vector3)(ref val3))..ctor(val2.x, 0f, val2.z); float magnitude = ((Vector3)(ref val3)).magnitude; Vector3 val4 = Vector3.Slerp(((Vector3)(ref val3)).normalized, _heading, 0.65f) * magnitude; ((Vector3)(ref val2))..ctor(val4.x, val2.y, val4.z); } if (NavMesh.SamplePosition(from + val2, ref val5, 6f, -1) && !(Vector3.Distance(from, ((NavMeshHit)(ref val5)).position) < 7.2000003f)) { spot = ((NavMeshHit)(ref val5)).position; return true; } } return false; } private static void LookAround(Vector3 pos) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) if (_reportTimer > 0f) { _reportTimer -= 0.4f; return; } try { if ((Object)(object)EnemyDirector.instance != (Object)null) { foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if ((Object)(object)item == (Object)null) { continue; } Enemy val = Reflect.Get(item, "Enemy"); if ((Object)(object)val == (Object)null) { continue; } Vector3 position = ((Component)val).transform.position; if (!(Vector3.Distance(pos, position) > 22f) && HasLineOfSight(pos, position)) { string text = EnemyDisplayName(item); if (_reportedEnemies.Add(text)) { _foundCount++; _reportTimer = 2f; BotFacts.NoteEnemy(text); BotPersona.Speak("scoutenemy", BotPersona.ScoutEnemy, 1f, 0f, pos, text); Plugin.Log.LogInfo((object)("[Scout] Spotted a " + text + ".")); return; } } } } } catch { } try { if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { Vector3 position2 = ((Component)player).transform.position; if (!(Vector3.Distance(pos, position2) > 22f) && HasLineOfSight(pos, position2) && _reportedPlayers.Add(((Object)player).GetInstanceID())) { bool flag = Reflect.Get(player, "isDisabled"); _foundCount++; _reportTimer = 2f; BotPersona.Speak(flag ? "scoutbody" : "scoutplayer", flag ? BotPersona.ScoutBody : BotPersona.ScoutPlayer, 1f, 0f, pos, SemiFunc.PlayerGetName(player)); break; } } } } catch { } } public static string EnemyDisplayName(EnemyParent ep) { try { object obj = Reflect.Get(ep, "enemyNameLocalized"); if (obj != null) { MethodInfo method = obj.GetType().GetMethod("GetLocalizedString", BindingFlags.Instance | BindingFlags.Public); if (method != null) { string text = method.Invoke(obj, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } string text2 = Reflect.Get(ep, "enemyName"); if (!string.IsNullOrEmpty(text2)) { return text2; } } catch { } return "something"; } private static bool HasLineOfSight(Vector3 from, Vector3 target) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Linecast(from + Vector3.up * 1f, target + Vector3.up * 0.8f, blockers, (QueryTriggerInteraction)1); } catch { return true; } } } public static class BotSelfAudit { public static void Run() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Invalid comparison between Unknown and I4 //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("================ BOT SPIKE SELF-AUDIT ================"); try { PlayerAvatar botAvatar = BotSpawner.BotAvatar; if ((Object)(object)botAvatar == (Object)null) { stringBuilder.AppendLine(" no bot in play — spawn him first (F9)"); Dump(stringBuilder); return; } PhotonView component = ((Component)botAvatar).GetComponent(); Vector3 position = ((Component)botAvatar).transform.position; Vector3 val = (((Object)(object)PlayerController.instance != (Object)null) ? ((Component)PlayerController.instance).transform.position : Vector3.zero); float num = Vector3.Distance(position, val); stringBuilder.AppendLine(" his real position : " + ((Vector3)(ref position)).ToString("F2")); stringBuilder.AppendLine(" vanilla would send : " + ((Vector3)(ref val)).ToString("F2") + " (this is YOUR player)"); stringBuilder.AppendLine(Line(num > 1.5f, "serialize fix ACTIVE", "positions coincidentally equal — walk away from him and re-run") + " drift " + num.ToString("F1") + "m"); object obj = Reflect.Get(botAvatar, "PlayerVisionTarget"); object obj2 = ((obj != null) ? Reflect.Get(obj, "VisionTransform") : null); stringBuilder.AppendLine(Line(obj != null, "PlayerVisionTarget present", "PlayerVisionTarget NULL — Update throws before it copies the position")); stringBuilder.AppendLine(Line(obj2 != null, "VisionTransform present", "VisionTransform NULL — this is what pinned him at (0,0,0) on clients")); object obj3 = Reflect.Get(botAvatar, "voiceChat"); Object val2 = (Object)((obj3 is Object) ? obj3 : null); bool flag = val2 != null && val2 != (Object)null; object obj4 = (flag ? Reflect.Get(obj3, "ttsVoice") : null); stringBuilder.AppendLine(Line(flag, "voiceChat bound", "voiceChat NOT bound — silent everywhere")); stringBuilder.AppendLine(Line(obj4 != null, "ttsVoice built", "ttsVoice MISSING — subtitles but no sound")); if (flag) { Component val3 = (Component)((obj3 is Component) ? obj3 : null); float num2 = (((Object)(object)val3 != (Object)null) ? Vector3.Distance(val3.transform.position, position) : (-1f)); stringBuilder.AppendLine(Line(num2 >= 0f && num2 < 3f, "voice is on his body", "voice object is " + num2.ToString("F1") + "m from him — distance attenuation will eat his lines")); } bool ok = (Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList.Contains(botAvatar); stringBuilder.AppendLine(Line(ok, "in GameDirector.PlayerList", "NOT in PlayerList — enemies, tumbles and game-over all resolve through it")); GameObject gameObject = ((Component)((Component)botAvatar).transform.root).gameObject; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = LayerMask.NameToLayer("PlayerVisualsLocal"); Renderer[] componentsInChildren = gameObject.GetComponentsInChildren(true); foreach (Renderer val4 in componentsInChildren) { num3++; if (!val4.enabled) { num5++; } else if ((int)val4.shadowCastingMode == 3) { num6++; } else if (num8 >= 0 && ((Component)val4).gameObject.layer == num8) { num7++; } else { num4++; } } stringBuilder.AppendLine(Line(num4 > 0, "renderers drawable (" + num4 + "/" + num3 + ")", "NOTHING DRAWABLE of " + num3 + " — disabled:" + num5 + " shadows-only:" + num6 + " on the hidden layer:" + num7)); object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null); bool ok2 = array != null && array.Length != 0 && "BOTSPIKE_BOT".Equals(array[0] as string); string text = ((array != null && array.Length > 2) ? (array[2] as string) : "(none)"); stringBuilder.AppendLine(Line(ok2, "instantiation tag present", "NO TAG — every client deletes him on the duplicate-owner guard")); stringBuilder.AppendLine(Line(text == Reflect.Get(botAvatar, "steamID"), "name synced ('" + text + "')", "name mismatch: sent '" + text + "' but he is '" + Reflect.Get(botAvatar, "steamID") + "' — tumbles will not find him")); Scene scene = ((Component)((Component)botAvatar).transform.root).gameObject.scene; string name = ((Scene)(ref scene)).name; stringBuilder.AppendLine(Line(name == "DontDestroyOnLoad", "persistent across levels", "in scene '" + name + "' — he will be destroyed at the next level load")); stringBuilder.AppendLine(" ---- your side (for reference) ----"); stringBuilder.AppendLine(" master client : " + PhotonNetwork.IsMasterClient + " in room: " + PhotonNetwork.InRoom + " players: " + (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : (-1))); } catch (Exception ex) { stringBuilder.AppendLine(" audit threw: " + ex); } Dump(stringBuilder); } private static string Line(bool ok, string good, string bad) { return (ok ? " [ ok ] " : " [ BAD] ") + (ok ? good : bad); } private static void Dump(StringBuilder sb) { sb.Append("======================================================"); Plugin.Log.LogWarning((object)sb.ToString()); } } public static class BotSelfHear { private static PlayerVoiceChat _mine; private static float _findAt; private static bool _on; private static float _restoreVolume = -1f; private static bool _warned; private static float _reportAt; public static bool Active => _on; public static void Reset() { _on = false; _mine = null; _restoreVolume = -1f; } public static void Tick(bool want) { try { PlayerVoiceChat val = Mine(); if ((Object)(object)val == (Object)null) { if (want && !_warned) { _warned = true; Plugin.Log.LogWarning((object)"[SelfHear] Couldn't find your own PlayerVoiceChat — he won't be able to hear you on the intercom."); } } else if (want) { val.OverrideHearSelf(0f, 0.35f); if (!_on) { _on = true; Plugin.Log.LogInfo((object)"[SelfHear] Looping your voice back locally so he can hear you. This is the game's own OverrideHearSelf — no microphone is opened."); } AudioSource val2 = Reflect.Get(val, "audioSource"); if ((Object)(object)val2 != (Object)null && !val2.mute) { val2.mute = true; } Report(val); } else if (_on) { _on = false; AudioSource val3 = Reflect.Get(val, "audioSource"); if ((Object)(object)val3 != (Object)null && val3.mute) { val3.mute = false; } _restoreVolume = -1f; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SelfHear] " + ex.Message)); } } private static void Report(PlayerVoiceChat vc) { try { if (!(Time.unscaledTime < _reportAt)) { _reportAt = Time.unscaledTime + 1f; AudioSource val = Reflect.Get(vc, "audioSource"); bool flag = Reflect.Get(vc, "isTalking"); float num = Reflect.Get(vc, "clipLoudness"); Plugin.Log.LogWarning((object)("[SelfHear] playing=" + ((Object)(object)val != (Object)null && val.isPlaying) + " clip=" + (((Object)(object)val != (Object)null && (Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name : "NULL") + " isTalking=" + flag + " loudness=" + num.ToString("F4") + " (the tap needs playing=True AND isTalking=True to read you)")); } } catch { } } private static PlayerVoiceChat Mine() { if ((Object)(object)_mine != (Object)null) { return _mine; } if (Time.unscaledTime < _findAt) { return null; } _findAt = Time.unscaledTime + 2f; try { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return null; } object obj = Reflect.Get(instance, "voiceChat"); _mine = (PlayerVoiceChat)((obj is PlayerVoiceChat) ? obj : null); } catch { } return _mine; } } public static class BotSight { private static int _mask = -1; public static int Blockers { get { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (_mask >= 0) { return _mask; } try { int num = LayerMask.op_Implicit(SemiFunc.LayerMaskGetVisionObstruct()); int num2 = LayerMask.NameToLayer("Player"); if (num2 >= 0) { num &= ~(1 << num2); } if (num != 0) { return _mask = num; } } catch { } return _mask = LayerMask.GetMask(new string[5] { "Default", "StaticGrabObject", "PhysGrabObject", "PhysGrabObjectHinge", "PhysGrabObjectCart" }); } } public static bool Blocked(Vector3 from, Vector3 to) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { int blockers = Blockers; if (blockers == 0) { return false; } return Physics.Linecast(from, to, blockers, (QueryTriggerInteraction)1); } catch { return false; } } public static bool CanSee(Vector3 from, Vector3 to, float fromEye = 1f, float toEye = 0.8f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) return !Blocked(from + Vector3.up * fromEye, to + Vector3.up * toEye); } } public static class BotSocial { private sealed class TalkState { public bool WasTalking; public float StartedAt; } private static readonly Dictionary Talkers = new Dictionary(); private static float _replyAt; private static bool _replyQueued; private static float _lastSpokeAt = -99f; private static float _pollTimer; private const float Earshot = 16f; private const float MinTalkSeconds = 0.7f; private static float _wanderIn; private static float _idleChatIn = 12f; private static float _lobbySeconds; private static Vector3 _anchor; private static bool _haveAnchor; private static float _suppressReplyUntil; public static void Reset() { Talkers.Clear(); _replyQueued = false; _haveAnchor = false; _wanderIn = 0f; _idleChatIn = Random.Range(8f, 18f) * BotPersona.LobbyChatterScale; _lobbySeconds = 0f; } public static void TickVoice(Vector3 botPos, float dt) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; _pollTimer -= dt; if (_pollTimer <= 0f) { _pollTimer = 0.15f; bool flag = ScanTalkers(botPos, unscaledTime); if (_replyQueued && flag) { _replyAt = Mathf.Max(_replyAt, unscaledTime + 0.5f); } } if (_replyQueued && unscaledTime >= _replyAt) { _replyQueued = false; if (unscaledTime < _suppressReplyUntil) { Plugin.Log.LogInfo((object)"[Social] Held back a small-talk reply — that one was addressed to him and deserves a real answer."); } else if (BotPersona.Speak("reply", BotPersona.Reply, 0.9f, 18f, botPos)) { _lastSpokeAt = unscaledTime; } } } public static void ExpectRealAnswer(float seconds = 4f) { _suppressReplyUntil = Time.unscaledTime + seconds; _replyQueued = false; } private static bool ScanTalkers(Vector3 botPos, float now) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) bool result = false; try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } object obj = Reflect.Get(player, "voiceChat"); if (obj == null) { continue; } bool flag = Reflect.Get(obj, "isTalking"); if (flag) { result = true; } int instanceID = ((Object)player).GetInstanceID(); if (!Talkers.TryGetValue(instanceID, out var value)) { value = (Talkers[instanceID] = new TalkState()); } if (flag && !value.WasTalking) { value.StartedAt = now; } else if (!flag && value.WasTalking) { float num = now - value.StartedAt; bool flag2 = Vector3.Distance(botPos, ((Component)player).transform.position) <= 16f; if (num >= 0.7f && flag2 && !_replyQueued) { _replyQueued = true; _replyAt = now + Random.Range(0.5f, 1.5f); } } value.WasTalking = flag; } } catch { } return result; } public static void TickLobby(GameObject bot, PlayerAvatar avatar, float dt) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00af: 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null) { return; } BotMovement.DirectMode = true; _lobbySeconds += dt; Vector3 position = bot.transform.position; if (!_haveAnchor) { _anchor = position; _haveAnchor = true; } _wanderIn -= dt; if (_wanderIn <= 0f && !BotMovement.HasGoal) { _wanderIn = Random.Range(4f, 11f); Vector3 val = _anchor; if (NearestHumanPos(position, out var result)) { val = result; } for (int i = 0; i < 8; i++) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(1.8f, 3.5f); Vector3 val3 = val + new Vector3(val2.x, 0f, val2.y); if (HasFloor(val3)) { BotMovement.SetDestination(val3); break; } } } if (!BotMovement.HasGoal && NearestHumanPos(position, out var result2)) { BotMovement.FaceTowards(result2); } BotMovement.Tick(bot, avatar, dt); _idleChatIn -= dt; if (_idleChatIn <= 0f) { _idleChatIn = Random.Range(14f, 32f) * BotPersona.LobbyChatterScale; _ = Time.unscaledTime; if (!_replyQueued && SayLobbyLine(position)) { _lastSpokeAt = Time.unscaledTime; } } } private static bool SayLobbyLine(Vector3 pos) { //IL_006f: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) _lobbySeconds += Time.deltaTime * 0f; int num = CountHumans(); if (_lobbySeconds > 90f && Random.value < 0.45f) { return BotPersona.Speak("lobbywait", BotPersona.LobbyImpatient, 1f, 40f, pos); } if (Random.value < 0.14f) { return BotPersona.Speak("lobbyrare", BotPersona.LobbyRare, 1f, 45f, pos); } if (Random.value < 0.05f) { return BotPersona.Speak("lobbyominous", BotPersona.LobbyOminous, 1f, 120f, pos); } if (num <= 1 && Random.value < 0.5f) { return BotPersona.Speak("lobbyalone", BotPersona.LobbyAlone, 1f, 60f, pos); } if (num >= 3 && Random.value < 0.5f) { return BotPersona.Speak("lobbycrowd", BotPersona.LobbyCrowd, 1f, 60f, pos); } return BotPersona.Speak("lobbyidle", BotPersona.LobbyIdle, 0.8f, 45f, pos); } private static int CountHumans() { int num = 0; try { if ((Object)(object)GameDirector.instance == (Object)null) { return 0; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null && !BotSpawner.IsBot(player)) { num++; } } } catch { } return num; } private static bool HasFloor(Vector3 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) try { return Physics.Raycast(point + Vector3.up * 0.6f, Vector3.down, 2.6f, LayerMask.GetMask(new string[3] { "Default", "StaticGrabObject", "PlayerOnlyCollision" }), (QueryTriggerInteraction)1); } catch { return true; } } private static bool NearestHumanPos(Vector3 from, out Vector3 result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) result = from; float num = float.MaxValue; try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { Vector3 position = ((Component)player).transform.position; float num2 = Vector3.Distance(from, position); if (num2 < num) { num = num2; result = position; } } } } catch { } return num < float.MaxValue; } } public static class BotSpawner { private static readonly HashSet Bots = new HashSet(); private static bool _expectBot; private static GameObject _bot; private static float _report; private static int _reportsLeft; private static Vector3 _driveTarget; private static bool _driving; private static bool _wasDown; private static float _visibilityAt; private static int _botSerial; private static GameObject _voice; private static float _autoRetry; private static float _sweepTimer; private static float _levelSettle; private static float _probeAt; private const float FollowNear = 2.2f; private const float FollowFar = 4.5f; private static float _grabProbeAt; private static readonly HashSet BotVoices = new HashSet(); private static float _voiceProbeAt; private const string BotTag = "BOTSPIKE_BOT"; private static readonly List _provisional = new List(); private static float _tumbleUpdateGripeAt; private static float _tumbleGripeAt; private static float _cosmeticGripeAt; private static float _clientUpkeepAt; private static float _beaconAt; private static bool ExploringBot; private static float _hideGripeAt; private static float _ttsAuditAt; private static float _ttsAuditDue; private static readonly string[] Lines = new string[5] { "Hello, I am Bertram.", "I found something over here.", "Watch out behind you.", "This one looks expensive.", "I am definitely a real person." }; private static float _voiceCheckAt; private static float _voiceWarnAt; private static bool _voiceReadyAnnounced; private static readonly HashSet BotCameras = new HashSet(); private static float _armProbeAt; private static float _serializeGripeAt; private static float _eyeGripeAt; private static bool _loggedUpdateError; private static float _gumballGripeAt; public static string ChosenName { get { if (Plugin.CfgBotName == null || string.IsNullOrEmpty(Plugin.CfgBotName.Value)) { return "Bertram"; } return Plugin.CfgBotName.Value.Trim(); } } public static int Deaths { get; internal set; } public static GameObject BotObject => _bot; public static PlayerAvatar BotAvatar { get { if ((Object)(object)_bot == (Object)null) { return null; } return _bot.GetComponent() ?? _bot.GetComponentInChildren(true); } } private static string BotName { get { if (Deaths > 0) { return ChosenName + " " + (Deaths + 1); } return ChosenName; } } public static bool HasBot => (Object)(object)_bot != (Object)null; public static Vector3 BotPosition { get { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_bot != (Object)null)) { return Vector3.zero; } return _bot.transform.position; } } public static string VoiceOffset { get { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bot == (Object)null) { return "-"; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); object obj = (((Object)(object)val != (Object)null) ? Reflect.Get(val, "voiceChat") : null); Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 == (Object)null) { return "no voice"; } return Vector3.Distance(val2.transform.position, _bot.transform.position).ToString("F1") + "m"; } } public static string BotHealth { get { if ((Object)(object)_bot == (Object)null) { return "-"; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); PlayerHealth val2 = (((Object)(object)val != (Object)null) ? val.playerHealth : null); if ((Object)(object)val2 == (Object)null) { return "?"; } return Reflect.Get(val2, "health") + "/" + Reflect.Get(val2, "maxHealth"); } } public static int BotCount { get { if (Bots.Count == 0) { return 0; } List list = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList : null); int num = 0; foreach (PlayerAvatar bot in Bots) { if (!((Object)(object)bot == (Object)null) && (list == null || list.Contains(bot))) { num++; } } return num; } } public static string TtsState { get { if ((Object)(object)_bot == (Object)null) { return "-"; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); object obj = (((Object)(object)val != (Object)null) ? Reflect.Get(val, "voiceChat") : null); if (obj == null) { return "no voiceChat"; } if (Reflect.Get(obj, "ttsVoice") == null) { return "MISSING"; } AudioSource val2 = Reflect.Get(obj, "ttsAudioSource"); if ((Object)(object)val2 == (Object)null) { return "no source"; } if ((Object)(object)val2.outputAudioMixerGroup == (Object)null) { return "UNROUTED(silent)"; } return "ok vol=" + val2.volume.ToString("F2"); } } public static bool IsBot(PlayerAvatar a) { if ((Object)(object)a != (Object)null) { return Bots.Contains(a); } return false; } public static void TeleportBots(Vector3 to) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_bot == (Object)null)) { DriveTo(to, Quaternion.Euler(0f, _bot.transform.eulerAngles.y, 0f)); } } [HarmonyPatch(typeof(SteamManager), "OpenProfile")] [HarmonyPrefix] public static bool OpenProfilePrefix(PlayerAvatar _playerAvatar) { if (!IsBot(_playerAvatar)) { return true; } Plugin.Log.LogInfo((object)"[Spike] Ignoring a Steam profile click on the bot — he hasn't got one."); return false; } [HarmonyPatch(typeof(PlayerAvatar), "OutroStart")] [HarmonyPostfix] public static void OutroStartPostfix(PlayerAvatar __instance) { if (IsBot(__instance)) { CompleteOutro(__instance); Plugin.Log.LogInfo((object)"[Outro] Bot outro completed instantly — he has no screen to fade."); } } private static void CompleteOutro(PlayerAvatar a) { try { Reflect.Set(a, "outroDone", true); PhotonView component = ((Component)a).GetComponent(); if ((Object)(object)component != (Object)null && PhotonNetwork.InRoom) { component.RPC("OutroDoneRPC", (RpcTarget)0, Array.Empty()); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Outro] " + ex.Message)); } } public static void CompleteOutros() { foreach (PlayerAvatar bot in Bots) { if (!((Object)(object)bot == (Object)null) && !Reflect.Get(bot, "outroDone")) { CompleteOutro(bot); Plugin.Log.LogWarning((object)"[Outro] Failsafe — a bot's outro was still pending during a restart. Completed it."); } } } [HarmonyPatch(typeof(PhysGrabber), "Update")] [HarmonyFinalizer] public static Exception PhysGrabberUpdateFinalizer(PhysGrabber __instance, Exception __exception) { if (__exception == null) { return null; } PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.playerAvatar : null); if ((Object)(object)val == (Object)null || !IsBot(val)) { return __exception; } if (Time.unscaledTime > _grabProbeAt) { _grabProbeAt = Time.unscaledTime + 30f; try { List list = new List(); if (Reflect.Get(val, "localCamera") == null) { list.Add("playerAvatar.localCamera"); } string[] array = new string[7] { "grabberAudioTransform", "physGrabBeamComponent", "physGrabPoint", "physGrabPointPuller", "physGrabPointPlane", "grabbedObjectTransform", "physGrabBeam" }; foreach (string text in array) { if (Reflect.Get(__instance, text) == null) { list.Add(text); } } Plugin.Log.LogWarning((object)("[Probe] PhysGrabber.Update NRE (bot, swallowed) — null: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "none of the usual suspects") + " | grabbed=" + __instance.grabbed)); } catch { } } return null; } public static bool IsBotVoice(PlayerVoiceChat v) { if ((Object)(object)v != (Object)null) { return BotVoices.Contains(v); } return false; } private static void AutoWireVoice(PlayerVoiceChat v) { try { int num = 0; FieldInfo[] fields = typeof(PlayerVoiceChat).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(Component).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(v) == null) { Component val = ((Component)v).GetComponent(fieldInfo.FieldType) ?? ((Component)v).GetComponentInChildren(fieldInfo.FieldType, true); if ((Object)(object)val != (Object)null) { fieldInfo.SetValue(v, val); num++; } } } if (num > 0) { Plugin.Log.LogInfo((object)("[Voice] Auto-wired " + num + " component field(s) the bypassed Awake/Start would have fetched.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Voice] AutoWire: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerVoiceChat), "Update")] [HarmonyFinalizer] public static Exception VoiceUpdateFinalizer(PlayerVoiceChat __instance, Exception __exception) { return FilterVoiceException(__instance, __exception); } [HarmonyPatch(typeof(PlayerVoiceChat), "LateUpdate")] [HarmonyFinalizer] public static Exception VoiceLateUpdateFinalizer(PlayerVoiceChat __instance, Exception __exception) { return FilterVoiceException(__instance, __exception); } private static Exception FilterVoiceException(PlayerVoiceChat v, Exception e) { if (e == null) { return null; } if (!IsBotVoice(v)) { return e; } if (Time.unscaledTime > _voiceProbeAt) { _voiceProbeAt = Time.unscaledTime + 30f; try { List list = new List(); FieldInfo[] fields = typeof(PlayerVoiceChat).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.FieldType.IsValueType && fieldInfo.GetValue(v) == null) { list.Add(fieldInfo.Name); } } Plugin.Log.LogWarning((object)("[Probe] Bot voice per-frame exception (swallowed): " + e.Message + " | null fields: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "none"))); } catch { } } return null; } private static string TaggedName(Component c) { try { PhotonView component = c.GetComponent(); object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null); if (array != null && array.Length > 2) { string text = array[2] as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } return BotName; } private static bool HasBotTag(Component c) { try { PhotonView component = c.GetComponent(); object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null); return array != null && array.Length != 0 && "BOTSPIKE_BOT".Equals(array[0] as string); } catch { return false; } } private static bool WouldBeDestroyedAsDuplicate(PlayerAvatar a) { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } PhotonView component = ((Component)a).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)a)) { PhotonView component2 = ((Component)player).GetComponent(); if ((Object)(object)component2 != (Object)null && component.Owner == component2.Owner) { return true; } } } } catch { } return false; } [HarmonyPatch(typeof(PlayerTumble), "Update")] [HarmonyFinalizer] public static Exception TumbleUpdateFinalizer(PlayerTumble __instance, Exception __exception) { if (__exception == null) { return null; } try { object obj = Reflect.Get(__instance, "playerAvatar"); if ((Object)((obj is Component) ? obj : null) != (Object)null) { return __exception; } if (Time.unscaledTime > _tumbleUpdateGripeAt) { _tumbleUpdateGripeAt = Time.unscaledTime + 30f; Plugin.Log.LogWarning((object)"[Spike] Silencing an orphaned PlayerTumble.Update (no owning avatar). This is the storm that crashed the client."); } return null; } catch { return __exception; } } [HarmonyPatch(typeof(PlayerTumble), "SetupRPC")] [HarmonyFinalizer] public static Exception TumbleSetupFinalizer(PlayerTumble __instance, string _steamID, Exception __exception) { if (__exception == null) { return null; } try { object obj = Reflect.Get(__instance, "playerAvatar"); if ((Object)((obj is Component) ? obj : null) != (Object)null) { return __exception; } if (Time.unscaledTime > _tumbleGripeAt) { _tumbleGripeAt = Time.unscaledTime + 20f; Plugin.Log.LogWarning((object)("[Spike] A tumble body arrived for '" + _steamID + "' but no player with that steamID is in this machine's PlayerList. If that is his name, HIS AVATAR WAS DELETED HERE ON ARRIVAL.")); } return null; } catch { return __exception; } } public static void BindBotVoiceLocally() { try { PlayerAvatar val = null; foreach (PlayerAvatar bot in Bots) { if ((Object)(object)bot != (Object)null) { val = bot; break; } } if ((Object)(object)val == (Object)null) { return; } object obj = Reflect.Get(val, "voiceChat"); Object val2 = (Object)((obj is Object) ? obj : null); if (val2 != null && val2 != (Object)null) { return; } PlayerVoiceChat val3 = null; foreach (PlayerVoiceChat botVoice in BotVoices) { if ((Object)(object)botVoice != (Object)null) { val3 = botVoice; break; } } if ((Object)(object)val3 == (Object)null) { return; } Reflect.Set(val, "voiceChat", val3); Reflect.Set(val3, "playerAvatar", val); Reflect.Set(val, "voiceChatFetched", true); try { if (!SemiFunc.MenuLevel()) { val3.ToggleMixer(false, false); } } catch { } Plugin.Log.LogInfo((object)"[Voice] Paired his avatar and voice on this machine — without this he has subtitles and no sound."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Voice] Local pairing: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerCosmetics), "SetupColorsRPC")] [HarmonyFinalizer] public static Exception CosmeticsColorFinalizer(PlayerCosmetics __instance, Exception __exception) { if (__exception == null) { return null; } try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent() : null); if ((Object)(object)val == (Object)null || !IsBot(val)) { return __exception; } if (Time.unscaledTime > _cosmeticGripeAt) { _cosmeticGripeAt = Time.unscaledTime + 30f; Plugin.Log.LogWarning((object)"[Spike] Swallowed his death-head colour setup (no cosmetics on this machine). Cosmetic only, but it was aborting a whole Photon message batch."); } return null; } catch { return __exception; } } public static void ClientKeepAlive() { try { if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient || Time.unscaledTime < _clientUpkeepAt) { return; } _clientUpkeepAt = Time.unscaledTime + 1f; if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar bot in Bots) { if (!((Object)(object)bot == (Object)null) && !GameDirector.instance.PlayerList.Contains(bot)) { GameDirector.instance.PlayerList.Add(bot); MakeVisible(((Component)bot).gameObject); Plugin.Log.LogWarning((object)("[Spike] He had fallen out of this machine's PlayerList after a level change — re-added (now " + GameDirector.instance.PlayerList.Count + ") and un-hidden.")); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] ClientKeepAlive: " + ex.Message)); } } public static void ClientBeaconTick() { //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) try { if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient || Time.unscaledTime < _beaconAt) { return; } _beaconAt = Time.unscaledTime + 5f; PlayerAvatar val = null; int num = 0; int num2 = 0; PlayerAvatar[] array = Object.FindObjectsOfType(); foreach (PlayerAvatar val2 in array) { if (!((Object)(object)val2 == (Object)null) && (HasBotTag((Component)(object)val2) || Bots.Contains(val2))) { num++; val = val2; } } if ((Object)(object)GameDirector.instance != (Object)null) { num2 = GameDirector.instance.PlayerList.Count; } if ((Object)(object)val == (Object)null) { BotNet.SendBotStatus("NO BOT OBJECT on my machine (playerList=" + num2 + ", provisional=" + _provisional.Count + ")"); return; } int num3 = 0; int num4 = 0; Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (Renderer obj in componentsInChildren) { num3++; if (obj.enabled) { num4++; } } string[] obj2 = new string[12] { "bot present: active=", ((Component)val).gameObject.activeInHierarchy.ToString(), " renderers=", num4.ToString(), "/", num3.ToString(), " pos=", null, null, null, null, null }; Vector3 position = ((Component)val).transform.position; obj2[7] = ((Vector3)(ref position)).ToString("F1"); obj2[8] = " inList="; obj2[9] = ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList.Contains(val)).ToString(); obj2[10] = " copies="; obj2[11] = num.ToString(); BotNet.SendBotStatus(string.Concat(obj2)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Beacon] " + ex.Message)); } } public static void HaltEverything(Vector3 botPos, bool announce) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) try { BotDance.Stop(announce: false); BotAssist.Stop(botPos, announce: false); BotScout.Stop(botPos, announce: false); BotWeapons.Reset(); BotMedic.Reset(); BotHands.OfferTarget = null; BotMovement.Stop(); BotMovement.SpeedScale = 1f; BotMovement.ForceCrouch = false; if (announce) { BotPersona.SpeakExact(BotPersona.Halted, botPos); } Plugin.Log.LogInfo((object)"[Cmd] Full stop — every activity cancelled."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Cmd] HaltEverything: " + ex.Message)); } } public static void ResolveProvisional() { if (_provisional.Count == 0) { return; } List list = new List(_provisional); _provisional.Clear(); foreach (PlayerAvatar item in list) { try { if (!((Object)(object)item == (Object)null)) { if (HasBotTag((Component)(object)item)) { Bots.Add(item); MakeVisible(((Component)item).gameObject); Plugin.Log.LogInfo((object)"[Spike] It is his — adopted the networked bot and un-hid him. The duplicate-owner guard would have deleted him."); } else { Plugin.Log.LogInfo((object)"[Spike] Not his after all — destroying it, as the game intended."); Bots.Remove(item); Object.Destroy((Object)(object)((Component)item).gameObject); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] ResolveProvisional: " + ex.Message)); } } } private static bool HasBotVoiceTag(Component c) { try { PhotonView component = c.GetComponent(); object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null); return array != null && array.Length != 0 && "BOTSPIKE_VOICE".Equals(array[0] as string); } catch { return false; } } [HarmonyPatch(typeof(PlayerVoiceChat), "Awake")] [HarmonyPrefix] public static bool VoiceAwakePrefix(PlayerVoiceChat __instance) { if (!HasBotVoiceTag((Component)(object)__instance)) { return true; } BotVoices.Add(__instance); try { if (Reflect.Get(__instance, "photonView") == null) { Reflect.Set(__instance, "photonView", ((Component)__instance).GetComponent()); } if ((Object)(object)RunManager.instance != (Object)null) { List list = Reflect.Get>(RunManager.instance, "voiceChats"); if (list != null && !list.Contains(__instance)) { list.Add(__instance); } } AutoWireVoice(__instance); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Voice] Awake bypass: " + ex.Message)); } Plugin.Log.LogInfo((object)"[Voice] Bot voice arrived — duplicate-owner execution bypassed."); return false; } [HarmonyPatch(typeof(PlayerVoiceChat), "Start")] [HarmonyPrefix] public static bool VoiceStartPrefix(PlayerVoiceChat __instance) { if ((Object)(object)__instance == (Object)null || !BotVoices.Contains(__instance)) { if (!((Object)(object)__instance != (Object)null) || !HasBotVoiceTag((Component)(object)__instance)) { return true; } BotVoices.Add(__instance); AutoWireVoice(__instance); } Object.DontDestroyOnLoad((Object)(object)((Component)__instance).gameObject); return false; } [HarmonyPatch(typeof(RoomVolume), "SetExplored")] [HarmonyPrefix] public static bool RoomExploredPrefix(ref bool __result) { if (!ExploringBot) { return true; } __result = false; return false; } [HarmonyPatch(typeof(RoomVolumeCheck), "CheckSet")] [HarmonyPrefix] public static void RoomCheckPrefix(RoomVolumeCheck __instance) { ExploringBot = OwnedByBot((Component)(object)__instance); } [HarmonyPatch(typeof(RoomVolumeCheck), "CheckSet")] [HarmonyFinalizer] public static void RoomCheckFinalizer() { ExploringBot = false; } private static bool OwnedByBot(Component c) { try { if ((Object)(object)c == (Object)null) { return false; } PlayerAvatar val = Reflect.Get(c, "player"); if ((Object)(object)val == (Object)null) { val = c.GetComponentInParent(); } return (Object)(object)val != (Object)null && IsBot(val); } catch { return false; } } private static void ApplyStats(PlayerAvatar avatar, string steamId) { try { StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null) && !string.IsNullOrEmpty(steamId)) { int num = ((Plugin.CfgStrength != null) ? Mathf.Clamp(Plugin.CfgStrength.Value, 0, 10) : 2); int num2 = ((Plugin.CfgRange != null) ? Mathf.Clamp(Plugin.CfgRange.Value, 0, 10) : 0); int value = ((Plugin.CfgStamina != null) ? Mathf.Clamp(Plugin.CfgStamina.Value, 0, 10) : 0); Set(instance, "playerUpgradeStrength", steamId, num); Set(instance, "playerUpgradeRange", steamId, num2); Set(instance, "playerUpgradeStamina", steamId, value); Set(instance, "playerUpgradeSpeed", steamId, 0); Set(instance, "playerUpgradeThrow", steamId, 0); Set(instance, "playerUpgradeHealth", steamId, 0); Set(instance, "playerUpgradeExtraJump", steamId, 0); Plugin.Log.LogInfo((object)("[Stats] " + BotName + ": strength +" + num + ", range +" + num2 + ", stamina +" + value + " (grabStrength ~" + (1f + (float)num * 0.2f).ToString("F1") + ", grabRange ~" + (4f + (float)num2) + "m)")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Stats] " + ex.Message)); } } private static void Set(StatsManager sm, string dict, string key, int value) { try { Dictionary dictionary = Reflect.Get>(sm, dict); if (dictionary != null) { dictionary[key] = value; } } catch { } } internal static void EnsureBodyParts(PlayerAvatar avatar) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)avatar == (Object)null) { return; } LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance == (Object)null) { return; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(0f, 3000f, 0f); if ((Object)(object)AssetManager.instance != (Object)null) { Vector3 val2 = Reflect.Get(AssetManager.instance, "physDisabledPosition"); if (val2 != Vector3.zero) { val = val2; } } object obj = Reflect.Get(avatar, "tumble"); if ((Object)((obj is Component) ? obj : null) == (Object)null) { GameObject val3 = Reflect.Get(instance, "PlayerTumblePrefab"); if ((Object)(object)val3 != (Object)null) { GameObject val4 = (GameManager.Multiplayer() ? PhotonNetwork.Instantiate(((Object)val3).name, val, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate(val3, val, Quaternion.identity)); PlayerTumble val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent() : null); if ((Object)(object)val5 != (Object)null) { val5.playerAvatar = avatar; Reflect.Set(avatar, "tumble", val5); Plugin.Log.LogInfo((object)"[Body] Built his tumble body — the cart NRE storm and physics falls both needed it."); } } } FixSpectateFraming(avatar); EnsureEyeTarget(avatar); object obj2 = Reflect.Get(avatar, "playerDeathHead"); if (!((Object)((obj2 is Component) ? obj2 : null) == (Object)null)) { return; } GameObject val6 = Reflect.Get(instance, "PlayerDeathHeadPrefab"); if ((Object)(object)val6 != (Object)null) { GameObject val7 = (GameManager.Multiplayer() ? PhotonNetwork.Instantiate(((Object)val6).name, val, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate(val6, val, Quaternion.identity)); PlayerDeathHead val8 = (((Object)(object)val7 != (Object)null) ? val7.GetComponent() : null); if ((Object)(object)val8 != (Object)null) { val8.playerAvatar = avatar; Reflect.Set(avatar, "playerDeathHead", val8); Plugin.Log.LogInfo((object)"[Body] Built his death head — without one he could never have been revived."); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Body] " + ex.Message)); } } [HarmonyPatch(typeof(PhysGrabber), "OverrideDisableGrabLookAt")] [HarmonyFinalizer] public static Exception GrabLookAtFinalizer(PhysGrabber __instance, Exception __exception) { if (__exception == null) { return null; } try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.playerAvatar : null); if ((Object)(object)val != (Object)null && IsBot(val)) { return null; } } catch { } return __exception; } public unsafe static void TrySpawn() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bot != (Object)null) { Plugin.Log.LogWarning((object)"[Spike] A bot already exists — despawn it first (F10)."); return; } if (!PhotonNetwork.InRoom) { Plugin.Log.LogWarning((object)"[Spike] Not in a Photon room. Host a lobby first (singleplayer never joins a room)."); return; } if (!PhotonNetwork.IsMasterClient) { Plugin.Log.LogWarning((object)"[Spike] Run this on the HOST — room objects are master-owned."); return; } NetworkManager instance = NetworkManager.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.playerAvatarPrefab : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[Spike] NetworkManager.playerAvatarPrefab is null — cannot spawn."); return; } Vector3 val2 = SpawnPos(); ManualLogSource log = Plugin.Log; string[] obj = new string[5] { "[Spike] ===== BOT SPIKE: spawning '", ((Object)val).name, "' as a ROOM OBJECT at ", null, null }; Vector3 val3 = val2; obj[3] = ((object)(*(Vector3*)(&val3))/*cast due to .constrained prefix*/).ToString(); obj[4] = " ====="; log.LogInfo((object)string.Concat(obj)); int num = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : (-1)); PlayerAvatar instance2 = PlayerAvatar.instance; try { _botSerial++; _expectBot = true; _bot = PhotonNetwork.InstantiateRoomObject(((Object)val).name, val2, Quaternion.identity, (byte)0, new object[3] { "BOTSPIKE_BOT", _botSerial, BotName }); } catch (Exception ex) { Plugin.Log.LogError((object)("[Spike] Q1 FAIL — InstantiateRoomObject threw: " + ex)); _bot = null; return; } finally { _expectBot = false; } if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogError((object)"[Spike] Q1 FAIL — instantiate returned null."); return; } PlayerAvatar val4 = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if ((Object)(object)val4 != (Object)null) { Bots.Add(val4); } Plugin.Log.LogInfo((object)("[Spike] Q1: object created. PlayerAvatar component " + (((Object)(object)val4 != (Object)null) ? "FOUND" : "MISSING"))); if (PlayerAvatar.instance != instance2) { Plugin.Log.LogError((object)"[Spike] !! SAFETY: PlayerAvatar.instance CHANGED — the bot hijacked the local player. Despawning immediately."); Despawn(); return; } Object.DontDestroyOnLoad((Object)(object)_bot); ClearLocalFlags(_bot); GiveEye(_bot, val4); GiveVisionTarget(_bot, val4); MakeVisible(_bot); CreateNameTag(val4); StripToBaseLook(val4); ApplyStats(val4, "BOT_" + _botSerial); RegisterWithEnemies(val4); DriveTo(val2, Quaternion.identity); Plugin.Log.LogInfo((object)("[Spike] Players before spawn: " + num)); _report = 0.5f; _reportsLeft = 6; _driving = false; } private static Vector3 SpawnPos() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return Vector3.zero; } Transform transform = ((Component)instance).transform; Vector3 forward = transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } return transform.position + ((Vector3)(ref forward)).normalized * 2.5f + Vector3.up * 0.2f; } public static void JumpNow() { if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogInfo((object)"[Spike] No bot to jump."); return; } PlayerAvatar avatar = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); Plugin.Log.LogInfo((object)(BotMovement.Jump(avatar) ? "[Spike] Jump!" : "[Spike] Can't jump right now (airborne, tumbling, or on cooldown).")); } public static void Despawn() { if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogInfo((object)"[Spike] No bot to despawn."); return; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); GameObject val2 = null; GameObject val3 = null; if ((Object)(object)val != (Object)null) { object obj = Reflect.Get(val, "tumble"); Component val4 = (Component)((obj is Component) ? obj : null); object obj2 = Reflect.Get(val, "playerDeathHead"); Component val5 = (Component)((obj2 is Component) ? obj2 : null); if ((Object)(object)val4 != (Object)null) { val2 = ((Component)val4.transform.root).gameObject; } if ((Object)(object)val5 != (Object)null) { val3 = ((Component)val5.transform.root).gameObject; } } try { PhotonNetwork.Destroy(_bot); } catch { try { Object.Destroy((Object)(object)_bot); } catch { } } GameObject[] array = (GameObject[])(object)new GameObject[2] { val2, val3 }; foreach (GameObject val6 in array) { if ((Object)(object)val6 == (Object)null) { continue; } try { PhotonNetwork.Destroy(val6); } catch { try { Object.Destroy((Object)(object)val6); } catch { } } Plugin.Log.LogInfo((object)("[Spike] Cleaned up his '" + ((Object)val6).name + "' — orphaned body parts NRE forever.")); } if ((Object)(object)_voice != (Object)null) { try { PhotonNetwork.Destroy(_voice); } catch { } _voice = null; } _bot = null; Bots.Clear(); _driving = false; BotMovement.Reset(); BotChatter.Reset(); BotHands.Reset(); BotDanger.Reset(); BotMedic.Reset(); BotScout.Reset(); BotBuddy.Reset(); BotEntrance.Reset(); BotHush.Reset(); BotGrudge.Reset(); BotTattle.Reset(); BotOpinions.Reset(); BotRevival.Reset(); BotLastStand.Reset(); BotAggro.Reset(); BotWeapons.Reset(); BotComposer.Reset(); BotEyeContact.Reset(); BotIdle.Reset(); BotNoise.Reset(); BotStuck.Reset(); BotIntercom.Reset(); BotSelfHear.Reset(); BotDance.Reset(); BotAssist.Reset(); BotSocial.Reset(); Plugin.Log.LogInfo((object)"[Spike] Bot despawned."); } public static void AutoSpawnTick() { if (Plugin.CfgAutoSpawn == null || !Plugin.CfgAutoSpawn.Value) { return; } if ((Object)(object)_bot != (Object)null) { _autoRetry = 0f; } else { if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) { return; } bool flag = BotMovement.InLobby() && Plugin.CfgLobbyBertram != null && Plugin.CfgLobbyBertram.Value; if (!BotMovement.LevelReady() && !flag) { _levelSettle = 0f; return; } _levelSettle += Time.deltaTime; if (_levelSettle < 5f) { return; } _autoRetry -= Time.deltaTime; if (_autoRetry > 0f) { return; } _autoRetry = 1.5f; TrySpawn(); if ((Object)(object)_bot != (Object)null) { _driving = true; Plugin.Log.LogInfo((object)"[Spike] Auto-spawned at level start."); if (!BotMovement.InLobby()) { BotEntrance.Prepare(); } } } } public static void Tick() { //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Unknown result type (might be due to invalid IL or missing references) //IL_04aa: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0505: Unknown result type (might be due to invalid IL or missing references) //IL_051b: Unknown result type (might be due to invalid IL or missing references) //IL_04f7: Unknown result type (might be due to invalid IL or missing references) //IL_04fc: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Unknown result type (might be due to invalid IL or missing references) //IL_0736: Unknown result type (might be due to invalid IL or missing references) //IL_073b: Unknown result type (might be due to invalid IL or missing references) //IL_0745: Unknown result type (might be due to invalid IL or missing references) //IL_074a: Unknown result type (might be due to invalid IL or missing references) //IL_074f: Unknown result type (might be due to invalid IL or missing references) //IL_064c: Unknown result type (might be due to invalid IL or missing references) //IL_0651: Unknown result type (might be due to invalid IL or missing references) //IL_065b: Unknown result type (might be due to invalid IL or missing references) //IL_0660: Unknown result type (might be due to invalid IL or missing references) //IL_0665: Unknown result type (might be due to invalid IL or missing references) //IL_061e: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) //IL_060d: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) //IL_075a: Unknown result type (might be due to invalid IL or missing references) //IL_075c: Unknown result type (might be due to invalid IL or missing references) //IL_0766: Unknown result type (might be due to invalid IL or missing references) //IL_076b: Unknown result type (might be due to invalid IL or missing references) //IL_066e: Unknown result type (might be due to invalid IL or missing references) //IL_0670: Unknown result type (might be due to invalid IL or missing references) //IL_0672: Unknown result type (might be due to invalid IL or missing references) //IL_067c: Unknown result type (might be due to invalid IL or missing references) //IL_0681: Unknown result type (might be due to invalid IL or missing references) //IL_05a2: Unknown result type (might be due to invalid IL or missing references) //IL_05a9: Unknown result type (might be due to invalid IL or missing references) //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05b3: Unknown result type (might be due to invalid IL or missing references) //IL_05b8: Unknown result type (might be due to invalid IL or missing references) //IL_05ba: Unknown result type (might be due to invalid IL or missing references) //IL_05bc: Unknown result type (might be due to invalid IL or missing references) //IL_05c3: Unknown result type (might be due to invalid IL or missing references) //IL_05c8: Unknown result type (might be due to invalid IL or missing references) //IL_05e7: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Unknown result type (might be due to invalid IL or missing references) //IL_05f5: Unknown result type (might be due to invalid IL or missing references) //IL_05fa: Unknown result type (might be due to invalid IL or missing references) //IL_05ff: Unknown result type (might be due to invalid IL or missing references) //IL_05de: Unknown result type (might be due to invalid IL or missing references) //IL_05e3: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_069b: Unknown result type (might be due to invalid IL or missing references) //IL_06a7: Unknown result type (might be due to invalid IL or missing references) //IL_06dd: Unknown result type (might be due to invalid IL or missing references) //IL_06ec: Unknown result type (might be due to invalid IL or missing references) //IL_06f6: Unknown result type (might be due to invalid IL or missing references) //IL_06fb: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_0714: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bot == (Object)null) { if (_reportsLeft > 0) { _reportsLeft = 0; Plugin.Log.LogError((object)"[Spike] Q1 RESULT: FAIL — the bot was DESTROYED (a self-destruct guard fired, or Awake threw)."); } return; } VoiceUpkeep(); if (_ttsAuditDue > 0f && Time.unscaledTime >= _ttsAuditDue) { _ttsAuditDue = 0f; TtsAudit(_bot.GetComponent() ?? _bot.GetComponentInChildren(true)); } BotDeathHead.Tick(); if (BotMovement.InLobby()) { if (Plugin.CfgLobbyBertram != null && Plugin.CfgLobbyBertram.Value) { PlayerAvatar avatar = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); BotPersona.Chatty = true; BotSocial.TickVoice(_bot.transform.position, Time.deltaTime); BotSocial.TickLobby(_bot, avatar, Time.deltaTime); } } else { if (!BotMovement.LevelReady()) { return; } BotPersona.Chatty = false; PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); bool awaitingReturn = BotRevival.AwaitingReturn; BotRevival.Tick(val, Time.deltaTime); BotAggro.Tick(val, Time.deltaTime); BotLastStand.Tick(val, Time.deltaTime); BotStuck.Tick(val, Time.deltaTime); BotNoise.Tick(_bot.transform.position, Time.deltaTime); if (!awaitingReturn && BotRevival.AwaitingReturn) { Deaths++; RegisterOwnIdentity(val); Plugin.Log.LogInfo((object)("[Revival] Death #" + Deaths + " — he'll be '" + BotName + "' when he returns.")); } PlayerAvatar val2 = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if ((Object)(object)val2 != (Object)null && (Reflect.Get(val2, "isDisabled") || Reflect.Get(val2, "deadSet"))) { if (!_wasDown) { _wasDown = true; Plugin.Log.LogInfo((object)"[Spike] He's down — every behaviour is parked until he's revived."); HaltEverything(_bot.transform.position, announce: false); BotIdle.Reset(); BotNoise.Reset(); BotComposer.Reset(); } BotMovement.Stop(); return; } if (_wasDown) { _wasDown = false; BotDeath.ClearDeathReport(); BotMovement.Reset(); Plugin.Log.LogInfo((object)"[Spike] Back on his feet — behaviours resumed."); } BotEntrance.MaybeRetry(); PlayerAvatar avatar2 = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if (BotEntrance.Tick(_bot, avatar2, Time.deltaTime)) { return; } BotMovement.DirectMode = false; BotSocial.TickVoice(_bot.transform.position, Time.deltaTime); BotTattle.Tick(Time.deltaTime); BotOpinions.TickAwareness(_bot.transform.position, Time.deltaTime); PlayerAvatar val3 = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); BotFacts.Refresh(val3, Time.deltaTime); BotEyeContact.Tick(_bot, val3, Time.deltaTime); BotComposer.Tick(_bot.transform.position); BotRelations.TickFirstSight(_bot.transform.position); BotAssist.MaybeOffer(_bot.transform.position); if (_driving) { PlayerAvatar val4 = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); PlayerAvatar instance = PlayerAvatar.instance; Vector3 pos; bool flag = BotHands.NearestHuman(_bot.transform.position, out pos) && Vector3.Distance(_bot.transform.position, pos) > 15f; BotBuddy.Tick(Time.deltaTime, _bot.transform.position); if (!BotHush.Tick(_bot, val4, Time.deltaTime) && !BotMedic.Tick(_bot, val4, Time.deltaTime) && !BotDanger.Tick(_bot, Time.deltaTime) && !BotScout.Tick(_bot, val4, Time.deltaTime) && !BotAssist.Tick(_bot, val4, Time.deltaTime) && !BotDance.Tick(_bot, val4, Time.deltaTime)) { if (flag) { BotMovement.SetDestination(pos); } else if (!BotWeapons.Tick(_bot, val4, Time.deltaTime) && !BotHands.Tick(_bot, val4, Time.deltaTime)) { if (!BotBuddy.AnchorPosition(out var pos2) && (Object)(object)instance != (Object)null) { pos2 = ((Component)instance).transform.position; } if (pos2 != Vector3.zero) { float num = Vector3.Distance(_bot.transform.position, pos2); Vector3 buddyVel = Vector3.zero; if ((Object)(object)instance != (Object)null) { Rigidbody component = ((Component)instance).GetComponent(); if ((Object)(object)component != (Object)null) { buddyVel = component.velocity; } } if (!BotIdle.StepAside(_bot, pos2, buddyVel) && !BotIdle.Tick(_bot, Time.deltaTime, pos2)) { if (num > 4.5f) { Vector3 val5 = pos2 - _bot.transform.position; val5.y = 0f; Vector3 val6 = pos2; if (((Vector3)(ref val5)).sqrMagnitude > 0.0001f) { float num2 = (((_botSerial + (int)(Time.time * 0.2f)) % 2 == 0) ? 1f : (-1f)); Vector3 val7 = Quaternion.Euler(0f, num2 * 45f, 0f) * -((Vector3)(ref val5)).normalized; NavMeshHit val8 = default(NavMeshHit); val6 = ((!NavMesh.SamplePosition(pos2 + val7 * 2.2f, ref val8, 1.8f, -1)) ? (pos2 - ((Vector3)(ref val5)).normalized * 2.2f) : ((NavMeshHit)(ref val8)).position); } if (!BotMovement.HasGoal || Vector3.Distance(BotMovement.Goal, val6) > 1.5f) { BotMovement.SetDestination(val6); } } else if (num < 2.2f) { if (BotMovement.HasGoal) { BotMovement.Stop(); } Vector3 val9 = _bot.transform.position + Vector3.up * 1.2f; bool num3 = BotSight.Blockers != 0 && Physics.Linecast(val9, pos2 + Vector3.up * 1.2f, BotSight.Blockers, (QueryTriggerInteraction)1); bool flag2 = BotSight.Blockers != 0 && Physics.Raycast(val9, _bot.transform.forward, 1.2f, BotSight.Blockers, (QueryTriggerInteraction)1); if (!num3 && !flag2) { BotMovement.FaceTowards(pos2); } else { BotMovement.FaceTowards(_bot.transform.position + _bot.transform.forward * 3f + _bot.transform.right * 1.5f); } } else if (!BotMovement.HasGoal) { Vector3 val10 = _bot.transform.position + Vector3.up * 1.2f; if (BotSight.Blockers == 0 || !Physics.Linecast(val10, pos2 + Vector3.up * 1.2f, BotSight.Blockers, (QueryTriggerInteraction)1)) { BotMovement.FaceTowards(pos2); } } } } } } BotMovement.Tick(_bot, val4, Time.deltaTime); } BotChatter.Tick(_bot, Time.deltaTime); if (Time.unscaledTime > _visibilityAt) { _visibilityAt = Time.unscaledTime + 0.5f; MakeVisible(_bot); } if (_reportsLeft > 0) { _sweepTimer -= Time.deltaTime; if (_sweepTimer <= 0f) { _sweepTimer = 1f; ClearLocalFlags(_bot); } } if (_reportsLeft > 0) { ReassertIdentity(); StripToBaseLook(_bot.GetComponent()); } if (_reportsLeft > 0) { _report -= Time.deltaTime; if (!(_report > 0f)) { _report = 1f; _reportsLeft--; Report(); } } } } private static void DriveTo(Vector3 pos, Quaternion rot) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_bot == (Object)null)) { PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); Vector3 position = _bot.transform.position; _bot.transform.position = pos; _bot.transform.rotation = rot; if (!((Object)(object)val == (Object)null)) { Reflect.Set(val, "clientPosition", pos); Reflect.Set(val, "clientPositionCurrent", pos); Reflect.Set(val, "clientRotation", rot); Reflect.Set(val, "clientRotationCurrent", rot); Vector3 val2 = (pos - position) / Mathf.Max(Time.deltaTime, 0.0001f); Reflect.Set(val, "rbVelocity", val2); Reflect.Set(val, "rbVelocityRaw", val2); Reflect.Set(val, "isMoving", ((Vector3)(ref val2)).magnitude > 0.3f); } } } private static void RegisterWithEnemies(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return; } try { PhotonView component = ((Component)avatar).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)EnemyDirector.instance == (Object)null) { return; } int viewID = component.ViewID; int num = 0; foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if ((Object)(object)item == (Object)null) { continue; } object obj = Reflect.Get(item, "Enemy"); if (obj == null) { continue; } try { MethodInfo methodInfo = AccessTools.Method(obj.GetType(), "PlayerAdded", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(obj, new object[1] { viewID }); num++; } } catch { } } Plugin.Log.LogInfo((object)("[Spike] Registered bot with " + num + " enemies (view " + viewID + ").")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] RegisterWithEnemies: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerAvatarVisuals), "ApplyLocalVisibility")] [HarmonyPrefix] public static bool NeverHideTheBot(PlayerAvatarVisuals __instance) { try { PlayerAvatar val = Reflect.Get(__instance, "playerAvatar"); if ((Object)(object)val == (Object)null) { val = ((Component)__instance).GetComponentInParent(); } if ((Object)(object)val == (Object)null || !IsBot(val)) { return true; } if (Time.unscaledTime > _hideGripeAt) { _hideGripeAt = Time.unscaledTime + 60f; Plugin.Log.LogInfo((object)"[Spike] Refused to hide him from himself (IsMine is true for a room object, so the game thinks he is the host's own body)."); } return false; } catch { } return true; } private static void MakeVisible(GameObject bot) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Invalid comparison between Unknown and I4 //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Invalid comparison between Unknown and I4 if ((Object)(object)bot == (Object)null) { return; } bot = ((Component)bot.transform.root).gameObject; int num = LayerMask.NameToLayer("PlayerVisuals"); int num2 = LayerMask.NameToLayer("PlayerVisualsLocal"); int num3 = 0; Renderer[] componentsInChildren = bot.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { bool num4 = (num2 >= 0 && ((Component)val).gameObject.layer == num2) || (int)val.shadowCastingMode == 3; if (num >= 0 && num2 >= 0 && ((Component)val).gameObject.layer == num2) { ((Component)val).gameObject.layer = num; } if ((int)val.shadowCastingMode == 3) { val.shadowCastingMode = (ShadowCastingMode)1; } if (num4) { val.enabled = true; num3++; } } } if (num3 > 0) { Plugin.Log.LogInfo((object)("[Spike] Made " + num3 + " hidden renderers visible.")); } } private static void ClearLocalFlags(GameObject bot) { if ((Object)(object)bot == (Object)null) { return; } int num = 0; MonoBehaviour[] componentsInChildren = bot.GetComponentsInChildren(true); foreach (MonoBehaviour val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } try { FieldInfo fieldInfo = Reflect.GetFieldInfo(((object)val).GetType(), "isLocal"); if (!(fieldInfo == null) && !(fieldInfo.FieldType != typeof(bool)) && (bool)fieldInfo.GetValue(val)) { fieldInfo.SetValue(val, false); num++; Plugin.Log.LogInfo((object)("[Spike] Cleared isLocal on " + ((object)val).GetType().Name)); } } catch { } } if (num > 0) { Plugin.Log.LogInfo((object)("[Spike] Cleared " + num + " isLocal flag(s).")); } } private static void RegisterOwnIdentity(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return; } try { Reflect.Set(avatar, "steamID", BotName); Reflect.Set(avatar, "playerName", BotName); if ((Object)(object)StatsManager.instance != (Object)null) { try { StatsManager.instance.PlayerAdd(BotName, BotName); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] PlayerAdd: " + ex.Message)); } } GiveOwnVoice(avatar); Plugin.Log.LogInfo((object)("[Spike] Identity set: " + BotName)); } catch (Exception ex2) { Plugin.Log.LogError((object)("[Spike] RegisterOwnIdentity: " + ex2)); } } private static void GiveOwnVoice(PlayerAvatar avatar) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)avatar == (Object)null) { return; } try { GameObject val = PhotonNetwork.InstantiateRoomObject("Voice", Vector3.zero, Quaternion.identity, (byte)0, new object[1] { "BOTSPIKE_VOICE" }); PhotonView val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[Spike] Voice object has no PhotonView."); return; } PhotonView component = ((Component)avatar).GetComponent(); if ((Object)(object)component != (Object)null) { component.RPC("UpdateMyPlayerVoiceChat", (RpcTarget)3, new object[1] { val2.ViewID }); } _voice = val; Object.DontDestroyOnLoad((Object)(object)val); PlayerVoiceChat component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { Reflect.Set(avatar, "voiceChat", component2); Reflect.Set(component2, "playerAvatar", avatar); Reflect.Set(avatar, "voiceChatFetched", true); } MuteMicRelay(); Plugin.Log.LogInfo((object)("[Spike] Gave the bot its own voice (view " + val2.ViewID + ").")); object obj = Reflect.Get(avatar, "voiceChat"); Plugin.Log.LogInfo((object)("[Spike] voiceChat bound: " + ((obj == null) ? "NULL (TTS will use the host!)" : obj.ToString()))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] GiveOwnVoice: " + ex.Message)); } } private static void RouteVoiceMixers(Component vcc) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown try { if ((Object)(object)vcc == (Object)null) { return; } AudioSource val = Reflect.Get(vcc, "audioSource"); AudioSource val2 = Reflect.Get(vcc, "ttsAudioSource"); bool flag = BotMovement.InLobby(); if (Reflect.Get(vcc, "inLobbyMixer") != flag) { Reflect.Set(vcc, "inLobbyMixer", flag); } object obj = Reflect.Get(vcc, flag ? "mixerMicrophoneSpectate" : "mixerMicrophoneSound"); object obj2 = Reflect.Get(vcc, flag ? "mixerTTSSpectate" : "mixerTTSSound"); bool flag2 = false; if ((Object)(object)val != (Object)null && obj != null && val.outputAudioMixerGroup != obj) { val.outputAudioMixerGroup = (AudioMixerGroup)obj; if (val.volume <= 0.001f) { val.volume = 1f; } flag2 = true; } if ((Object)(object)val2 != (Object)null && obj2 != null && val2.outputAudioMixerGroup != obj2) { val2.outputAudioMixerGroup = (AudioMixerGroup)obj2; flag2 = true; } if ((Object)(object)val2 != (Object)null && !BotEspeak.Active) { if (val2.mute) { val2.mute = false; flag2 = true; } if (val2.volume <= 0.001f) { val2.volume = 1f; flag2 = true; } if (!((Behaviour)val2).enabled) { ((Behaviour)val2).enabled = true; flag2 = true; } } object obj3 = Reflect.Get(vcc, "lowPassLogicTTS"); if (obj3 != null && Reflect.Get(obj3, "Volume") <= 0.001f) { Reflect.Set(obj3, "Volume", 1f); flag2 = true; } if (flag2) { Plugin.Log.LogInfo((object)"[Voice] Routed his voice + TTS onto the game's audio mixer — unrouted sources play silently, which is why he had subtitles and no sound."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Voice] RouteVoiceMixers: " + ex.Message)); } } private static void EnsureTts(PlayerAvatar avatar) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)avatar == (Object)null) { return; } object obj = Reflect.Get(avatar, "voiceChat"); if (obj == null) { return; } Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || Reflect.Get(obj, "ttsVoice") != null) { return; } if (!val.gameObject.activeSelf) { val.gameObject.SetActive(true); } GameObject val2 = Reflect.Get(obj, "TTSprefab"); if ((Object)(object)val2 == (Object)null) { return; } GameObject val3 = new GameObject("BotSpike.TtsHolder"); val3.SetActive(false); GameObject val4 = Object.Instantiate(val2, val3.transform); BotVoiceClarity.TuneSynth(val4); val4.transform.SetParent(val.transform, false); val4.transform.localPosition = Vector3.zero; val4.transform.localRotation = Quaternion.identity; Object.Destroy((Object)(object)val3); object component = val4.GetComponent(typeof(TTSVoice)); if (component == null) { Object.Destroy((Object)(object)val4); return; } AudioSource component2 = val4.GetComponent(); Reflect.Set(obj, "ttsVoice", component); Reflect.Set(obj, "ttsAudioSource", component2); Reflect.Set(component, "playerAvatar", avatar); AudioLowPassLogic val5 = (((Object)(object)component2 != (Object)null) ? ((Component)component2).GetComponent() : null); if ((Object)(object)val5 != (Object)null) { Reflect.Set(obj, "lowPassLogicTTS", val5); Reflect.Set(val5, "Fetch", true); Plugin.Log.LogInfo((object)"[Spike] Wired his TTS into the low-pass/distance logic — he was audible from anywhere."); } else { Plugin.Log.LogWarning((object)"[Spike] TTS prefab had no AudioLowPassLogic — his voice will not attenuate."); } Reflect.Set(obj, "TTSinstantiated", true); Plugin.Log.LogInfo((object)"[Spike] Built his TTS voice by hand — the lazy path never ran, which is why he was silent."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] EnsureTts: " + ex.Message)); } } private static void TtsAudit(PlayerAvatar a) { //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) try { if (Time.unscaledTime < _ttsAuditAt) { return; } _ttsAuditAt = Time.unscaledTime + 6f; if ((Object)(object)a == (Object)null) { Plugin.Log.LogWarning((object)"[TTS] no avatar"); return; } object obj = Reflect.Get(a, "voiceChat"); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[TTS] voiceChat=NULL — he has no voice object, so nothing can ever be heard. This is the whole bug."); return; } object obj2 = Reflect.Get(val, "ttsVoice"); AudioSource val2 = Reflect.Get(val, "ttsAudioSource"); bool flag = BotMovement.InLobby(); if (Reflect.Get(val, "inLobbyMixer") != flag) { Reflect.Set(val, "inLobbyMixer", flag); } if (obj2 == null || (Object)(object)val2 == (Object)null) { Plugin.Log.LogError((object)("[TTS] ttsVoice=" + ((obj2 == null) ? "NULL" : "ok") + " ttsAudioSource=" + (((Object)(object)val2 == (Object)null) ? "NULL" : "ok") + " — the synthesiser was never built, so every line is dropped after the subtitle. EnsureTts should have made it.")); return; } float num = -1f; try { AudioListener val3 = Object.FindObjectOfType(); if ((Object)(object)val3 != (Object)null) { num = Vector3.Distance(val.transform.position, ((Component)val3).transform.position); } } catch { } string text = (((Object)(object)val2.outputAudioMixerGroup != (Object)null) ? ((Object)val2.outputAudioMixerGroup).name : "NONE(silent)"); Plugin.Log.LogWarning((object)("[TTS] playing=" + val2.isPlaying + " vol=" + val2.volume.ToString("F2") + " mute=" + val2.mute + " enabled=" + ((Behaviour)val2).enabled + " mixer=" + text + " blend=" + val2.spatialBlend.ToString("F2") + " max=" + val2.maxDistance.ToString("F0") + " distToEars=" + num.ToString("F1") + " lobbyBus=" + flag + " voicePos=" + ((object)val.transform.position/*cast due to .constrained prefix*/).ToString())); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[TTS] audit: " + ex.Message)); } } public static void Say(string text) { if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogInfo((object)"[Spike] No bot to speak."); return; } if (BotHush.Active) { Plugin.Log.LogInfo((object)("[Hush] Swallowed a line (" + BotHush.SecondsLeft.ToString("F0") + "s left): \"" + text + "\"")); return; } try { PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); PhotonView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val == (Object)null) { return; } string text2 = text; if (IsEspeakTtsLoaded() && Plugin.CfgEspeakVoiceTag != null && !string.IsNullOrEmpty(Plugin.CfgEspeakVoiceTag.Value)) { string text3 = Plugin.CfgEspeakVoiceTag.Value.Trim(); if (!string.IsNullOrEmpty(text3) && !text2.StartsWith("[")) { text2 = text3 + " " + text; } } try { MethodInfo method = typeof(PlayerAvatar).GetMethod("ChatMessageSendRPC", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = 0; if (method != null) { ParameterInfo[] parameters = method.GetParameters(); for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType != typeof(PhotonMessageInfo)) { num++; } } } if (num >= 2) { val2.RPC("ChatMessageSendRPC", (RpcTarget)0, new object[2] { text2, false }); } else { val2.RPC("ChatMessageSendRPC", (RpcTarget)0, new object[1] { text2 }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] Chat RPC failed: " + ex.Message)); } Plugin.Log.LogInfo((object)("[Spike] " + BotName + " says: \"" + text + "\"")); _ttsAuditDue = Time.unscaledTime + 0.7f; } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Spike] Say failed: " + ex2.Message)); } } public static bool IsEspeakTtsLoaded() { try { return Chainloader.PluginInfos.ContainsKey("Lavighju.espeakTTS") || Chainloader.PluginInfos.ContainsKey("espeakTTS") || Chainloader.PluginInfos.ContainsKey("com.lavighju.espeaktts"); } catch { return false; } } private static bool IsHawkingTtsLoaded() { try { return Chainloader.PluginInfos.ContainsKey("com.omniscye.hawkingtts") || Chainloader.PluginInfos.ContainsKey("HawkingTTS") || Chainloader.PluginInfos.ContainsKey("hawkingtts"); } catch { return false; } } public static void SayRandom() { Say(Lines[Random.Range(0, Lines.Length)]); } private static void MuteMicRelay() { if ((Object)(object)_voice == (Object)null) { return; } try { Component[] componentsInChildren = _voice.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { string name = ((object)val).GetType().Name; if (!(name != "Recorder") || !(name != "PhotonVoiceView")) { SetBoolProp(val, "RecordingEnabled", value: false); SetBoolProp(val, "TransmitEnabled", value: false); } } } } catch { } } private static void SetBoolProp(object obj, string prop, bool value) { try { PropertyInfo property = obj.GetType().GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite && property.PropertyType == typeof(bool)) { property.SetValue(obj, value, null); } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "Update")] [HarmonyPrefix] public static void AvatarUpdatePrefix(PlayerAvatar __instance) { if (!IsBot(__instance)) { return; } GameObject gameObject = ((Component)__instance).gameObject; try { object obj = Reflect.Get(__instance, "PlayerVisionTarget"); if (obj == null) { GiveVisionTarget(gameObject, __instance); } else if (Reflect.Get(obj, "VisionTransform") == null) { Reflect.Set(obj, "VisionTransform", EnsureHeadTransform(gameObject)); Plugin.Log.LogWarning((object)"[Probe] VisionTransform WAS null — repaired. (That was the Update NRE.)"); } object obj2 = Reflect.Get(__instance, "voiceChat"); Object val = (Object)((obj2 is Object) ? obj2 : null); if (val != null && val == (Object)null && (Object)(object)_voice != (Object)null) { PlayerVoiceChat component = _voice.GetComponent(); if ((Object)(object)component != (Object)null) { Reflect.Set(__instance, "voiceChat", component); Reflect.Set(component, "playerAvatar", __instance); Plugin.Log.LogWarning((object)"[Voice] voiceChat was destroyed — rebound his own Voice object."); } } } catch { } } private static void VoiceUpkeep() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bot == (Object)null) { return; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { return; } object obj = Reflect.Get(val, "voiceChat"); Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 != (Object)null) { val2.transform.position = _bot.transform.position + Vector3.up * 1.4f; } if (Time.unscaledTime < _voiceCheckAt) { return; } _voiceCheckAt = Time.unscaledTime + 1.5f; RunManager instance = RunManager.instance; if ((Object)(object)instance != (Object)null && (Reflect.Get(instance, "restarting") || Reflect.Get(instance, "waitToChangeScene"))) { return; } RepairGrabberHandles(val); if ((Object)(object)val2 == (Object)null) { if (Time.unscaledTime > _voiceWarnAt) { _voiceWarnAt = Time.unscaledTime + 10f; Plugin.Log.LogWarning((object)("[Voice] voiceChat is missing or destroyed — " + (((Object)(object)_voice == (Object)null) ? "recreating the Voice object." : "rebinding the survivor."))); } if ((Object)(object)_voice == (Object)null) { GiveOwnVoice(val); return; } PlayerVoiceChat component = _voice.GetComponent(); if ((Object)(object)component != (Object)null) { Reflect.Set(val, "voiceChat", component); Reflect.Set(component, "playerAvatar", val); Reflect.Set(val, "voiceChatFetched", true); } return; } if (!val2.gameObject.activeSelf) { val2.gameObject.SetActive(true); Plugin.Log.LogWarning((object)"[Voice] His Voice object had been disabled — re-enabled it."); } EnsureTts(val); MuteMicRelay(); RouteVoiceMixers(val2); BotEspeakHook.MuteVanillaVoice(val2); bool flag = Reflect.Get(val2, "ttsVoice") != null; if (flag && !_voiceReadyAnnounced) { _voiceReadyAnnounced = true; Plugin.Log.LogInfo((object)"[Voice] TTS ready — he can actually be heard now."); } else if (!flag && Time.unscaledTime > _voiceWarnAt) { _voiceWarnAt = Time.unscaledTime + 12f; Plugin.Log.LogWarning((object)"[Voice] TTS still missing (waiting on the game's voice systems) — retrying."); } WorldStateUpkeep(val); } private static void WorldStateUpkeep(PlayerAvatar a) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if ((Object)(object)a == (Object)null) { return; } bool flag = BotMovement.InLobby(); bool flag2 = BotMovement.LevelReady(); bool flag3 = flag || flag2; bool flag4 = flag || flag2; try { LevelGenerator instance = LevelGenerator.Instance; if (!flag2 && !flag && (Object)(object)instance != (Object)null) { LevelState state = instance.State; flag3 = (int)state == 0 || (int)state == 1 || (int)state == 2 || (int)state == 3 || (int)state == 4 || (int)state == 5; } } catch { } if (!flag3 && !flag4) { return; } try { if (flag3 && (Object)(object)GameDirector.instance != (Object)null && !GameDirector.instance.PlayerList.Contains(a)) { GameDirector.instance.PlayerList.Add(a); if (flag4) { RegisterWithEnemies(a); } Plugin.Log.LogWarning((object)("[Spike] He had fallen out of GameDirector.PlayerList — re-added (now " + GameDirector.instance.PlayerList.Count + "). Enemies can see him again.")); } } catch { } EnsureBodyParts(a); if (flag4 && !SemiFunc.MenuLevel() && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null) { CreateNameTag(a); } } public static void OnNewLevel() { if ((Object)(object)_bot == (Object)null) { return; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if (!((Object)(object)val == (Object)null)) { object obj = Reflect.Get(val, "worldSpaceUIPlayerName"); if ((Object)((obj is Component) ? obj : null) == (Object)null) { CreateNameTag(val); } BotRelations.CommitRun(); BotFacts.NewLevel(); RegisterWithEnemies(val); EnsureBodyParts(val); RepairGrabberHandles(val); MakeVisible(_bot); _voiceReadyAnnounced = false; } } private static void HandleUnexpectedDestroy(PlayerAvatar a) { Bots.Remove(a); _bot = null; _reportsLeft = 0; _voiceReadyAnnounced = false; BotMovement.Reset(); BotHands.Reset(); BotDanger.Reset(); BotScout.Reset(); BotHush.Reset(); BotEntrance.Reset(); Plugin.Log.LogWarning((object)"[Spike] Bot destroyed outside Despawn — state cleared so auto-spawn can rebuild him."); } [HarmonyPatch(typeof(PlayerAvatar), "FixedUpdate")] [HarmonyPrefix] public static bool AvatarFixedUpdatePrefix(PlayerAvatar __instance) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (!IsBot(__instance)) { return true; } try { Rigidbody val = Reflect.Get(__instance, "rb"); if ((Object)(object)val != (Object)null) { val.MovePosition(Reflect.Get(__instance, "clientPositionCurrent")); Quaternion val2 = Reflect.Get(__instance, "clientRotationCurrent"); if (Mathf.Abs(val2.x) + Mathf.Abs(val2.y) + Mathf.Abs(val2.z) + Mathf.Abs(val2.w) > 0.0001f) { val.MoveRotation(val2); } } } catch { } return false; } [HarmonyPatch(typeof(PlayerLocalCamera), "Update")] [HarmonyPrefix] public static bool LocalCameraPrefix(PlayerLocalCamera __instance) { return !IsBotCamera(__instance); } private static bool IsBotCamera(PlayerLocalCamera cam) { try { if ((Object)(object)cam == (Object)null) { return false; } if (BotCameras.Contains(cam)) { return true; } PlayerAvatar val = Reflect.Get(cam, "playerAvatar"); if ((Object)(object)val != (Object)null && IsBot(val)) { BotCameras.Add(cam); return true; } PlayerAvatar componentInParent = ((Component)cam).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && IsBot(componentInParent)) { BotCameras.Add(cam); return true; } } catch { } return false; } private static void SyncBeamRef(PhysGrabBeam beam, string field, Transform target) { if (!((Object)(object)target == (Object)null) && !((Object)(object)Reflect.Get(beam, field) != (Object)null)) { Reflect.Set(beam, field, target); Plugin.Log.LogInfo((object)("[Spike] Re-pointed PhysGrabBeam." + field + " at the live handle.")); } } [HarmonyPatch(typeof(PlayerAvatarRightArm), "Update")] [HarmonyFinalizer] public static Exception RightArmFinalizer(PlayerAvatarRightArm __instance, Exception __exception) { if (__exception == null) { return null; } try { PlayerAvatar val = Reflect.Get(__instance, "playerAvatar"); if ((Object)(object)val == (Object)null) { val = ((Component)__instance).GetComponentInParent(); } if ((Object)(object)val != (Object)null && IsBot(val)) { if (Time.unscaledTime > _armProbeAt) { _armProbeAt = Time.unscaledTime + 30f; Plugin.Log.LogWarning((object)("[Probe] Bot right-arm NRE (swallowed): " + __exception.Message)); } return null; } } catch { } return __exception; } [HarmonyPatch(typeof(PhysGrabber), "OnPhotonSerializeView")] [HarmonyPrefix] public static bool GrabberSerializePrefix(PhysGrabber __instance) { try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.playerAvatar : null); if ((Object)(object)val == (Object)null || !IsBot(val)) { return true; } if ((Object)(object)Reflect.Get(__instance, "physGrabPointPlane") != (Object)null && (Object)(object)Reflect.Get(__instance, "physGrabPointPuller") != (Object)null) { return true; } RepairGrabberHandles(val); if ((Object)(object)Reflect.Get(__instance, "physGrabPointPlane") == (Object)null || (Object)(object)Reflect.Get(__instance, "physGrabPointPuller") == (Object)null) { if (Time.unscaledTime > _serializeGripeAt) { _serializeGripeAt = Time.unscaledTime + 15f; Plugin.Log.LogWarning((object)"[Spike] Skipping his grabber serialization — handles not rebuildable yet."); } return false; } } catch { } return true; } private static void RepairGrabberHandles(PlayerAvatar a) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown try { PhysGrabber val = (((Object)(object)a != (Object)null) ? a.physGrabber : null); if ((Object)(object)val == (Object)null) { return; } string[] array = new string[3] { "physGrabPoint", "physGrabPointPuller", "physGrabPointPlane" }; foreach (string text in array) { if (!((Object)(object)Reflect.Get(val, text) != (Object)null)) { GameObject val2 = new GameObject("BotSpike." + text); val2.transform.SetParent(((Component)val).transform, false); Reflect.Set(val, text, val2.transform); Plugin.Log.LogInfo((object)("[Spike] Rebuilt a grabber handle that died with the old scene: " + text)); } } PhysGrabBeam physGrabBeamComponent = val.physGrabBeamComponent; if (!((Object)(object)physGrabBeamComponent != (Object)null)) { return; } SyncBeamRef(physGrabBeamComponent, "PhysGrabPoint", Reflect.Get(val, "physGrabPoint")); SyncBeamRef(physGrabBeamComponent, "PhysGrabPointPuller", Reflect.Get(val, "physGrabPointPuller")); array = new string[3] { "PhysGrabPointOrigin", "PhysGrabPointOriginClient", "PhysGrabPointOriginLocal" }; foreach (string text2 in array) { if (!((Object)(object)Reflect.Get(physGrabBeamComponent, text2) != (Object)null)) { GameObject val3 = new GameObject("BotSpike." + text2); val3.transform.SetParent(((Component)physGrabBeamComponent).transform, false); Reflect.Set(physGrabBeamComponent, text2, val3.transform); Plugin.Log.LogInfo((object)("[Spike] Rebuilt a beam origin: " + text2)); } } } catch { } } private static void ReassertIdentity() { EnsureTts(((Object)(object)_bot != (Object)null) ? (_bot.GetComponent() ?? _bot.GetComponentInChildren(true)) : null); if (!((Object)(object)_bot == (Object)null)) { PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if (!((Object)(object)val == (Object)null) && Reflect.Get(val, "playerName") != BotName) { Reflect.Set(val, "playerName", BotName); Reflect.Set(val, "steamID", BotName); Plugin.Log.LogInfo((object)"[Spike] Re-asserted identity (something overwrote it)."); } } } private static void GiveEye(GameObject bot, PlayerAvatar avatar) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || (Object)(object)avatar == (Object)null) { return; } try { if (Reflect.Get(avatar, "localCamera") == null) { GameObject val = new GameObject("BotSpike.EyeHolder"); val.SetActive(false); GameObject val2 = new GameObject("BotSpike.Eye"); val2.transform.SetParent(val.transform, false); PlayerLocalCamera val3 = val2.AddComponent(); ((Behaviour)val3).enabled = false; Reflect.Set(val3, "playerAvatar", avatar); val2.transform.SetParent(bot.transform, false); val2.transform.localPosition = new Vector3(0f, 1.3f, 0f); val2.transform.localRotation = Quaternion.identity; Object.Destroy((Object)(object)val); BotCameras.Add(val3); Reflect.Set(avatar, "localCamera", val3); Plugin.Log.LogInfo((object)"[Spike] Gave the bot an eye (localCamera) — grab path needs it."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] GiveEye: " + ex.Message)); } } [HarmonyPatch(typeof(SemiFunc), "PlayerGetName")] [HarmonyPrefix] public static bool PlayerGetNamePrefix(PlayerAvatar player, ref string __result) { if ((Object)(object)player == (Object)null || !Bots.Contains(player)) { return true; } __result = BotName; return false; } private static void StripToBaseLook(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return; } try { PlayerCosmetics val = avatar.playerCosmetics ?? ((Component)avatar).GetComponentInChildren(true); if (!((Object)(object)val == (Object)null)) { val.SetupCosmetics(false, true, new List()); Plugin.Log.LogInfo((object)"[Spike] Stripped cosmetics — base bot look."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] StripToBaseLook: " + ex.Message)); } } private static void FixSpectateFraming(PlayerAvatar avatar) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatarCollision val = Reflect.Get(avatar, "playerAvatarCollision"); if ((Object)(object)val == (Object)null || (Object)(object)val.CollisionTransform == (Object)null || (Object)(object)val.Collider == (Object)null) { if ((Object)(object)avatar.spectatePoint == (Object)null) { avatar.spectatePoint = EnsureHeadTransform(((Component)avatar).gameObject); } return; } Transform val2 = val.CollisionTransform.Find("BotSpike.SpectatePoint"); if ((Object)(object)val2 == (Object)null) { val2 = new GameObject("BotSpike.SpectatePoint").transform; val2.SetParent(val.CollisionTransform, false); } val2.localPosition = new Vector3(0f, val.Collider.height - 0.18f, 0f); if ((Object)(object)avatar.spectatePoint != (Object)(object)val2) { avatar.spectatePoint = val2; Plugin.Log.LogInfo((object)"[Body] Re-anchored the death cam to his head — it had been framing his waist."); } MarkIgnored(((Component)val).gameObject); MarkIgnored(((Component)val.Collider).gameObject); MarkIgnored(((Component)val.CollisionTransform).gameObject); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Body] Spectate framing: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerAvatarCollision), "Update")] [HarmonyPostfix] public static void CollisionScalePostfix(PlayerAvatarCollision __instance) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatar val = (((Object)(object)__instance != (Object)null) ? __instance.PlayerAvatar : null); if ((Object)(object)val == (Object)null || !IsBot(val) || (Object)(object)__instance.CollisionTransform == (Object)null || (Object)(object)__instance.Collider == (Object)null) { return; } Vector3 val2 = WantedCollisionScale(val); if (!(val2.y <= 0.01f)) { Reflect.Set(__instance, "Scale", val2); __instance.CollisionTransform.localScale = val2; Vector3 val3 = __instance.CollisionTransform.position + Vector3.up * (__instance.Collider.height * val2.y - 0.18f); Reflect.Set(__instance, "deathHeadPosition", val3); if ((Object)(object)val.spectatePoint != (Object)null && ((Object)val.spectatePoint).name == "BotSpike.SpectatePoint") { val.spectatePoint.position = val3; } } } catch { } } private static Vector3 WantedCollisionScale(PlayerAvatar bot) { //IL_008e: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) try { if ((Reflect.Get(bot, "isCrouching") || Reflect.Get(bot, "isCrawling")) && (Object)(object)PlayerCollision.instance != (Object)null && (Object)(object)PlayerCollision.instance.CrouchCollision != (Object)null) { return ((Component)PlayerCollision.instance.CrouchCollision).transform.localScale; } if ((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.PlayerCollision != (Object)null) { return ((Component)PlayerController.instance.PlayerCollision).transform.localScale; } } catch { } return Vector3.one; } private static void MarkIgnored(GameObject go) { if (!((Object)(object)go == (Object)null) && !((Object)(object)go.GetComponent() != (Object)null)) { go.AddComponent(); Plugin.Log.LogInfo((object)("[Body] Spectate camera will ignore '" + ((Object)go).name + "'.")); } } private static void EnsureEyeTarget(PlayerAvatar avatar) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)avatar == (Object)null) { return; } GameObject gameObject = ((Component)((Component)avatar).transform.root).gameObject; PlayerEyes componentInChildren = gameObject.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { if (Time.unscaledTime > _eyeGripeAt) { _eyeGripeAt = Time.unscaledTime + 60f; Plugin.Log.LogWarning((object)("[Spike] No PlayerEyes anywhere under '" + ((Object)gameObject).name + "' — cannot stop the zero-vector spam at its source.")); } return; } Transform val = EnsureHeadTransform(((Component)avatar).gameObject); if (!((Object)(object)val == (Object)null)) { Transform val2 = val.Find("BotSpike.LookAt"); if ((Object)(object)val2 == (Object)null) { val2 = new GameObject("BotSpike.LookAt").transform; val2.SetParent(val, false); } val2.localPosition = new Vector3(0f, 0f, 2f); if ((Object)(object)componentInChildren.lookAt != (Object)(object)val2) { componentInChildren.lookAt = val2; Plugin.Log.LogInfo((object)"[Spike] Gave his eyes a look-at target — without one, PlayerAvatarVisuals does LookRotation on a zero vector every frame."); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] EnsureEyeTarget: " + ex.Message)); } } private static Transform EnsureHeadTransform(GameObject bot) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Transform val = bot.transform.Find("BotSpike.Head"); if ((Object)(object)val != (Object)null) { return val; } GameObject val2 = new GameObject("BotSpike.Head"); val2.transform.SetParent(bot.transform, false); val2.transform.localPosition = new Vector3(0f, 1.3f, 0f); return val2.transform; } private static void GiveVisionTarget(GameObject bot, PlayerAvatar avatar) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || (Object)(object)avatar == (Object)null) { return; } try { PlayerVisionTarget val = Reflect.Get(avatar, "PlayerVisionTarget"); if (!((Object)(object)val != (Object)null) || !((Object)(object)val.VisionTransform != (Object)null)) { Transform visionTransform = EnsureHeadTransform(bot); if ((Object)(object)val != (Object)null) { val.VisionTransform = visionTransform; Plugin.Log.LogInfo((object)"[Spike] Existing PlayerVisionTarget had a null VisionTransform — assigned his head. (This is what muted him.)"); return; } GameObject val2 = new GameObject("BotSpike.VisionHolder"); val2.SetActive(false); GameObject val3 = new GameObject("BotSpike.VisionTarget"); val3.transform.SetParent(val2.transform, false); PlayerVisionTarget val4 = val3.AddComponent(); ((Behaviour)val4).enabled = false; val4.VisionTransform = visionTransform; val3.transform.SetParent(bot.transform, false); val3.transform.localPosition = Vector3.zero; Object.Destroy((Object)(object)val2); Reflect.Set(avatar, "PlayerVisionTarget", val4); Plugin.Log.LogInfo((object)"[Spike] Gave the bot a vision target (PlayerAvatar.Update needs it)."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] GiveVisionTarget: " + ex.Message)); } } private static void KeepPositionTracking(PlayerAvatar a) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)a == (Object)null) && !((Object)(object)a.photonView == (Object)null) && !a.photonView.IsMine) { Reflect.Set(a, "clientPositionCurrent", Reflect.Get(a, "clientPosition")); Quaternion val = Reflect.Get(a, "clientRotation"); if (Mathf.Abs(val.x) + Mathf.Abs(val.y) + Mathf.Abs(val.z) + Mathf.Abs(val.w) > 0.0001f) { Reflect.Set(a, "clientRotationCurrent", val); } } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "Update")] [HarmonyFinalizer] public static Exception AvatarUpdateFinalizer(PlayerAvatar __instance, Exception __exception) { if (__exception == null) { return null; } if (Bots.Count == 0) { return __exception; } if (IsBot(__instance)) { KeepPositionTracking(__instance); } if (IsBot(__instance) && Time.unscaledTime > _probeAt) { _probeAt = Time.unscaledTime + 30f; try { object obj = Reflect.Get(__instance, "PlayerVisionTarget"); object obj2 = ((obj != null) ? Reflect.Get(obj, "VisionTransform") : null); object obj3 = Reflect.Get(__instance, "voiceChat"); Object val = (Object)((obj3 is Object) ? obj3 : null); bool flag = val != null && val == (Object)null; Plugin.Log.LogWarning((object)("[Probe] Update NRE suspects — PlayerVisionTarget=" + ((obj == null) ? "NULL" : "ok") + " VisionTransform=" + ((obj2 == null) ? "NULL" : "ok") + " voiceChat=" + ((obj3 == null) ? "NULL" : (flag ? "DESTROYED" : "ok")) + " voiceChatFetched=" + Reflect.Get(__instance, "voiceChatFetched") + " playerTransform=" + ((Reflect.Get(__instance, "playerTransform") == null) ? "NULL" : "ok") + " tumble=" + ((Reflect.Get(__instance, "tumble") == null) ? "NULL" : "ok") + " visuals=" + ((Reflect.Get(__instance, "playerAvatarVisuals") == null) ? "NULL" : "ok"))); } catch { } } if ((Object)(object)__instance != (Object)null && Bots.Contains(__instance)) { if (!_loggedUpdateError) { _loggedUpdateError = true; Plugin.Log.LogWarning((object)("[Spike] Swallowing bot PlayerAvatar.Update error (logged once): " + __exception.GetType().Name + ": " + __exception.Message + "\n" + __exception.StackTrace)); } return null; } return __exception; } private static void CreateNameTag(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return; } try { if (!((Object)(object)WorldSpaceUIParent.instance == (Object)null) && !((Object)/*isinst with value type is only supported in some contexts*/ != (Object)null) && !SemiFunc.MenuLevel()) { WorldSpaceUIParent.instance.PlayerName(avatar); object obj = Reflect.Get(avatar, "worldSpaceUIPlayerName"); if ((Object)((obj is Component) ? obj : null) != (Object)null) { Plugin.Log.LogInfo((object)"[Spike] Created name tag for the bot."); } else { Plugin.Log.LogWarning((object)"[Spike] PlayerName() ran but assigned nothing — no tag this level."); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] CreateNameTag: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerNameChecker), "Update")] [HarmonyFinalizer] public static Exception PlayerNameCheckerFinalizer() { return null; } private static void Report() { //IL_01bb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogError((object)"[Spike] Q1 RESULT: FAIL — the bot was DESTROYED (a self-destruct guard fired)."); _reportsLeft = 0; return; } PlayerAvatar val = _bot.GetComponent() ?? _bot.GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[Spike] PlayerAvatar component vanished."); _reportsLeft = 0; return; } bool flag = (Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList.Contains(val); int num = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : (-1)); int num2 = 0; int num3 = 0; Renderer[] componentsInChildren = _bot.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null)) { num2++; if (val2.enabled && ((Component)val2).gameObject.activeInHierarchy) { num3++; } } } bool flag2 = Reflect.Get(val, "isLocal"); PhotonView val3 = val.photonView ?? _bot.GetComponent(); string text = (((Object)(object)val3 == (Object)null) ? "no view" : ((val3.Owner == null) ? "ROOM (null owner)" : val3.Owner.NickName)); Plugin.Log.LogInfo((object)$"[Spike] alive=Y | Q2 inPlayerList={flag} (list={num}) | isLocal={flag2} (must be False) | owner={text} | viewID={(((Object)(object)val3 != (Object)null) ? val3.ViewID : (-1))} | renderers={num2} enabled={num3} | hp={BotHealth} | pos={_bot.transform.position}"); if (flag2) { Plugin.Log.LogError((object)"[Spike] !! isLocal is TRUE on the bot — it thinks it's the human. Patch failed; despawn before it interferes."); } if (_reportsLeft == 0) { Plugin.Log.LogInfo((object)"[Spike] ===== VERDICT ====="); Plugin.Log.LogInfo((object)("[Spike] Q1 survived spawn : " + (((Object)(object)_bot != (Object)null) ? "YES" : "NO"))); Plugin.Log.LogInfo((object)("[Spike] Q2 counted as player: " + (flag ? "YES" : "NO"))); Plugin.Log.LogInfo((object)("[Spike] Q3 renders : check the OTHER client's screen (host sees " + num3 + " enabled renderers)")); Plugin.Log.LogInfo((object)"[Spike] Q4 host can drive : press F11 to orbit it, then watch the client"); Plugin.Log.LogInfo((object)"[Spike] Scan the log above for exceptions — those are the subsystems needing stubs."); } } public static void ToggleDrive() { //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) if ((Object)(object)_bot == (Object)null) { Plugin.Log.LogInfo((object)"[Spike] No bot to drive."); return; } _driving = !_driving; if (_driving) { _driveTarget = _bot.transform.position; } Plugin.Log.LogInfo((object)("[Spike] Walk test " + (_driving ? "ON — Bertram will walk to you (NavMesh + physics)." : "OFF"))); } [HarmonyPatch(typeof(StatsManager), "GetPlayerHealth")] [HarmonyPrefix] public static bool GetPlayerHealthPrefix(string _steamID, ref int __result) { if (!string.IsNullOrEmpty(_steamID)) { return true; } __result = 100; return false; } [HarmonyPatch(typeof(StatsManager), "GetPlayerMaxHealth")] [HarmonyPrefix] public static bool GetPlayerMaxHealthPrefix(string _steamID, ref int __result) { if (!string.IsNullOrEmpty(_steamID)) { return true; } __result = 100; return false; } [HarmonyPatch(typeof(StatsManager), "GetPlayerColor")] [HarmonyPrefix] public static bool GetPlayerColorPrefix(string _steamID, ref int __result) { if (!string.IsNullOrEmpty(_steamID)) { return true; } __result = 0; return false; } [HarmonyPatch(typeof(StatsManager), "FetchPlayerUpgrades")] [HarmonyPrefix] public static bool FetchPlayerUpgradesPrefix(string _steamID, ref Dictionary __result) { if (!string.IsNullOrEmpty(_steamID)) { return true; } __result = new Dictionary(); return false; } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] [HarmonyPrefix] public static bool AwakePrefix(PlayerAvatar __instance) { if ((Object)(object)__instance == (Object)null) { return true; } bool flag = _expectBot || HasBotTag((Component)(object)__instance); if (!flag && !PhotonNetwork.IsMasterClient && WouldBeDestroyedAsDuplicate(__instance)) { _provisional.Add(__instance); Plugin.Log.LogInfo((object)"[Spike] An avatar arrived that the duplicate-owner guard would delete. Holding it one frame to see whether it is his."); flag = true; } if (!flag) { return true; } try { Bots.Add(__instance); if (!_expectBot) { Plugin.Log.LogInfo((object)"[Spike] Recognised the host's bot arriving over the network — skipping the duplicate-owner guard that would have deleted him."); try { GameObject gameObject = ((Component)((Component)__instance).transform.root).gameObject; Object.DontDestroyOnLoad((Object)(object)gameObject); Plugin.Log.LogInfo((object)("[Spike] Claimed his networked copy across level loads ('" + ((Object)gameObject).name + "') — without this a client loses him at the first level.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] DontDestroyOnLoad: " + ex.Message)); } } PhotonView component = ((Component)__instance).GetComponent(); Reflect.Set(__instance, "rb", ((Component)__instance).GetComponent()); Reflect.Set(__instance, "photonView", component); Reflect.Set(__instance, "collider", ((Component)__instance).GetComponentInChildren()); Reflect.Set(__instance, "isDisabled", false); Reflect.Set(__instance, "playerAvatarCollision", ((Component)__instance).GetComponent()); Reflect.Set(__instance, "isLocal", false); Reflect.Set(__instance, "steamID", TaggedName((Component)(object)__instance)); Reflect.Set(__instance, "playerName", TaggedName((Component)(object)__instance)); string text = (((Object)(object)component == (Object)null) ? "no view" : ((component.Owner == null) ? "ROOM (null owner)" : ("actor " + component.Owner.ActorNumber))); Plugin.Log.LogInfo((object)("[Spike] Awake bypassed for bot. view=" + (((Object)(object)component != (Object)null) ? component.ViewID : (-1)) + " owner=" + text)); if ((Object)(object)GameDirector.instance != (Object)null && !GameDirector.instance.PlayerList.Contains(__instance)) { GameDirector.instance.PlayerList.Add(__instance); Plugin.Log.LogInfo((object)("[Spike] Added bot to GameDirector.PlayerList (now " + GameDirector.instance.PlayerList.Count + ").")); } } catch (Exception ex2) { Plugin.Log.LogError((object)("[Spike] AwakePrefix: " + ex2)); } return false; } [HarmonyPatch(typeof(PhysGrabber), "ForceGrabPhysObject")] [HarmonyPrefix] public static bool ForceGrabPrefix(PhysGrabber __instance) { try { if ((Object)(object)__instance == (Object)null) { return true; } PlayerAvatar playerAvatar = __instance.playerAvatar; if ((Object)(object)playerAvatar != (Object)null && Bots.Contains(playerAvatar)) { return false; } } catch { } return true; } [HarmonyPatch(typeof(GumballValuable), "ClientEffectsLoop")] [HarmonyPrefix] public static void GumballLoopPrefix(GumballValuable __instance) { try { int num = Reflect.Get(__instance, "playerCount"); if (num <= 0) { return; } List list = Reflect.Get>(__instance, "allPlayers"); List list2 = Reflect.Get>(__instance, "playerInSight"); List list3 = Reflect.Get>(__instance, "hypnosisLines"); int num2 = num; if (list != null) { num2 = Mathf.Min(num2, list.Count); } if (list2 != null) { num2 = Mathf.Min(num2, list2.Count); } if (list3 != null) { num2 = Mathf.Min(num2, list3.Count); } if (num2 < num) { Reflect.Set(__instance, "playerCount", Mathf.Max(0, num2)); if (Time.unscaledTime > _gumballGripeAt) { _gumballGripeAt = Time.unscaledTime + 30f; Plugin.Log.LogWarning((object)("[Spike] Gumball had playerCount=" + num + " but only " + num2 + " built entries — clamped. (Its setup aborts on the bot's stub camera.)")); } } } catch { } } [HarmonyPatch(typeof(GumballValuable), "UpdateHypnosisLinesList")] [HarmonyFinalizer] public static Exception GumballLinesFinalizer() { return null; } [HarmonyPatch(typeof(GumballValuable), "CheckForLeftPlayers")] [HarmonyFinalizer] public static Exception GumballLeftPlayersFinalizer() { return null; } [HarmonyPatch(typeof(PlayerAvatar), "OnDestroy")] [HarmonyPrefix] public static void OnDestroyPrefix(PlayerAvatar __instance) { if (IsBot(__instance)) { HandleUnexpectedDestroy(__instance); } try { if (!((Object)(object)__instance == (Object)null) && Bots.Contains(__instance)) { Plugin.Log.LogError((object)("[Spike] BOT IS BEING DESTROYED. Caller stack:\n" + new StackTrace(fNeedFileInfo: true))); } } catch { } } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] [HarmonyPostfix] public static void AwakePostfix(PlayerAvatar __instance) { try { if (!((Object)(object)__instance == (Object)null) && Bots.Contains(__instance)) { Reflect.Set(__instance, "isLocal", false); Reflect.Set(__instance, "steamID", TaggedName((Component)(object)__instance)); Reflect.Set(__instance, "playerName", TaggedName((Component)(object)__instance)); Plugin.Log.LogInfo((object)("[Spike] Awake: bot forced to isLocal=false, steamID=" + TaggedName((Component)(object)__instance))); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Spike] AwakePostfix: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerAvatar), "Start")] [HarmonyPrefix] public static bool StartPrefix(PlayerAvatar __instance) { if ((Object)(object)__instance == (Object)null || !Bots.Contains(__instance)) { return true; } Plugin.Log.LogInfo((object)"[Spike] Start: intercepted for bot (skipping the local-player hijack)."); if (!PhotonNetwork.IsMasterClient) { try { MakeVisible(((Component)__instance).gameObject); Plugin.Log.LogInfo((object)"[Spike] Client copy: ran the normal Start and un-hid his renderers."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] Client visibility: " + ex.Message)); } return true; } SafeInvoke(__instance, "SoundsSetup"); RegisterOwnIdentity(__instance); try { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerAvatar), "LateStart", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.Invoke(__instance, null) is IEnumerator enumerator) { ((MonoBehaviour)__instance).StartCoroutine(enumerator); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Spike] LateStart failed (stub needed): " + ex2.Message)); } return false; } private static void SafeInvoke(object target, string method) { try { MethodInfo methodInfo = AccessTools.Method(target.GetType(), method, (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)("[Spike] " + method + " not found.")); return; } methodInfo.Invoke(target, null); Plugin.Log.LogInfo((object)("[Spike] " + method + " OK.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Spike] " + method + " threw (stub needed): " + ((ex.InnerException != null) ? ex.InnerException.Message : ex.Message))); } } } public static class BotStuck { private const float RecallGrace = 25f; private const float WedgedAfter = 12f; private static float _recallFor; private static bool _recalling; private static bool _saidRecall; private static Vector3 _lastPos; private static bool _havePos; private static float _noProgressFor; private static float _nextUnstickAt; public static bool Recalling => _recalling; public static void Reset() { _recallFor = 0f; _recalling = false; _saidRecall = false; _havePos = false; _noProgressFor = 0f; } public static void Tick(PlayerAvatar bot, float dt) { try { if ((Object)(object)bot == (Object)null) { Reset(); } else if (Reflect.Get(bot, "isDisabled")) { Reset(); } else if (!SemiFunc.RunIsLevel()) { _recalling = false; _noProgressFor = 0f; } else if (!TickFellOutOfTheWorld(bot) && !BotEntrance.Dormant) { TickTruckRecall(bot, dt); TickWedged(bot, dt); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Stuck] " + ex.Message)); } } private static bool TickFellOutOfTheWorld(PlayerAvatar bot) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) try { GameObject botObject = BotSpawner.BotObject; if ((Object)(object)botObject == (Object)null) { return false; } float num = float.MaxValue; int num2 = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { num2++; float y = ((Component)player).transform.position.y; if (y < num) { num = y; } } } if (num2 == 0) { return false; } float num3 = num - botObject.transform.position.y; if (num3 < 25f) { return false; } Plugin.Log.LogError((object)("[Stuck] He is " + num3.ToString("F0") + "m below the lowest crewmate — he has fallen out of the level. Recovering him; while he is down there and alive, the run cannot end.")); if (!TruckSpot(out var spot)) { foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList) { if (!((Object)(object)player2 == (Object)null) && !BotSpawner.IsBot(player2)) { spot = ((Component)player2).transform.position; break; } } } PutHim(bot, spot); return true; } catch { return false; } } private static void TickTruckRecall(PlayerAvatar bot, float dt) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!HumansAllAboard() || InTruck(bot)) { if (_recalling) { Plugin.Log.LogInfo((object)"[Stuck] He's aboard — recall over."); } _recalling = false; _recallFor = 0f; _saidRecall = false; return; } if (!_recalling) { _recalling = true; _recallFor = 0f; BotSpawner.HaltEverything(((Component)bot).transform.position, announce: false); Plugin.Log.LogWarning((object)"[Stuck] Everyone else is aboard and he isn't — recalling him to the truck. PlayersAllInTruck() is false while he's out here, so the level cannot end."); } _recallFor += dt; if (TruckSpot(out var spot)) { BotMovement.SetDestination(spot); BotMovement.SpeedScale = 1f; if (!_saidRecall && _recallFor > 1.5f) { _saidRecall = true; BotPersona.SpeakExact(BotPersona.Recalled, ((Component)bot).transform.position); } if (_recallFor > 25f) { _recallFor = 0f; Plugin.Log.LogWarning((object)("[Stuck] He couldn't walk it in " + 25 + "s — putting him in the truck by hand rather than strand the run.")); BotPersona.SpeakExact(BotPersona.RecallTeleport, ((Component)bot).transform.position); PutHim(bot, spot); } } } private static bool HumansAllAboard() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return false; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "isDisabled")) { num++; if (!InTruck(player)) { return false; } } } return num > 0; } catch { return false; } } private static bool InTruck(PlayerAvatar p) { try { object obj = Reflect.Get(p, "RoomVolumeCheck"); return obj != null && Reflect.Get(obj, "inTruck"); } catch { return false; } } private static bool TruckSpot(out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; try { if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { spot = ((Component)TruckSafetySpawnPoint.instance).transform.position; return true; } if ((Object)(object)GameDirector.instance != (Object)null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && InTruck(player)) { spot = ((Component)player).transform.position; return true; } } } } catch { } return false; } private unsafe static void PutHim(PlayerAvatar bot, Vector3 spot) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) try { GameObject botObject = BotSpawner.BotObject; if (!((Object)(object)botObject == (Object)null)) { NavMeshHit val2 = default(NavMeshHit); Vector3 val = (NavMesh.SamplePosition(spot, ref val2, 4f, -1) ? (((NavMeshHit)(ref val2)).position + Vector3.up * 0.2f) : (spot + Vector3.up * 0.2f)); Rigidbody val3 = botObject.GetComponent() ?? botObject.GetComponentInChildren(true); if ((Object)(object)val3 != (Object)null) { val3.velocity = Vector3.zero; val3.angularVelocity = Vector3.zero; val3.position = val; } botObject.transform.position = val; BotMovement.Reset(); BotMovement.SetDestination(val); ManualLogSource log = Plugin.Log; Vector3 val4 = val; log.LogWarning((object)("[Stuck] Teleported him to " + ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString() + ".")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Stuck] PutHim: " + ex.Message)); } } private unsafe static void TickWedged(PlayerAvatar bot, float dt) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) GameObject botObject = BotSpawner.BotObject; if ((Object)(object)botObject == (Object)null) { return; } Vector3 position = botObject.transform.position; if (!BotMovement.HasGoal || BotDance.Dancing || BotHush.Active) { _noProgressFor = 0f; _lastPos = position; _havePos = true; return; } if (_havePos && Vector3.Distance(position, _lastPos) < 0.25f) { _noProgressFor += dt; } else { _noProgressFor = 0f; } _lastPos = position; _havePos = true; if (!(_noProgressFor < 12f) && !(Time.unscaledTime < _nextUnstickAt)) { _noProgressFor = 0f; _nextUnstickAt = Time.unscaledTime + 20f; NavMeshHit val = default(NavMeshHit); if (NavMesh.SamplePosition(position, ref val, 6f, -1) && Vector3.Distance(((NavMeshHit)(ref val)).position, position) > 0.6f) { ManualLogSource log = Plugin.Log; string[] obj = new string[7] { "[Stuck] Wedged for ", 12.ToString(), "s with a live goal — nudging him back onto the navmesh from ", null, null, null, null }; Vector3 val2 = position; obj[3] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString(); obj[4] = " to "; obj[5] = ((object)((NavMeshHit)(ref val)).position/*cast due to .constrained prefix*/).ToString(); obj[6] = "."; log.LogWarning((object)string.Concat(obj)); PutHim(bot, ((NavMeshHit)(ref val)).position); } else { Plugin.Log.LogWarning((object)("[Stuck] No progress toward his goal for " + 12 + "s — abandoning it.")); BotMovement.Stop(); BotHands.Reset(); } } } } public static class BotTattle { private sealed class Report { public string Culprit; public int CulpritId; public string Item; public int Value; public float At; public readonly HashSet Witnesses = new HashSet(); public readonly HashSet Told = new HashSet(); } private static readonly List Pending = new List(); private static float _nextCheck; private const float WitnessRange = 18f; private const float TellRange = 8f; private const float ReportLife = 240f; private const int MaxPending = 6; public static void Reset() { Pending.Clear(); } public static void Witnessed(Component thing, PlayerAvatar culprit, int value) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: 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_00e2: Unknown result type (might be due to invalid IL or missing references) try { if (!BotSpawner.HasBot || (Object)(object)thing == (Object)null || (Object)(object)culprit == (Object)null || BotSpawner.IsBot(culprit) || value < 50) { return; } Vector3 botPosition = BotSpawner.BotPosition; Vector3 position = thing.transform.position; if (Vector3.Distance(botPosition, position) > 18f || !CanSee(botPosition, position)) { return; } Report report = new Report { Culprit = SemiFunc.PlayerGetName(culprit), CulpritId = ((Object)culprit).GetInstanceID(), Item = PrettyName(thing), Value = value, At = Time.unscaledTime }; foreach (PlayerAvatar item in Players()) { if (!((Object)(object)item == (Object)null)) { Vector3 position2 = ((Component)item).transform.position; if (Vector3.Distance(position2, position) <= 18f && CanSee(position2, position)) { report.Witnesses.Add(((Object)item).GetInstanceID()); } } } report.Witnesses.Add(report.CulpritId); Pending.Add(report); while (Pending.Count > 6) { Pending.RemoveAt(0); } Plugin.Log.LogInfo((object)("[Tattle] Noted: " + report.Culprit + " destroyed " + report.Item + " ($" + report.Value + "). " + report.Witnesses.Count + " witness(es) — waiting to tell someone else.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Tattle] " + ex.Message)); } } public static void Tick(float dt) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (Pending.Count == 0 || !BotSpawner.HasBot || Time.unscaledTime < _nextCheck) { return; } _nextCheck = Time.unscaledTime + 1f; try { for (int num = Pending.Count - 1; num >= 0; num--) { if (Time.unscaledTime - Pending[num].At > 240f) { Pending.RemoveAt(num); } } if (Pending.Count == 0) { return; } Vector3 botPosition = BotSpawner.BotPosition; foreach (PlayerAvatar item in Players()) { if ((Object)(object)item == (Object)null) { continue; } int instanceID = ((Object)item).GetInstanceID(); Vector3 position = ((Component)item).transform.position; if (Vector3.Distance(botPosition, position) > 8f || !CanSee(botPosition, position)) { continue; } for (int i = 0; i < Pending.Count; i++) { Report report = Pending[i]; if (!report.Witnesses.Contains(instanceID) && !report.Told.Contains(instanceID)) { report.Told.Add(instanceID); Speak(report, botPosition); if (report.Told.Count + report.Witnesses.Count >= CountPlayers()) { Pending.RemoveAt(i); } return; } } } } catch { } } private static void Speak(Report r, Vector3 botPos) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) string text = Line(r); Plugin.Log.LogInfo((object)("[Tattle] Telling on " + r.Culprit + ": " + text)); BotPersona.SpeakLiteral(text, botPos); } private static string Line(Report r) { string text = "$" + r.Value.ToString("N0"); string[] array = ((!BotPersona.Nsfw) ? new string[5] { r.Culprit + " fudged a grab and destroyed a " + r.Item + " worth " + text + ". Butterfingers.", "You missed it. " + r.Culprit + " turned a " + text + " " + r.Item + " into scrap.", "Ask " + r.Culprit + " about the " + r.Item + ". Go on. " + text + ", that was.", "Just so you know, " + r.Culprit + " binned a " + r.Item + ". " + text + ". Gone.", r.Culprit + " owes the company " + text + " for a " + r.Item + ". Clumsy." } : new string[5] { r.Culprit + " fudged a grab and destroyed a " + r.Item + " worth " + text + ". Butterfingers.", "You missed it. " + r.Culprit + " turned a " + text + " " + r.Item + " into scrap.", "Ask " + r.Culprit + " about the " + r.Item + ". Go on. " + text + ", that was.", "Just so you know, " + r.Culprit + " binned a " + r.Item + ". " + text + ". Gone.", r.Culprit + " owes the company " + text + " for a " + r.Item + ". Clumsy sod." }); return array[Random.Range(0, array.Length)]; } private static string PrettyName(Component thing) { try { string text = ((Object)thing).name ?? "thing"; int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } text = text.Replace("Valuable", "").Replace("_", " ").Trim(); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < text.Length; i++) { if (i > 0 && char.IsUpper(text[i]) && !char.IsUpper(text[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(text[i]); } string text2 = stringBuilder.ToString().Trim().ToLowerInvariant(); while (text2.Contains(" ")) { text2 = text2.Replace(" ", " "); } return string.IsNullOrEmpty(text2) ? "thing" : text2; } catch { return "thing"; } } private static IEnumerable Players() { if ((Object)(object)GameDirector.instance == (Object)null) { yield break; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player) && !Reflect.Get(player, "isDisabled")) { yield return player; } } } private static int CountPlayers() { int num = 0; foreach (PlayerAvatar item in Players()) { _ = item; num++; } return num; } private static bool CanSee(Vector3 from, Vector3 to) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { int blockers = BotSight.Blockers; if (blockers == 0) { return true; } return !Physics.Linecast(from + Vector3.up * 1.2f, to + Vector3.up * 0.5f, blockers, (QueryTriggerInteraction)1); } catch { return true; } } } [HarmonyPatch] public static class BotTumbleGuard { private static float _gripeAt; public static IEnumerable TargetMethods() { string[] array = new string[9] { "TumbleImpactRPC", "TumbleOverrideRPC", "TumbleOverrideTimeRPC", "TumbleForceRPC", "TumbleTorqueRPC", "TumbleRequestRPC", "TumbleSetRPC", "ImpactHurtSetRPC", "FixedUpdate" }; string[] array2 = array; foreach (string text in array2) { MethodBase methodBase = AccessTools.Method(typeof(PlayerTumble), text, (Type[])null, (Type[])null); if (methodBase != null) { yield return methodBase; } } } [HarmonyFinalizer] public static Exception Guard(PlayerTumble __instance, Exception __exception, MethodBase __originalMethod) { if (__exception == null) { return null; } try { object obj = Reflect.Get(__instance, "playerAvatar"); if ((Object)((obj is Component) ? obj : null) != (Object)null) { return __exception; } if (Time.unscaledTime > _gripeAt) { _gripeAt = Time.unscaledTime + 20f; Plugin.Log.LogWarning((object)("[Tumble] Swallowed " + __originalMethod.Name + " on an ownerless tumble body. Left alone this aborts the whole incoming Photon batch, dropping unrelated RPCs with it.")); } return null; } catch { return __exception; } } } public static class BotVoiceClarity { public static int MsPerFrame { get { if (Plugin.CfgTtsFrameMs == null) { return 14; } return Plugin.CfgTtsFrameMs.Value; } } public static int Flutter { get { if (Plugin.CfgTtsFlutter == null) { return 4; } return Plugin.CfgTtsFlutter.Value; } } public static void TuneSynth(GameObject ttsObject) { try { if ((Object)(object)ttsObject == (Object)null) { return; } int num = 0; Speech[] componentsInChildren = ttsObject.GetComponentsInChildren(true); foreach (Speech val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { val.msPerSpeechFrame = Mathf.Clamp(MsPerFrame, 1, 100); val.flutter = Mathf.Clamp(Flutter, 0, 200); num++; } } Plugin.Log.LogInfo((object)("[Voice] Tuned " + num + " synth voice(s): " + MsPerFrame + "ms/frame, flutter " + Flutter + " (engine defaults are 10 and 10).")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Voice] TuneSynth: " + ex.Message)); } } [HarmonyPatch(typeof(PlayerVoiceChat), "TtsFollowVoiceSettings")] [HarmonyPostfix] public static void ClarityPostfix(PlayerVoiceChat __instance) { try { if (Plugin.CfgTtsClarity != null && !Plugin.CfgTtsClarity.Value) { return; } PlayerAvatar val = Reflect.Get(__instance, "playerAvatar"); if ((Object)(object)val == (Object)null || !BotSpawner.IsBot(val)) { return; } AudioSource val2 = Reflect.Get(__instance, "ttsAudioSource"); if (!((Object)(object)val2 == (Object)null)) { float num = 1f + Reflect.Get(__instance, "TTSPitchChange"); num *= Reflect.Get(__instance, "pitchMultiplier"); float num2 = ((Plugin.CfgTtsPitchMultiplier != null) ? Plugin.CfgTtsPitchMultiplier.Value : 1f); num *= num2; if (Reflect.Get(val, "isDisabled")) { num = 1f; } if (num <= 0.01f) { num = 1f; } AudioSource val3 = Reflect.Get(__instance, "audioSource"); float num3 = (((Object)(object)val3 != (Object)null) ? val3.pitch : 1f); if (num3 <= 0.01f) { num3 = 1f; } val2.pitch = num3 * num; } } catch { } } } [HarmonyPatch] public static class BotVoiceSpatial { private struct Ref { public bool Ok; public float Min; public float Max; public AudioRolloffMode Mode; } private static float _logAt; private static readonly HashSet Said = new HashSet(); private static void Once(string key, string msg) { if (Said.Add(key)) { Plugin.Log.LogInfo((object)("[Voice] " + msg)); } } [HarmonyPatch(typeof(PlayerVoiceChat), "Update")] [HarmonyPostfix] public static void ForceThreeDee(PlayerVoiceChat __instance) { try { if (Plugin.CfgSpatialVoice != null && !Plugin.CfgSpatialVoice.Value) { return; } PlayerAvatar val = Reflect.Get(__instance, "playerAvatar"); if ((Object)(object)val == (Object)null || !BotSpawner.IsBot(val)) { return; } if (BotMovement.InLobby()) { AudioSource val2 = Reflect.Get(__instance, "audioSource"); AudioSource val3 = Reflect.Get(__instance, "ttsAudioSource"); SilenceKlattersynthIfEspeaking(__instance, val3); if ((Object)(object)val2 != (Object)null && val2.spatialBlend > 0.001f) { val2.spatialBlend = 0f; } if ((Object)(object)val3 != (Object)null && val3.spatialBlend > 0.001f) { val3.spatialBlend = 0f; Once("lobbyflat", "Flattened his voice for the lobby. He was the only positional voice in a room where everyone else is 2D — 42m away with a 20m range, i.e. perfectly synthesised and completely inaudible."); } return; } AudioSource src = Reflect.Get(__instance, "audioSource"); AudioSource val4 = Reflect.Get(__instance, "ttsAudioSource"); SilenceKlattersynthIfEspeaking(__instance, val4); Ref model = Sample(); bool flag = false; if (Apply(src, Reflect.Get(__instance, "lowPassLogic"), model)) { flag = true; } if (Apply(val4, Reflect.Get(__instance, "lowPassLogicTTS"), model)) { flag = true; } if (flag && Time.unscaledTime > _logAt) { _logAt = Time.unscaledTime + 30f; Plugin.Log.LogInfo((object)"[Voice] Put his voice back into 3D. The host owns his Voice object, so the game's \"hear yourself\" rule was flattening him to 2D — audible at full volume from anywhere in the level."); } } catch { } } private static void SilenceKlattersynthIfEspeaking(PlayerVoiceChat vcc, AudioSource tts) { try { if (!((Object)(object)tts == (Object)null)) { if (tts.mute) { tts.mute = false; } float num = (BotEspeak.Active ? 0f : 1f); if (!Mathf.Approximately(tts.volume, num)) { tts.volume = num; } object obj = Reflect.Get(vcc, "lowPassLogicTTS"); if (obj != null && !Mathf.Approximately(Reflect.Get(obj, "Volume"), num)) { Reflect.Set(obj, "Volume", num); } } } catch { } } private static Ref Sample() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GameDirector.instance == (Object)null) { return default(Ref); } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !BotSpawner.IsBot(player)) { PlayerVoiceChat val = Reflect.Get(player, "voiceChat"); AudioSource val2 = (((Object)(object)val != (Object)null) ? Reflect.Get(val, "audioSource") : null); if (!((Object)(object)val2 == (Object)null) && !(val2.maxDistance <= 1f)) { return new Ref { Ok = true, Min = val2.minDistance, Max = val2.maxDistance, Mode = val2.rolloffMode }; } } } } catch { } return default(Ref); } private static bool Apply(AudioSource src, object lowPass, Ref model) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)src == (Object)null) { return false; } bool result = false; if (src.spatialBlend < 0.999f) { src.spatialBlend = 1f; result = true; } float num = (model.Ok ? model.Max : 25f); if (src.maxDistance < num * 0.9f) { src.minDistance = (model.Ok ? model.Min : 1f); src.maxDistance = num; if (model.Ok) { src.rolloffMode = model.Mode; } result = true; } if (lowPass != null) { if (Reflect.Get(lowPass, "SpatialBlend") < 0.999f) { Reflect.Set(lowPass, "SpatialBlend", 1f); result = true; } if (Reflect.Get(lowPass, "Falloff") < num * 0.9f) { Reflect.Set(lowPass, "Falloff", num); result = true; } } return result; } } public static class BotVoiceTap { public struct Chunk { public int SpeakerId; public float[] Samples; public int Rate; } private sealed class Stream { public PlayerVoiceChat Vc; public PlayerAvatar Avatar; public int LastPos; public bool WasTalking; public float SilenceFor; public float Peak; } private static readonly Dictionary Streams = new Dictionary(); private static readonly ConcurrentQueue Out = new ConcurrentQueue(); private static float _rescanAt; private static float _reportAt; private static int _tappedCount; public static int TappedSpeakers => _tappedCount; public static bool TryDequeue(out Chunk c) { return Out.TryDequeue(out c); } public static void Submit(Chunk c) { if (c.Samples != null && c.Samples.Length != 0 && Out.Count < 60) { Out.Enqueue(c); } } public static void Reset() { Streams.Clear(); Chunk result; while (Out.TryDequeue(out result)) { } _tappedCount = 0; } public static void Tick(float dt) { try { if (Time.unscaledTime > _rescanAt) { _rescanAt = Time.unscaledTime + 2f; Rescan(); } int num = 0; foreach (KeyValuePair stream in Streams) { Stream value = stream.Value; if ((Object)(object)value.Vc == (Object)null || (Object)(object)value.Avatar == (Object)null) { continue; } AudioSource val = Reflect.Get(value.Vc, "audioSource"); if ((Object)(object)val == (Object)null || (Object)(object)val.clip == (Object)null || !val.isPlaying) { continue; } bool flag = Reflect.Get(value.Vc, "isTalking"); if (flag) { num++; } if (!flag && BotSelfHear.Active && (Object)(object)value.Avatar == (Object)(object)PlayerAvatar.instance) { flag = true; } if (!flag) { value.LastPos = val.timeSamples; value.WasTalking = false; continue; } value.WasTalking = true; int timeSamples = val.timeSamples; int samples = val.clip.samples; if (samples <= 0 || timeSamples == value.LastPos) { continue; } int num2 = (timeSamples - value.LastPos + samples) % samples; if (num2 <= 0) { continue; } int num3 = Mathf.Min(samples - 1, val.clip.frequency); if (num2 > num3) { value.LastPos = (timeSamples - num3 + samples) % samples; num2 = num3; } float[] array = ReadWrapped(val.clip, value.LastPos, num2, samples); value.LastPos = timeSamples; if (array == null) { continue; } for (int i = 0; i < array.Length; i++) { float num4 = ((array[i] < 0f) ? (0f - array[i]) : array[i]); if (num4 > value.Peak) { value.Peak = num4; } } Out.Enqueue(new Chunk { SpeakerId = ((Object)value.Avatar).GetInstanceID(), Samples = array, Rate = val.clip.frequency }); } if (!(Time.unscaledTime > _reportAt) || Streams.Count <= 0) { return; } _reportAt = Time.unscaledTime + 20f; StringBuilder stringBuilder = new StringBuilder("[Tap] "); foreach (KeyValuePair stream2 in Streams) { Stream value2 = stream2.Value; string value3 = (((Object)(object)value2.Avatar != (Object)null) ? SemiFunc.PlayerGetName(value2.Avatar) : "?"); AudioSource val2 = (((Object)(object)value2.Vc != (Object)null) ? Reflect.Get(value2.Vc, "audioSource") : null); string value4 = ((!((Object)(object)val2 == (Object)null)) ? ((!((Object)(object)val2.clip == (Object)null)) ? (val2.isPlaying ? ("ok " + val2.clip.frequency + "Hz peak=" + value2.Peak.ToString("F3")) : "not playing") : "NO CLIP (cannot hear them)") : "NO AUDIOSOURCE"); bool flag2 = (Object)(object)value2.Vc != (Object)null && Reflect.Get(value2.Vc, "isTalking"); stringBuilder.Append(value3).Append("[").Append(value4) .Append(flag2 ? ", TALKING" : "") .Append("] "); value2.Peak = 0f; } Plugin.Log.LogInfo((object)stringBuilder.ToString()); } catch (Exception ex) { if (Time.frameCount % 600 == 0) { Plugin.Log.LogWarning((object)("[Tap] " + ex.Message)); } } } private static float[] ReadWrapped(AudioClip clip, int from, int count, int len) { try { int num = Mathf.Max(1, clip.channels); float[] array; if (from + count <= len) { array = new float[count * num]; clip.GetData(array, from); } else { int num2 = len - from; float[] array2 = new float[num2 * num]; float[] array3 = new float[(count - num2) * num]; clip.GetData(array2, from); clip.GetData(array3, 0); array = new float[array2.Length + array3.Length]; Array.Copy(array2, 0, array, 0, array2.Length); Array.Copy(array3, 0, array, array2.Length, array3.Length); } if (num == 1) { return array; } float[] array4 = new float[array.Length / num]; for (int i = 0; i < array4.Length; i++) { float num3 = 0f; for (int j = 0; j < num; j++) { num3 += array[i * num + j]; } array4[i] = num3 / (float)num; } return array4; } catch { return null; } } private static void Rescan() { try { if ((Object)(object)GameDirector.instance == (Object)null) { return; } HashSet hashSet = new HashSet(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || BotSpawner.IsBot(player)) { continue; } PlayerVoiceChat val = Reflect.Get(player, "voiceChat"); if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)player).GetInstanceID(); hashSet.Add(instanceID); if (!Streams.TryGetValue(instanceID, out var value)) { value = new Stream { Vc = val, Avatar = player }; Streams[instanceID] = value; Plugin.Log.LogInfo((object)("[Tap] Now hearing " + SemiFunc.PlayerGetName(player) + ".")); } else { value.Vc = val; value.Avatar = player; } } } List list = null; foreach (KeyValuePair stream in Streams) { if (!hashSet.Contains(stream.Key)) { (list ?? (list = new List())).Add(stream.Key); } } if (list != null) { foreach (int item in list) { Streams.Remove(item); } } _tappedCount = Streams.Count; } catch { } } } public static class BotWeapons { private const float NoticeRange = 12f; private const float DeliveredRange = 3.5f; private static readonly HashSet Handled = new HashSet(); private static PhysGrabObject _carrying; private static PlayerAvatar _recipient; private static float _scanAt; private static bool _offering; private static float _offerUntil; private static float _nagAt; public static void Reset() { Handled.Clear(); Finish(); _scanAt = 0f; } private static void Finish() { _carrying = null; _recipient = null; _offering = false; BotHands.OfferTarget = null; } public static bool Tick(GameObject bot, PlayerAvatar avatar, float dt) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_018e: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0258: 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_0272: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bot == (Object)null || (Object)(object)avatar == (Object)null) { return false; } Vector3 position = bot.transform.position; try { if ((Object)(object)_carrying != (Object)null) { if (!StillValid(_carrying) || (Object)(object)_recipient == (Object)null) { _carrying = null; _recipient = null; return false; } Vector3 position2 = ((Component)_recipient).transform.position; if (_offering) { PlayerAvatar val = Reflect.Get(_carrying, "lastPlayerGrabbing"); if ((Object)(object)val != (Object)null && !BotSpawner.IsBot(val)) { BotHands.OfferTarget = null; BotHands.DropEverything(); BotPersona.SpeakExact(BotPersona.WeaponHandover, position, SemiFunc.PlayerGetName(val)); Plugin.Log.LogInfo((object)("[Weapons] " + SemiFunc.PlayerGetName(val) + " took it.")); Finish(); return false; } if (Time.unscaledTime > _offerUntil || Vector3.Distance(position, position2) > 8f) { BotHands.OfferTarget = null; BotHands.DropEverything(); BotPersona.SpeakExact(BotPersona.WeaponHandover, position, SemiFunc.PlayerGetName(_recipient)); Plugin.Log.LogInfo((object)"[Weapons] Nobody took it — left it with them."); Finish(); return false; } if (Time.unscaledTime > _nagAt) { _nagAt = Time.unscaledTime + 6f; BotPersona.Speak("weapnag", BotPersona.WeaponIgnored, 1f, 6f, position); } BotHands.OfferTarget = _recipient; return true; } if (Vector3.Distance(position, position2) <= 3.5f) { _offering = true; _offerUntil = Time.unscaledTime + 25f; _nagAt = Time.unscaledTime + 5f; BotHands.OfferTarget = _recipient; BotMovement.FaceTowards(position2); Plugin.Log.LogInfo((object)("[Weapons] Holding it out for " + SemiFunc.PlayerGetName(_recipient) + " to take.")); return true; } return true; } if (Time.unscaledTime < _scanAt) { return false; } _scanAt = Time.unscaledTime + 2f; if (!SemiFunc.RunIsLevel()) { return false; } if (BotHands.Current != BotHands.Job.Idle) { return false; } if (BotHands.WorkPaused) { return false; } string what; PhysGrabObject val2 = NearestLooseWeapon(position, out what); if ((Object)(object)val2 == (Object)null) { return false; } PlayerAvatar val3 = Recipient(position); if ((Object)(object)val3 == (Object)null) { return false; } Handled.Add(((Object)val2).GetInstanceID()); if (!BotHands.DeliverTo(val2, ((Component)val3).transform.position, SemiFunc.PlayerGetName(val3), 45f)) { return false; } _carrying = val2; _recipient = val3; BotPersona.SpeakExact(BotPersona.WeaponFound, position, what); Plugin.Log.LogInfo((object)("[Weapons] Found a " + what + " — taking it to " + SemiFunc.PlayerGetName(val3) + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Weapons] " + ex.Message)); } return false; } private static PhysGrabObject NearestLooseWeapon(Vector3 from, out string what) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) what = "weapon"; PhysGrabObject result = null; float num = 12f; try { ItemAttributes[] array = Object.FindObjectsOfType(); foreach (ItemAttributes val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.item == (Object)null || !IsWeapon(val.item.itemType)) { continue; } PhysGrabObject val2 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInParent(); if (!((Object)(object)val2 == (Object)null) && !Handled.Contains(((Object)val2).GetInstanceID()) && !Reflect.Get(val2, "grabbed")) { float num2 = Vector3.Distance(from, ((Component)val2).transform.position); if (!(num2 >= num)) { num = num2; result = val2; what = Describe(val.item.itemType); } } } } catch { } return result; } private static bool IsWeapon(itemType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 if ((int)t != 9 && (int)t != 6 && (int)t != 7 && (int)t != 11) { return (int)t == 15; } return true; } private static string Describe(itemType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 switch (t - 6) { default: if ((int)t != 15) { break; } return "launcher"; case 3: return "gun"; case 0: return "grenade"; case 1: return "melee weapon"; case 5: return "mine"; case 2: case 4: break; } return "weapon"; } private static PlayerAvatar Recipient(Vector3 from) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) try { PlayerAvatar current = BotBuddy.Current; if (Alive(current) && Vector3.Distance(from, ((Component)current).transform.position) < 30f) { return current; } PlayerAvatar result = null; float num = float.MaxValue; if ((Object)(object)GameDirector.instance == (Object)null) { return null; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (Alive(player)) { float num2 = Vector3.Distance(from, ((Component)player).transform.position); if (num2 < num) { num = num2; result = player; } } } return result; } catch { return null; } } private static bool Alive(PlayerAvatar p) { if ((Object)(object)p != (Object)null && !BotSpawner.IsBot(p)) { return !Reflect.Get(p, "isDisabled"); } return false; } private static bool StillValid(PhysGrabObject o) { try { return (Object)(object)o != (Object)null && (Object)(object)((Component)o).gameObject != (Object)null; } catch { return false; } } } public static class LevelGate { private static object _genSeen; private static int _reported; private static bool _placed; private static float _stuckFor; private static float _logTimer; private static float _spawnStateFor; private static bool _forced; private static float _errorAt; public static void Reset() { _genSeen = null; _reported = 0; _placed = false; _stuckFor = 0f; _spawnStateFor = 0f; _forced = false; } public unsafe static void Tick(float dt) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Invalid comparison between Unknown and I4 //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Invalid comparison between Unknown and I4 //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Invalid comparison between Unknown and I4 //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Invalid comparison between Unknown and I4 //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Invalid comparison between Unknown and I4 //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) try { if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) { return; } RunManager instance = RunManager.instance; if ((Object)(object)instance != (Object)null && Reflect.Get(instance, "restarting")) { BotSpawner.CompleteOutros(); } LevelGenerator instance2 = LevelGenerator.Instance; if ((Object)(object)instance2 == (Object)null) { Reset(); return; } if (_genSeen != instance2) { _genSeen = instance2; _reported = 0; _placed = false; _stuckFor = 0f; _spawnStateFor = 0f; _forced = false; } LevelState state = instance2.State; int botCount = BotSpawner.BotCount; _logTimer -= dt; if (_logTimer <= 0f && (int)state != 15) { _logTimer = 1f; int num = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : (-1)); Plugin.Log.LogInfo((object)("[Gate] state=" + ((object)(*(LevelState*)(&state))/*cast due to .constrained prefix*/).ToString() + " bots=" + botCount + " reported=" + _reported + " playerSpawned=" + Reflect.Get(instance2, "playerSpawned") + " playerList=" + num)); } if ((int)state == 13) { _spawnStateFor += dt; if (_spawnStateFor > 5f && !_forced) { _forced = true; int num2 = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : 0); int num3 = Reflect.Get(instance2, "playerSpawned"); if (num3 < num2) { Reflect.Set(instance2, "playerSpawned", num2); Plugin.Log.LogWarning((object)("[Gate] FAILSAFE — stuck in PlayerSpawn for 5s with " + num3 + "/" + num2 + " reported. Forcing the counter so the level loads.")); } } } else { _spawnStateFor = 0f; _forced = false; } if (botCount > 0 && (int)state == 13 && _reported < botCount) { _stuckFor += dt; if (_stuckFor > 0.3f) { while (_reported < botCount) { ReportOneSpawned(instance2); _reported++; } Plugin.Log.LogInfo((object)("[Gate] Reported " + botCount + " bot(s) as spawned — releasing the level-load gate.")); } } else if ((int)state != 13) { _stuckFor = 0f; } if (_placed || !instance2.Generated || (int)state != 15 || BotMovement.InLobby() || !BotMovement.LevelReady()) { return; } PlayerAvatar instance3 = PlayerAvatar.instance; if (!((Object)(object)instance3 != (Object)null)) { return; } if (!BotEntrance.Prepare()) { Transform transform = ((Component)instance3).transform; Vector3 forward = transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } BotSpawner.TeleportBots(transform.position + ((Vector3)(ref forward)).normalized * 2f + Vector3.up * 0.2f); } BotMovement.Reset(); BotHands.Reset(); BotSpawner.OnNewLevel(); BotPersona.Reset(); BotEntrance.Reset(); BotBuddy.Reset(); BotPersona.Chatty = false; BotDanger.Reset(); BotMedic.Reset(); BotScout.Reset(); _placed = true; Plugin.Log.LogInfo((object)"[Gate] New level — bot placed."); } catch (Exception ex) { if (Time.unscaledTime > _errorAt) { _errorAt = Time.unscaledTime + 2f; Plugin.Log.LogError((object)("[Gate] EXCEPTION (the gate is not running!): " + ex)); } } } private static void ReportOneSpawned(LevelGenerator gen) { PhotonView val = Reflect.Get(gen, "PhotonView"); if ((Object)(object)val != (Object)null && PhotonNetwork.InRoom) { val.RPC("PlayerSpawnedRPC", (RpcTarget)0, Array.Empty()); return; } int num = Reflect.Get(gen, "playerSpawned"); Reflect.Set(gen, "playerSpawned", num + 1); } } public struct MidiNote { public float Start; public float End; public int Key; public float Velocity; public int Channel; } public class MidiSong { public readonly List Notes = new List(); public float Length; } public static class MidiReader { private struct RawNote { public long OnTick; public long OffTick; public int Key; public int Velocity; public int Channel; } public static MidiSong Read(byte[] d) { try { if (d == null || d.Length < 14) { return null; } if (d[0] != 77 || d[1] != 84 || d[2] != 104 || d[3] != 100) { return null; } int num = (d[12] << 8) | d[13]; if (num <= 0) { num = 480; } if ((num & 0x8000) != 0) { return null; } MidiSong midiSong = new MidiSong(); List> list = new List>(); List list2 = new List(); int num2 = 14; while (num2 + 8 <= d.Length) { bool flag = d[num2] == 77 && d[num2 + 1] == 84 && d[num2 + 2] == 114 && d[num2 + 3] == 107; int num3 = (d[num2 + 4] << 24) | (d[num2 + 5] << 16) | (d[num2 + 6] << 8) | d[num2 + 7]; num2 += 8; if (num3 < 0 || num2 + num3 > d.Length) { break; } if (flag) { ReadTrack(d, num2, num2 + num3, list2, list); } num2 += num3; } if (list2.Count == 0) { return null; } list.Sort((KeyValuePair a, KeyValuePair b) => a.Key.CompareTo(b.Key)); foreach (RawNote item in list2) { float num4 = ToSeconds(item.OnTick, list, num); float num5 = ToSeconds(item.OffTick, list, num); if (num5 <= num4) { num5 = num4 + 0.05f; } midiSong.Notes.Add(new MidiNote { Start = num4, End = num5, Key = item.Key, Velocity = Mathf.Clamp01((float)item.Velocity / 127f), Channel = item.Channel }); if (num5 > midiSong.Length) { midiSong.Length = num5; } } return midiSong; } catch { return null; } } private static void ReadTrack(byte[] d, int p, int end, List outNotes, List> tempos) { long num = 0L; int num2 = 0; Dictionary dictionary = new Dictionary(); while (p < end) { num += ReadVar(d, ref p); if (p >= end) { break; } int num3 = d[p]; if (num3 >= 128) { num2 = num3; p++; } if (num2 < 128) { return; } int num4 = num2 & 0xF0; int num5 = num2 & 0xF; switch (num2) { case 255: break; case 240: case 247: { int num8 = ReadVar(d, ref p); p += num8; continue; } default: switch (num4) { default: return; case 128: case 144: { if (p + 1 >= end) { return; } int num6 = d[p++]; int num7 = d[p++]; int key = num5 * 128 + num6; RawNote value; if (num4 == 144 && num7 > 0) { dictionary[key] = new RawNote { OnTick = num, Key = num6, Velocity = num7, Channel = num5 }; } else if (dictionary.TryGetValue(key, out value)) { value.OffTick = num; outNotes.Add(value); dictionary.Remove(key); } break; } case 160: case 176: case 224: p += 2; break; case 192: case 208: p++; break; } continue; } if (p >= end) { break; } byte num9 = d[p++]; int num10 = ReadVar(d, ref p); if (num9 == 81 && num10 == 3 && p + 3 <= end) { tempos.Add(new KeyValuePair(num, (d[p] << 16) | (d[p + 1] << 8) | d[p + 2])); } p += num10; } foreach (KeyValuePair item in dictionary) { RawNote value2 = item.Value; value2.OffTick = num; outNotes.Add(value2); } } private static int ReadVar(byte[] d, ref int p) { int num = 0; for (int i = 0; i < 4; i++) { if (p >= d.Length) { break; } int num2 = d[p++]; num = (num << 7) | (num2 & 0x7F); if ((num2 & 0x80) == 0) { break; } } return num; } private static float ToSeconds(long tick, List> tempos, int tpq) { double num = 0.0; long num2 = 0L; int num3 = 500000; foreach (KeyValuePair tempo in tempos) { if (tempo.Key >= tick) { break; } num += (double)(tempo.Key - num2) * ((double)num3 / 1000000.0) / (double)tpq; num2 = tempo.Key; num3 = tempo.Value; } num += (double)(tick - num2) * ((double)num3 / 1000000.0) / (double)tpq; return (float)num; } } public static class MidiSynth { public static float[] Render(MidiSong song, float maxSeconds) { try { if (song == null || song.Notes.Count == 0) { return null; } int num = 22050; float num2 = Mathf.Min(song.Length + 0.5f, maxSeconds); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 * (float)num)); float[] array = new float[num3]; foreach (MidiNote note in song.Notes) { if (note.Start >= num2) { continue; } int num4 = Mathf.Clamp(Mathf.FloorToInt(note.Start * (float)num), 0, num3 - 1); float num5 = Mathf.Min(note.End, num2) - note.Start; if (num5 <= 0f) { continue; } num5 = Mathf.Min(num5, 2.5f); int num6 = Mathf.Min(Mathf.CeilToInt(num5 * (float)num), num3 - num4); if (num6 > 0) { if (note.Channel == 9) { Drum(array, num4, num6, note, num); } else { Tone(array, num4, num6, note, num); } } } float num7 = 0f; for (int i = 0; i < num3; i++) { float num8 = ((array[i] < 0f) ? (0f - array[i]) : array[i]); if (num8 > num7) { num7 = num8; } } if (num7 > 0.0001f) { float num9 = 0.85f / num7; for (int j = 0; j < num3; j++) { array[j] = (float)Math.Tanh(array[j] * num9); } } return array; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Synth] " + ex.Message)); return null; } } private static void Tone(float[] buf, int from, int count, MidiNote n, int rate) { float num = 440f * Mathf.Pow(2f, (float)(n.Key - 69) / 12f); if (num < 20f || num > 8000f) { return; } float num2 = num / (float)rate; float num3 = num * 1.005f / (float)rate; float num4 = 0.16f * (0.35f + n.Velocity * 0.65f); float num5 = 0f; float num6 = 0f; int num7 = Mathf.Min(rate / 200, count); float num8 = 1f / (float)Mathf.Max(1, count); for (int i = 0; i < count; i++) { num5 += num2; if (num5 >= 1f) { num5 -= 1f; } num6 += num3; if (num6 >= 1f) { num6 -= 1f; } float num9 = ((num5 < 0.5f) ? 1f : (-1f)) * 0.6f + ((num6 < 0.5f) ? 1f : (-1f)) * 0.4f; float num10 = 1f - (float)i * num8; num10 *= num10; if (i < num7) { num10 *= (float)i / (float)num7; } buf[from + i] += num9 * num4 * num10; } } private static void Drum(float[] buf, int from, int count, MidiNote n, int rate) { bool flag = n.Key <= 41; int num = Mathf.Min(count, rate / (flag ? 8 : 16)); float num2 = 0.22f * (0.4f + n.Velocity * 0.6f); float num3 = 0f; for (int i = 0; i < num; i++) { float num4 = Random.value * 2f - 1f; num3 = (flag ? Mathf.Lerp(num3, num4, 0.1f) : Mathf.Lerp(num3, num4, 0.55f)); float num5 = 1f - (float)i / (float)num; buf[from + i] += num3 * num2 * num5 * num5; } } } public static class NameMatcher { public static bool Mentions(string text, string name) { if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(name)) { return false; } string text2 = Clean(name); if (text2.Length < 2) { return false; } if ((" " + Clean(text) + " ").Contains(" " + text2 + " ")) { return true; } string[] array = Clean(text).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return false; } string text3 = Skeleton(text2); int num = Mathf_Max(1, text2.Length / 4); for (int i = 1; i <= 3; i++) { for (int j = 0; j + i <= array.Length; j++) { string text4 = string.Concat(array, j, i); if (text4.Length >= 2 && Math.Abs(text4.Length - text2.Length) <= text2.Length / 2 + 1) { if (text4 == text2) { return true; } if (Distance(text4, text2) <= num) { return true; } if (Skeleton(text4) == text3) { return true; } } } } return false; } private static string Clean(string s) { char[] array = new char[s.Length]; int num = 0; string text = s.ToLowerInvariant(); foreach (char c in text) { if (char.IsLetterOrDigit(c)) { array[num++] = c; } else if (num > 0 && array[num - 1] != ' ') { array[num++] = ' '; } } return new string(array, 0, num).Trim(); } private static string Skeleton(string word) { if (string.IsNullOrEmpty(word)) { return ""; } char[] array = new char[word.Length]; int num = 0; for (int i = 0; i < word.Length; i++) { char c = Fold(word[i]); if (c != 0 && (num <= 0 || array[num - 1] != c)) { array[num++] = c; } } return new string(array, 0, num); } private static char Fold(char c) { switch (c) { case 'a': case 'e': case 'h': case 'i': case 'o': case 'u': case 'w': case 'y': return '\0'; case 'b': case 'p': return 'B'; case 'd': case 't': return 'D'; case 'c': case 'g': case 'k': case 'q': return 'K'; case 's': case 'x': case 'z': return 'S'; case 'f': case 'v': return 'F'; case 'm': case 'n': return 'M'; case 'l': return 'L'; case 'r': return 'R'; case 'j': return 'J'; default: return char.ToUpperInvariant(c); } } private static int Distance(string a, string b) { int length = a.Length; int length2 = b.Length; if (length == 0) { return length2; } if (length2 == 0) { return length; } int[] array = new int[length2 + 1]; int[] array2 = new int[length2 + 1]; for (int i = 0; i <= length2; i++) { array[i] = i; } for (int j = 1; j <= length; j++) { array2[0] = j; for (int k = 1; k <= length2; k++) { int num = ((a[j - 1] != b[k - 1]) ? 1 : 0); int num2 = array[k] + 1; int num3 = array2[k - 1] + 1; int num4 = array[k - 1] + num; array2[k] = ((num2 < num3) ? ((num2 < num4) ? num2 : num4) : ((num3 < num4) ? num3 : num4)); } int[] array3 = array; array = array2; array2 = array3; } return array[length2]; } private static int Mathf_Max(int a, int b) { if (a <= b) { return b; } return a; } } [BepInPlugin("com.boneless.bertrambuddybot", "Bertram Buddy Bot", "3.9.1")] public class Plugin : BaseUnityPlugin { public static Plugin Instance; public static ConfigEntry CfgEnabled; public static ConfigEntry CfgSpawnKey; public static ConfigEntry CfgDespawnKey; public static ConfigEntry CfgDriveKey; public static ConfigEntry CfgSpeakKey; public static ConfigEntry CfgJumpKey; public static ConfigEntry CfgAuditKey; public static ConfigEntry CfgLocalTestKey; public static ConfigEntry CfgAutoSpawn; public static ConfigEntry CfgLobbyBertram; public static ConfigEntry CfgPhysicsFalls; public static ConfigEntry CfgJumpStart; public static ConfigEntry CfgNsfw; public static ConfigEntry CfgTtsClarity; public static ConfigEntry CfgBuddyHud; public static ConfigEntry CfgMidiUrl; public static ConfigEntry CfgTtsFrameMs; public static ConfigEntry CfgTtsFlutter; public static ConfigEntry CfgTtsPitchMultiplier; public static ConfigEntry CfgHawkingVoice; public static ConfigEntry CfgEspeakVoiceTag; public static ConfigEntry CfgEspeak; public static ConfigEntry CfgEspeakVoice; public static ConfigEntry CfgEspeakPitch; public static ConfigEntry CfgEspeakRate; public static ConfigEntry CfgUseWindowsVoice; public static ConfigEntry CfgCustomVoice; public static ConfigEntry CfgHiddenEntrance; public static ConfigEntry CfgVoiceCommands; public static ConfigEntry CfgVoiceDevice; public static ConfigEntry CfgVoiceModelPath; public static ConfigEntry CfgVoiceGrammar; public static ConfigEntry CfgVoiceVerbose; public static ConfigEntry CfgWakeAliases; public static ConfigEntry CfgIntercomKey; public static ConfigEntry CfgIntercomOnly; public static ConfigEntry CfgSpatialVoice; public static ConfigEntry CfgOwnMic; public static ConfigEntry CfgBotColor; public static ConfigEntry CfgLocalGuards; public static ConfigEntry CfgLobbyChatter; public static ConfigEntry CfgVoiceEarshot; public static ConfigEntry CfgCarryFragility; public static ConfigEntry CfgBotName; public static ConfigEntry CfgBuddyOnlyCommands; public static ConfigEntry CfgStrength; public static ConfigEntry CfgRange; public static ConfigEntry CfgStamina; private Harmony _harmony; public static ManualLogSource Log { get; private set; } private void Awake() { //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Expected O, but got Unknown //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Expected O, but got Unknown //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Expected O, but got Unknown //IL_0840: Unknown result type (might be due to invalid IL or missing references) //IL_084a: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; CfgEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enables Bertram Buddy Bot."); CfgMidiUrl = ((BaseUnityPlugin)this).Config.Bind("Music", "MidiUrlTemplate", "", "Optional. A URL that returns a .mid file for a search term; {q} is replaced with the url-encoded request. Leave blank to use only the local BotSpike/midi folder. Downloaded tracks are played from memory and never saved. Whatever you point this at is your responsibility."); CfgBuddyHud = ((BaseUnityPlugin)this).Config.Bind("General", "BuddyWindow", true, "Show the buddy command panel in the lower-right when Bertram picks you. Hold ALT to browse it; the wheel scrolls, releasing ALT sends, 1-9 send directly."); CfgTtsClarity = ((BaseUnityPlugin)this).Config.Bind("Voice", "ClearSpeech", true, "Keep his TTS intelligible: never use the whisper voice, and stop his speech pitch swinging with which way his head is pointing. Turn off for vanilla behaviour."); CfgTtsFrameMs = ((BaseUnityPlugin)this).Config.Bind("Voice", "SpeechFrameMs", 14, "Milliseconds per speech frame in the Klattersynth voice. The engine default is 10; higher is slower and easier to understand. Range 1-100."); CfgTtsFlutter = ((BaseUnityPlugin)this).Config.Bind("Voice", "SpeechFlutter", 4, "How much warble is in his voice. The engine default is 10; lower is steadier. Range 0-200."); CfgTtsPitchMultiplier = ((BaseUnityPlugin)this).Config.Bind("Voice", "PitchMultiplier", 1f, "Pitch scale for Bertram's TTS voice (1.0 for normal pitch). Range 0.5 to 2.0."); CfgHawkingVoice = ((BaseUnityPlugin)this).Config.Bind("Voice", "DectalkVoiceTag", "", "DECtalk voice profile tag for Bertram (leave empty for normal speech)."); CfgEspeak = ((BaseUnityPlugin)this).Config.Bind("Voice", "UseEspeak", true, "Give Bertram an eSpeak NG voice instead of the game's Klattersynth. HIS voice only - every human still sounds exactly as before, which is the one thing the separate espeakTTS mod cannot do. Each machine renders his speech locally from the chat text the game already broadcasts, so all clients hear it and no audio is networked."); CfgEspeakVoice = ((BaseUnityPlugin)this).Config.Bind("Voice", "EspeakVoiceName", "en+m3", new ConfigDescription("Bertram's voice. m1-m7 are male, f1-f5 female; the robosoft/klatt/UniRobot ones lean robotic, croak and whisper are characterful. Changes apply to his next line.", (AcceptableValueBase)(object)new AcceptableValueList(new string[40] { "en+m1", "en+m2", "en+m3", "en+m4", "en+m5", "en+m6", "en+m7", "en+f1", "en+f2", "en+f3", "en+f4", "en+f5", "en+robosoft", "en+robosoft2", "en+robosoft3", "en+robosoft4", "en+robosoft5", "en+robosoft6", "en+robosoft7", "en+robosoft8", "en+klatt", "en+klatt2", "en+klatt3", "en+klatt4", "en+klatt5", "en+klatt6", "en+UniRobot", "en+semibot", "en+croak", "en+whisper", "en+Denis", "en+Mike", "en+Storm", "en+Tweaky", "en+Demonic", "en+grandpa", "en+max", "en+norbert", "en+quincy", "en+zac" }), Array.Empty())); CfgEspeakPitch = ((BaseUnityPlugin)this).Config.Bind("Voice", "EspeakPitch", 45, new ConfigDescription("How deep he sounds. Lower is deeper.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 99), Array.Empty())); CfgEspeakRate = ((BaseUnityPlugin)this).Config.Bind("Voice", "EspeakRate", 165, new ConfigDescription("How fast he talks, in words per minute.", (AcceptableValueBase)(object)new AcceptableValueRange(80, 450), Array.Empty())); EventHandler eventHandler = delegate { try { if (CfgEspeak.Value && BotSpawner.HasBot) { BotSpawner.Say("Testing. One two. This is what I sound like now."); } } catch { } }; CfgEspeakVoice.SettingChanged += eventHandler; CfgEspeakPitch.SettingChanged += eventHandler; CfgEspeakRate.SettingChanged += eventHandler; CfgEspeakVoiceTag = ((BaseUnityPlugin)this).Config.Bind("Voice", "EspeakVoiceTag", "[en+m3]", "Voice tag prefixed to Bertram's speech so the espeakTTS mod gives him his own voice. REQUIRES the separate 'espeakTTS' mod by Lavighju — install it and every player who has it hears him clearly, because that mod broadcasts the voice to all clients itself. Without it this does nothing and he uses the game's built-in synth. Try [en+m3] male 3, [en+m1]..[en+m7], [en+croak], [en+klatt]. Empty = no tag."); CfgUseWindowsVoice = ((BaseUnityPlugin)this).Config.Bind("Voice", "UseWindowsNaturalVoice", false, "Use Windows' built-in natural voice (Microsoft David) rendered on a background thread for crystal-clear human speech with 0 stutter and 0 extra downloads."); CfgSpawnKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "Spawn", (KeyCode)290, "Spawn a bot (host, in a lobby)."); CfgDespawnKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "Despawn", (KeyCode)291, "Despawn the bot."); CfgDriveKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "DriveTest", (KeyCode)292, "Toggle the movement test (orbits the bot)."); CfgSpeakKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "Speak", (KeyCode)293, "Make the bot say a line via the game's TTS."); CfgJumpKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "Jump", (KeyCode)289, "Make the bot jump (test)."); CfgLocalTestKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "LocalMultiplayerTest", (KeyCode)287, "At the MAIN MENU: arm the game's own local multiplayer test mode, so a second copy of REPO.exe on this PC can join you on one Steam account. Press in both instances."); CfgAuditKey = ((BaseUnityPlugin)this).Config.Bind("Keys", "SelfAudit", (KeyCode)288, "Print a self-audit: what the host is putting on the wire for him, and every precondition a client depends on. Solo-usable."); CfgAutoSpawn = ((BaseUnityPlugin)this).Config.Bind("General", "AutoSpawn", true, "Spawn the bot automatically a few seconds into each mission level. Also avoids a game bug: systems like GumballValuable cache the player count at level start, so a player appearing later breaks them. Set false to spawn manually with F9."); CfgLobbyBertram = ((BaseUnityPlugin)this).Config.Bind("General", "LobbyBertram", true, "Let Bertram hang around the lobby with the crew before a run — loitering, wisecracking, and answering back when someone talks. He does not loot or pathfind there."); CfgPhysicsFalls = ((BaseUnityPlugin)this).Config.Bind("General", "PhysicsFalls", true, "When airborne, hand the bot to the game's own PlayerTumble body so he falls under real physics (and can be grabbed and thrown while down) instead of being lowered by our own code."); CfgJumpStart = ((BaseUnityPlugin)this).Config.Bind("General", "JumpStart", true, "Bertram's special ability: when the area is clear he'll go to a dead crewmate, announce \"Applying jumper cables!\" and revive them at 1 HP. It costs him 25 health, and kills him if he has 25 or less."); CfgNsfw = ((BaseUnityPlugin)this).Config.Bind("General", "NSFWLanguage", false, "Let Bertram swear. Off by default. This swaps his whole dialogue set for a cruder version of the same character rather than just bleeping words in."); CfgHiddenEntrance = ((BaseUnityPlugin)this).Config.Bind("General", "HiddenEntrance", true, "Start each level with Bertram hidden inside the extraction point nearest the truck. He steps out when the crew discovers that room, so he never appears out of thin air. Off = he appears beside the host."); CfgStrength = ((BaseUnityPlugin)this).Config.Bind("Stats", "Strength", 2, "Grab-strength upgrades, same scale as a player's (each level is +0.2 grab strength on a base of 1.0). 2 = he can wrestle heavier things out of awkward places."); CfgRange = ((BaseUnityPlugin)this).Config.Bind("Stats", "Range", 0, "Grab-range upgrades, +1 metre each on a base of 4m."); CfgStamina = ((BaseUnityPlugin)this).Config.Bind("Stats", "Stamina", 0, "Stamina upgrades. Reserved for the bot roster — different bots will differ here."); CfgBotName = ((BaseUnityPlugin)this).Config.Bind("General", "BotName", "Bertram", "What to call him. He recognises his name PHONETICALLY, so any name works without configuring mishearings."); CfgBuddyOnlyCommands = ((BaseUnityPlugin)this).Config.Bind("Voice", "ObeyBuddyOnly", false, "Only take spoken orders from the crewmate he's currently attached to (he re-attaches when they die). Off = anyone in earshot can command him."); CfgCarryFragility = ((BaseUnityPlugin)this).Config.Bind("General", "CarryFragility", 0.35f, "How breakable a valuable is while BERTRAM is carrying it, as a multiplier (1 = same as a player, 0.35 = takes about a third of the damage). He clips doorframes more than a human does, so this stops him writing off the haul. Uses the game's own OverrideFragility, which self-restores the moment he lets go."); CfgVoiceCommands = ((BaseUnityPlugin)this).Config.Bind("Voice", "VoiceCommands", true, "Understand SPOKEN commands from the HOST's microphone (offline, via Vosk). Needs libvosk.dll and a vosk-model* folder next to BotSpike.dll — both ship in the package. Same phrases as text chat."); CfgVoiceDevice = ((BaseUnityPlugin)this).Config.Bind("Voice", "Device", "", "Microphone device name for voice commands. Empty = system default."); CfgVoiceModelPath = ((BaseUnityPlugin)this).Config.Bind("Voice", "ModelPath", "", "Full path to a Vosk model folder. Empty = auto-detect a vosk-model* folder next to the plugin."); CfgVoiceVerbose = ((BaseUnityPlugin)this).Config.Bind("Voice", "VerboseRecognition", true, "Log every partial transcript as it forms, and say so when an utterance ends with no words at all. This is how you tell 'he never heard me' apart from 'he heard me wrong' - they need completely different fixes. Noisy; turn off once speech is behaving."); CfgVoiceGrammar = ((BaseUnityPlugin)this).Config.Bind("Voice", "KeywordGrammar", true, "Restrict recognition to the command vocabulary. Much more accurate for a fixed command set: Vosk can only emit words from the list, so his name stops being misheard. Unknown-word handling is included, so ordinary conversation maps to unknown rather than the nearest command. Set false for free-form recognition."); CfgVoiceEarshot = ((BaseUnityPlugin)this).Config.Bind("Voice", "Earshot", true, "Spoken commands only land if Bertram is close enough to the SPEAKER to have heard them — a whisper carries ~6m, a shout ~45m. Applies to host and modded clients alike. false = radio mode (any distance, host behaviour before 2.1)."); CfgWakeAliases = ((BaseUnityPlugin)this).Config.Bind("Voice", "WakeAliases", "bertrum,bertran,bert ram,bird ram,birch ram,but ram,were trump,her trump,bert rum,but rum,bertrand,bertha,bertie,burt ram,burt rum,brett rum,red rum,her tram,the tram,but tram,bad rum", "Common mishearings of 'Bertram', comma-separated, rewritten to 'bertram' before matching. The small offline model struggles with the name specifically — a real log had \"hello bertram\" come back as \"and i were trump\" — so this list is what makes the wake word usable. The intercom key needs none of it."); CfgIntercomOnly = ((BaseUnityPlugin)this).Config.Bind("Voice", "IntercomOnly", false, "Only take SPOKEN orders while the intercom key is held — ignore the spoken wake word entirely. Turn this on if he starts acting on crew banter: a held key cannot be misheard and has no listening window afterwards, so false commands become impossible rather than merely unlikely. Text chat is unaffected."); CfgSpatialVoice = ((BaseUnityPlugin)this).Config.Bind("Voice", "SpatialVoice", true, "Position Bertram's voice in the world so it fades with distance like everyone else's. The host OWNS his voice object, so the game's \"hear yourself\" rule flattens him to 2D and he becomes audible at full volume from anywhere in the level. Set false if his voice goes missing entirely and you'd rather have him too loud than silent."); CfgOwnMic = ((BaseUnityPlugin)this).Config.Bind("Voice", "OwnMicrophone", false, "DANGEROUS, default off. Open the microphone directly instead of using the game's own voice loopback. R.E.P.O. records the same device through the same Unity API and Unity allows only ONE capture per device, so this can stop your voice chat transmitting entirely. Only enable if the intercom cannot hear you and you want to test."); CfgLobbyChatter = ((BaseUnityPlugin)this).Config.Bind("General", "LobbyChatter", 1.8f, "How often Bertram talks unprompted in the lobby, as a multiplier on the gap between his lines. 1.0 is the original (very chatty) rate; higher is quieter. 1.8 roughly halves it. Set 4 for the occasional remark, or turn him off in the lobby entirely with General.LobbyBertram."); CfgLocalGuards = ((BaseUnityPlugin)this).Config.Bind("General", "LocalGuards", true, "Undo the effects the game applies to Bertram because the host OWNS him and it therefore thinks he is YOU: 2D full-volume sounds, your saved cosmetics and colour, and being copied onto your body. Set false to turn the whole sweep off in one go if it misbehaves — nothing else in the mod is affected."); CfgBotColor = ((BaseUnityPlugin)this).Config.Bind("General", "BotColor", 6, "Which colour Bertram is, as an index into the game's player-colour palette. Without this he inherits YOUR saved colour, because SetupColors falls back to MetaManager.colorsEquipped for anything the host owns. Out-of-range values wrap."); CfgIntercomKey = ((BaseUnityPlugin)this).Config.Bind("Voice", "IntercomKey", "LeftAlt", "Push-to-talk DIRECT to Bertram. Hold this key and speak: no wake word needed, and the earshot rule is skipped because you opened the channel deliberately. Any Unity KeyCode name; 'None' disables it. LeftAlt is also the buddy-panel key, so both ways of giving him an order sit under one finger."); if (CfgEnabled.Value) { BotRelations.Load(); BotRelations.NewSession(); _harmony = new Harmony("com.boneless.botspike"); Patch("Spawner", typeof(BotSpawner)); Patch("Death", typeof(BotDeath)); Patch("Aggro", typeof(BotAggro)); Patch("VoiceClarity", typeof(BotVoiceClarity)); Patch("InputGate", typeof(BotInputGate)); Patch("TumbleGuard", typeof(BotTumbleGuard)); Patch("NetTransform", typeof(BotNetTransform)); Patch("VoiceSpatial", typeof(BotVoiceSpatial)); Patch("Espeak", typeof(BotEspeakHook)); Patch("LocalGuards", typeof(BotLocalGuards)); Patch("DeathHead", typeof(BotDeathHead)); Patch("Chatter", typeof(BotChatter)); Patch("Commands", typeof(BotCommands)); SceneGuard.Hook(); SpamCatcher.Hook(); BotEars.Init(); Log.LogInfo((object)"Bertram Buddy Bot v3.9.1 ARMED. F6 localtest / F7 audit / F8 jump / F9 spawn / F10 despawn / F11 walk-to-me / F12 speak."); } else { Log.LogInfo((object)"Bertram Buddy Bot v3.9.1 loaded but DISABLED (General/Enabled=false). No patches applied."); } } private void Patch(string name, Type type) { try { _harmony.PatchAll(type); } catch (Exception ex) { Log.LogError((object)("[Patch] '" + name + "' FAILED to apply — that feature is off for this session, everything else still loaded. Cause: " + ex.Message)); } } private void OnGUI() { if (CfgEnabled != null && CfgEnabled.Value) { BotBuddyHud.Draw(); BotLastStandHud.Draw(); } } private void Update() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (CfgEnabled == null || !CfgEnabled.Value) { return; } try { BotSpawner.ResolveProvisional(); BotSpawner.ClientBeaconTick(); BotSpawner.ClientKeepAlive(); BotDance.ClientTick(); BotSpawner.BindBotVoiceLocally(); BotBuddyHud.Tick(); if (Input.GetKeyDown(CfgSpawnKey.Value)) { BotSpawner.TrySpawn(); } else if (Input.GetKeyDown(CfgDespawnKey.Value)) { BotSpawner.Despawn(); } else if (Input.GetKeyDown(CfgDriveKey.Value)) { BotSpawner.ToggleDrive(); } else if (Input.GetKeyDown(CfgSpeakKey.Value)) { BotSpawner.SayRandom(); } else if (Input.GetKeyDown(CfgJumpKey.Value)) { BotSpawner.JumpNow(); } else if (Input.GetKeyDown(CfgAuditKey.Value)) { BotSelfAudit.Run(); } else if (Input.GetKeyDown(CfgLocalTestKey.Value)) { BotLocalTest.Arm(); } SceneGuard.Tick(Time.deltaTime); SpamCatcher.Tick(); LevelGate.Tick(Time.deltaTime); BotIntercom.Tick(); BotEspeak.Tick(); BotCommands.TickPendingChat(); BotEars.Tick(); BotNet.EnsureReady(); if (!SceneGuard.Transitioning) { BotSpawner.AutoSpawnTick(); BotSpawner.Tick(); } } catch (Exception ex) { if (Time.frameCount % 300 == 0) { Log.LogError((object)("[Spike] Update: " + ex)); } } } private void OnDestroy() { try { BotEars.Shutdown(); } catch { } try { SpamCatcher.Unhook(); } catch { } try { BotRelations.Save(force: true); } catch { } } } public static class Reflect { private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary FieldCache = new Dictionary(); private static readonly Dictionary PropCache = new Dictionary(); public static FieldInfo GetFieldInfo(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } string key = type.FullName + "." + name; lock (FieldCache) { if (FieldCache.TryGetValue(key, out var value)) { return value; } FieldInfo fieldInfo = null; try { Type type2 = type; while (type2 != null && fieldInfo == null) { fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); type2 = type2.BaseType; } } catch { } FieldCache[key] = fieldInfo; return fieldInfo; } } public static PropertyInfo GetPropInfo(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } string key = type.FullName + "." + name; lock (PropCache) { if (PropCache.TryGetValue(key, out var value)) { return value; } PropertyInfo propertyInfo = null; try { propertyInfo = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } catch { } PropCache[key] = propertyInfo; return propertyInfo; } } public static T Get(object obj, string name) { if (obj == null) { return default(T); } try { FieldInfo fieldInfo = GetFieldInfo(obj.GetType(), name); if (fieldInfo != null) { return (fieldInfo.GetValue(obj) is T val) ? val : default(T); } PropertyInfo propInfo = GetPropInfo(obj.GetType(), name); if (propInfo != null && propInfo.CanRead && propInfo.GetValue(obj, null) is T result) { return result; } } catch { } return default(T); } public static void Set(object obj, string name, object value) { if (obj == null) { return; } try { FieldInfo fieldInfo = GetFieldInfo(obj.GetType(), name); if (fieldInfo != null) { fieldInfo.SetValue(obj, value); return; } PropertyInfo propInfo = GetPropInfo(obj.GetType(), name); if (propInfo != null && propInfo.CanWrite) { propInfo.SetValue(obj, value, null); } } catch { } } public static object Invoke(object obj, string name, params object[] args) { if (obj == null || string.IsNullOrEmpty(name)) { return null; } try { MethodInfo method = obj.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { return method.Invoke(obj, args); } } catch { } return null; } } public static class SceneGuard { private static float _quietFor; private static bool _hooked; private const float SettleSeconds = 2f; public static bool Transitioning => _quietFor > 0f; public static void Hook() { if (!_hooked) { _hooked = true; SceneManager.activeSceneChanged += OnSceneChanged; SceneManager.sceneUnloaded += OnSceneUnloaded; SceneManager.sceneLoaded += OnSceneLoaded; Plugin.Log.LogInfo((object)"[Guard] Watching scene changes."); } } public static void Unhook() { if (_hooked) { _hooked = false; SceneManager.activeSceneChanged -= OnSceneChanged; SceneManager.sceneUnloaded -= OnSceneUnloaded; SceneManager.sceneLoaded -= OnSceneLoaded; } } public static void Tick(float dt) { if (_quietFor > 0f) { _quietFor -= dt; if (_quietFor <= 0f) { Plugin.Log.LogInfo((object)"[Guard] Scene settled — bot active again."); } } } public static void Hold(float seconds) { if (seconds > _quietFor) { _quietFor = seconds; } } private static void OnSceneChanged(Scene from, Scene to) { Silence("active scene changed"); } private static void OnSceneUnloaded(Scene s) { Silence("scene unloaded"); } private static void OnSceneLoaded(Scene s, LoadSceneMode m) { Silence("scene loaded"); } private static void Silence(string why) { _quietFor = 2f; try { BotMovement.Reset(); BotHands.Reset(); BotDanger.Reset(); BotScout.Reset(); BotMedic.Reset(); BotEntrance.Reset(); } catch { } Plugin.Log.LogInfo((object)("[Guard] " + why + " — pausing the bot for " + 2f + "s.")); } } public static class SpamCatcher { private sealed class Entry { public int Count; public float FirstAt; public bool Traced; } [CompilerGenerated] private static class <>O { public static LogCallback <0>__OnLog; } private static readonly Dictionary Seen = new Dictionary(); private static bool _hooked; private static float _summaryAt; private static readonly string[] Watch = new string[4] { "Look rotation viewing vector is zero", "Look rotation viewing vector is not valid", "Invalid AABB", "Quaternion To Matrix conversion failed" }; private static StackTraceLogType _previousTrace = (StackTraceLogType)1; public static void Hook() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!_hooked) { _hooked = true; object obj = <>O.<0>__OnLog; if (obj == null) { LogCallback val = OnLog; <>O.<0>__OnLog = val; obj = (object)val; } Application.logMessageReceived += (LogCallback)obj; try { _previousTrace = Application.GetStackTraceLogType((LogType)3); Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)1); } catch { } Plugin.Log.LogInfo((object)"[Spam] Watching for repeated engine warnings — will report their source once each."); } } public static void Unhook() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!_hooked) { return; } _hooked = false; object obj = <>O.<0>__OnLog; if (obj == null) { LogCallback val = OnLog; <>O.<0>__OnLog = val; obj = (object)val; } Application.logMessageReceived -= (LogCallback)obj; try { Application.SetStackTraceLogType((LogType)3, _previousTrace); } catch { } } private static void KeepTracesOn() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 try { if ((int)Application.GetStackTraceLogType((LogType)3) != 1) { Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)1); } } catch { } } public static void Tick() { KeepTracesOn(); if (Seen.Count == 0 || Time.unscaledTime < _summaryAt) { return; } _summaryAt = Time.unscaledTime + 30f; foreach (KeyValuePair item in Seen) { Entry value = item.Value; if (value.Count >= 20) { float num = Mathf.Max(0.01f, Time.unscaledTime - value.FirstAt); Plugin.Log.LogWarning((object)("[Spam] \"" + item.Key + "\" x" + value.Count + " (" + ((float)value.Count / num).ToString("F0") + "/sec)")); value.Count = 0; value.FirstAt = Time.unscaledTime; } } } private static void OnLog(string condition, string stack, LogType type) { if (string.IsNullOrEmpty(condition)) { return; } bool flag = false; for (int i = 0; i < Watch.Length; i++) { if (condition.IndexOf(Watch[i], StringComparison.Ordinal) >= 0) { flag = true; break; } } if (flag) { if (!Seen.TryGetValue(condition, out var value)) { Dictionary seen = Seen; Entry obj = new Entry { FirstAt = Time.unscaledTime }; value = obj; seen[condition] = obj; } value.Count++; if (!value.Traced) { value.Traced = true; Plugin.Log.LogWarning((object)("[Spam] SOURCE OF \"" + condition + "\":\n" + (string.IsNullOrEmpty(stack) ? "(no stack trace available)" : stack))); } } } } }