Decompiled source of TwitchChatSpawner v1.0.0

TwitchChatSpawner.dll

Decompiled 16 hours ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using Sodalite;
using Sodalite.Api;
using Sodalite.ModPanel;
using Sodalite.UiWidgets;
using Steamworks;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
namespace jediSpawner
{
	public class ArmorSwitchPanel : MonoBehaviour
	{
		public Text FriendlySelection;

		public Text EnemyTypeSelection;

		public Text EnemySelection;

		public Text Status;

		public Button PreviousFriendly;

		public Button NextFriendly;

		public Button PreviousEnemyType;

		public Button NextEnemyType;

		public Button PreviousEnemy;

		public Button NextEnemy;

		public Button ApplyFriendly;

		public Button ApplyEnemy;

		public Button SpawnFriendly;

		public Button SpawnEnemy;

		public Button RandomArmorToggle;

		private ChatWatcher watcher;

		public void Bind(ChatWatcher owner)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Expected O, but got Unknown
			if (!((Object)(object)watcher != (Object)null))
			{
				watcher = owner;
				((UnityEvent)PreviousFriendly.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepFriendlyArmor(-1);
				});
				((UnityEvent)NextFriendly.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepFriendlyArmor(1);
				});
				((UnityEvent)PreviousEnemyType.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepEnemyTemplate(-1);
				});
				((UnityEvent)NextEnemyType.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepEnemyTemplate(1);
				});
				((UnityEvent)PreviousEnemy.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepEnemyArmor(-1);
				});
				((UnityEvent)NextEnemy.onClick).AddListener((UnityAction)delegate
				{
					watcher.StepEnemyArmor(1);
				});
				((UnityEvent)ApplyFriendly.onClick).AddListener(new UnityAction(watcher.ApplyFriendlyArmorToAll));
				((UnityEvent)ApplyEnemy.onClick).AddListener(new UnityAction(watcher.ApplyEnemyArmorToAll));
				((UnityEvent)SpawnFriendly.onClick).AddListener(new UnityAction(watcher.SpawnFriendlyFromCurrentFile));
				((UnityEvent)SpawnEnemy.onClick).AddListener(new UnityAction(watcher.SpawnEnemyFromCurrentFile));
				if ((Object)(object)RandomArmorToggle != (Object)null)
				{
					((UnityEvent)RandomArmorToggle.onClick).AddListener(new UnityAction(watcher.ToggleRandomArmor));
				}
			}
		}

		public void Refresh(ChatSpawner definition, int friendly, int enemyType, int enemy, string status, bool randomArmor)
		{
			Status.text = status;
			FriendlySelection.text = "FRIENDLY ARMOR\n" + ((!((Object)(object)definition == (Object)null)) ? definition.GetFriendlyOutfitName(friendly) : "Loading...");
			EnemyTypeSelection.text = "ENEMY TYPE\n" + ((!((Object)(object)definition == (Object)null)) ? definition.GetEnemyTemplateName(enemyType) : "Loading...");
			EnemySelection.text = "ENEMY ARMOR\n" + ((!((Object)(object)definition == (Object)null)) ? definition.GetEnemyOutfitName(enemyType, enemy) : "Loading...");
			if (randomArmor)
			{
				FriendlySelection.text = "FRIENDLY ARMOR\nRandom T&H outfit";
				EnemySelection.text = "ENEMY ARMOR\nRandom T&H outfit";
			}
			if ((Object)(object)RandomArmorToggle != (Object)null)
			{
				((Component)RandomArmorToggle).GetComponentInChildren<Text>().text = "RANDOM T&H ARMOR\n" + ((!randomArmor) ? "OFF" : "ON");
			}
			bool flag = (Object)(object)definition != (Object)null && definition.GetFriendlyOutfitCount() > 0;
			bool flag2 = (Object)(object)definition != (Object)null && definition.GetEnemyTemplateCount() > 0;
			bool flag3 = flag2 && definition.GetEnemyOutfitCount(enemyType) > 0;
			Button previousFriendly = PreviousFriendly;
			bool interactable = flag && !randomArmor;
			((Selectable)NextFriendly).interactable = interactable;
			((Selectable)previousFriendly).interactable = interactable;
			((Selectable)ApplyFriendly).interactable = (Object)(object)definition != (Object)null && (randomArmor || flag);
			Button previousEnemyType = PreviousEnemyType;
			interactable = flag2;
			((Selectable)NextEnemyType).interactable = interactable;
			((Selectable)previousEnemyType).interactable = interactable;
			Button previousEnemy = PreviousEnemy;
			interactable = flag3 && !randomArmor;
			((Selectable)NextEnemy).interactable = interactable;
			((Selectable)previousEnemy).interactable = interactable;
			((Selectable)ApplyEnemy).interactable = (Object)(object)definition != (Object)null && (randomArmor || flag3);
			((Selectable)SpawnFriendly).interactable = (Object)(object)definition != (Object)null;
			((Selectable)SpawnEnemy).interactable = flag2;
		}
	}
	public class ChatSpawner : MonoBehaviour
	{
		public const int RandomArmorIndex = -2;

		private static readonly Dictionary<string, GameObject> resolvedObjectPrefabs = new Dictionary<string, GameObject>();

		private static readonly HashSet<string> missingObjectWarnings = new HashSet<string>();

		public SosigEnemyTemplate t;

		public List<SosigEnemyTemplate> EnemyT;

		public GameObject namething;

		public GameObject enemyNamePlate;

		[Tooltip("Custom UniversalModPanel canvas used by the armor-switching tablet.")]
		public GameObject ArmorPanelPrefab;

		public bool isEnemy = false;

		public float enemyIFF = 1f;

		public List<SosigOutfitConfig> GetAvailableArmorOutfits()
		{
			List<SosigOutfitConfig> list = new List<SosigOutfitConfig>();
			HashSet<SosigOutfitConfig> hashSet = new HashSet<SosigOutfitConfig>();
			IM val = Object.FindObjectOfType<IM>();
			if ((Object)(object)val == (Object)null || val.odicSosigObjsByID == null)
			{
				return list;
			}
			foreach (SosigEnemyTemplate value in val.odicSosigObjsByID.Values)
			{
				if ((Object)(object)value == (Object)null || value.OutfitConfig == null)
				{
					continue;
				}
				foreach (SosigOutfitConfig item in value.OutfitConfig)
				{
					if ((Object)(object)item != (Object)null && HasArmorOptions(item) && hashSet.Add(item))
					{
						list.Add(item);
					}
				}
			}
			return list;
		}

		public SosigOutfitConfig PickRandomArmor(List<SosigOutfitConfig> outfits)
		{
			return (outfits != null && outfits.Count != 0) ? outfits[Random.Range(0, outfits.Count)] : null;
		}

		private static bool HasArmorOptions(SosigOutfitConfig outfit)
		{
			return HasArmorOptions(outfit.Headwear, outfit.Chance_Headwear) || HasArmorOptions(outfit.Facewear, outfit.Chance_Facewear) || HasArmorOptions(outfit.Eyewear, outfit.Chance_Eyewear) || HasArmorOptions(outfit.Torsowear, outfit.Chance_Torsowear) || HasArmorOptions(outfit.Pantswear, outfit.Chance_Pantswear) || HasArmorOptions(outfit.Pantswear_Lower, outfit.Chance_Pantswear_Lower) || HasArmorOptions(outfit.Backpacks, outfit.Chance_Backpacks) || HasArmorOptions(outfit.TorosDecoration, outfit.Chance_TorosDecoration);
		}

		private static bool HasArmorOptions(List<FVRObject> pieces, float chance)
		{
			if (pieces == null || chance <= 0f)
			{
				return false;
			}
			for (int i = 0; i < pieces.Count; i++)
			{
				if ((Object)(object)pieces[i] != (Object)null)
				{
					return true;
				}
			}
			return false;
		}

		public void SpawningSequence()
		{
			SpawningSequence(-1);
		}

		public Sosig SpawningSequence(int outfitIndex)
		{
			//IL_002f: 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_00aa: 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)
			int num = ((!((Object)(object)GM.CurrentPlayerBody == (Object)null)) ? GM.CurrentPlayerBody.GetPlayerIFF() : 0);
			Sosig val = SpawnFromTemplate(t, ((Component)this).transform.position, ((Component)this).transform.rotation, num, outfitIndex);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			val.SetIFF(num);
			val.SetOriginalIFFTeam(num);
			val.DoesAggroOnFriendlyFire = false;
			if ((Object)(object)GM.CurrentPlayerBody != (Object)null && (Object)(object)GM.CurrentPlayerBody.Head != (Object)null)
			{
				val.CommandAssaultPoint(GetFriendlyFollowPoint());
				val.SetCurrentOrder((SosigOrder)7);
			}
			val.FallbackOrder = (SosigOrder)4;
			AttachNamePlate((!((Object)(object)namething == (Object)null)) ? namething : enemyNamePlate, val, isEnemyNamePlate: false);
			ChatWatcher.RegisterFriendly(val);
			return val;
		}

		public void SpawningSequenceEnemy(int iff)
		{
			SpawningSequenceEnemy(iff, -1, -1);
		}

		public Sosig SpawningSequenceEnemy(int iff, int templateIndex, int outfitIndex)
		{
			//IL_0011: 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_006c: 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)
			SosigEnemyTemplate enemyTemplate = GetEnemyTemplate(templateIndex);
			Sosig val = SpawnFromTemplate(enemyTemplate, ((Component)this).transform.position, ((Component)this).transform.rotation, iff, outfitIndex);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if ((Object)(object)GM.CurrentPlayerBody != (Object)null)
			{
				val.CommandAssaultPoint(((Component)GM.CurrentPlayerBody).transform.position);
				val.SetCurrentOrder((SosigOrder)7);
			}
			val.FallbackOrder = (SosigOrder)4;
			AttachNamePlate(enemyNamePlate, val, isEnemyNamePlate: true);
			ChatWatcher.RegisterEnemy(val);
			return val;
		}

		public Sosig SpawningSequenceBoss(int iff, int templateIndex, int outfitIndex, float healthMultiplier, float scaleMultiplier, float speedMultiplier)
		{
			//IL_0011: 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_0078: 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)
			SosigEnemyTemplate enemyTemplate = GetEnemyTemplate(templateIndex);
			Sosig val = SpawnFromTemplate(enemyTemplate, ((Component)this).transform.position, ((Component)this).transform.rotation, iff, outfitIndex);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			ApplyBossModifiers(val, healthMultiplier, scaleMultiplier, speedMultiplier);
			if ((Object)(object)GM.CurrentPlayerBody != (Object)null)
			{
				val.CommandAssaultPoint(((Component)GM.CurrentPlayerBody).transform.position);
				val.SetCurrentOrder((SosigOrder)7);
			}
			val.FallbackOrder = (SosigOrder)4;
			AttachNamePlate(enemyNamePlate, val, isEnemyNamePlate: true);
			ChatWatcher.RegisterEnemy(val);
			ChatWatcher.RegisterBoss(val);
			return val;
		}

		public int GetFriendlyOutfitCount()
		{
			return (!((Object)(object)t == (Object)null) && t.OutfitConfig != null) ? t.OutfitConfig.Count : 0;
		}

		public string GetFriendlyOutfitName(int index)
		{
			SosigOutfitConfig outfit = GetOutfit(t, index);
			return (!((Object)(object)outfit == (Object)null)) ? CleanAssetName(((Object)outfit).name) : "None";
		}

		public int GetEnemyTemplateCount()
		{
			return GetUniqueEnemyTemplates().Count;
		}

		public string GetEnemyTemplateName(int index)
		{
			SosigEnemyTemplate enemyTemplate = GetEnemyTemplate(index);
			return (!((Object)(object)enemyTemplate == (Object)null)) ? CleanAssetName(((Object)enemyTemplate).name) : "None";
		}

		public int GetEnemyOutfitCount(int templateIndex)
		{
			SosigEnemyTemplate enemyTemplate = GetEnemyTemplate(templateIndex);
			return (!((Object)(object)enemyTemplate == (Object)null) && enemyTemplate.OutfitConfig != null) ? enemyTemplate.OutfitConfig.Count : 0;
		}

		public string GetEnemyOutfitName(int templateIndex, int outfitIndex)
		{
			SosigOutfitConfig outfit = GetOutfit(GetEnemyTemplate(templateIndex), outfitIndex);
			return (!((Object)(object)outfit == (Object)null)) ? CleanAssetName(((Object)outfit).name) : "None";
		}

		public SosigEnemyTemplate GetEnemyTemplate(int index)
		{
			List<SosigEnemyTemplate> uniqueEnemyTemplates = GetUniqueEnemyTemplates();
			if (uniqueEnemyTemplates.Count == 0)
			{
				return null;
			}
			if (index < 0 || index >= uniqueEnemyTemplates.Count)
			{
				return uniqueEnemyTemplates[Random.Range(0, uniqueEnemyTemplates.Count)];
			}
			return uniqueEnemyTemplates[index];
		}

		public List<SosigEnemyTemplate> GetUniqueEnemyTemplates()
		{
			List<SosigEnemyTemplate> list = new List<SosigEnemyTemplate>();
			if (EnemyT == null)
			{
				return list;
			}
			for (int i = 0; i < EnemyT.Count; i++)
			{
				SosigEnemyTemplate val = EnemyT[i];
				if ((Object)(object)val != (Object)null && !list.Contains(val))
				{
					list.Add(val);
				}
			}
			return list;
		}

		public SosigOutfitConfig GetOutfit(SosigEnemyTemplate template, int index)
		{
			if ((Object)(object)template == (Object)null || template.OutfitConfig == null || template.OutfitConfig.Count == 0)
			{
				return null;
			}
			if (index < 0 || index >= template.OutfitConfig.Count)
			{
				return template.OutfitConfig[Random.Range(0, template.OutfitConfig.Count)];
			}
			return template.OutfitConfig[index];
		}

		public bool ApplyOutfit(Sosig sosig, SosigOutfitConfig outfit, bool removeCurrentArmor)
		{
			if ((Object)(object)sosig == (Object)null || (Object)(object)outfit == (Object)null || sosig.Links == null || sosig.Links.Count < 4)
			{
				return false;
			}
			SosigWearable[] array = ((!removeCurrentArmor) ? null : ((Component)sosig).GetComponentsInChildren<SosigWearable>(true));
			int num = 0;
			if (TrySpawnAccessory(outfit.Headwear, outfit.Chance_Headwear, sosig.Links[0]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Facewear, outfit.Chance_Facewear, sosig.Links[0]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Eyewear, outfit.Chance_Eyewear, sosig.Links[0]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Torsowear, outfit.Chance_Torsowear, sosig.Links[1]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Pantswear, outfit.Chance_Pantswear, sosig.Links[2]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Pantswear_Lower, outfit.Chance_Pantswear_Lower, sosig.Links[3]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.Backpacks, outfit.Chance_Backpacks, sosig.Links[1]))
			{
				num++;
			}
			if (TrySpawnAccessory(outfit.TorosDecoration, outfit.Chance_TorosDecoration, sosig.Links[1]))
			{
				num++;
			}
			if (num == 0)
			{
				Debug.LogWarning((object)("[Twitch Chat Spawner] No loadable armor pieces were found for outfit " + ((Object)outfit).name));
			}
			else if (array != null)
			{
				RemoveWearables(array);
			}
			return num > 0;
		}

		public void RemoveCurrentArmor(Sosig sosig)
		{
			if (!((Object)(object)sosig == (Object)null))
			{
				RemoveWearables(((Component)sosig).GetComponentsInChildren<SosigWearable>(true));
			}
		}

		private void RemoveWearables(SosigWearable[] wearables)
		{
			foreach (SosigWearable val in wearables)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if ((Object)(object)val.L != (Object)null)
					{
						val.L.DeRegisterWearable(val);
					}
					((Component)val).transform.SetParent((Transform)null);
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}

		private static void ApplyBossModifiers(Sosig boss, float healthMultiplier, float scaleMultiplier, float speedMultiplier)
		{
			//IL_0043: 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)
			healthMultiplier = Mathf.Clamp(healthMultiplier, 1f, 25f);
			scaleMultiplier = Mathf.Clamp(scaleMultiplier, 1f, 2f);
			speedMultiplier = Mathf.Clamp(speedMultiplier, 0.5f, 2f);
			((Component)boss).transform.localScale = ((Component)boss).transform.localScale * scaleMultiplier;
			for (int i = 0; i < boss.Links.Count; i++)
			{
				SosigLink val = boss.Links[i];
				if (!((Object)(object)val == (Object)null))
				{
					val.m_fullintegrity *= healthMultiplier;
					val.m_integrity = val.m_fullintegrity;
				}
			}
			float num = Mathf.Sqrt(healthMultiplier);
			boss.StunThreshold *= num;
			boss.StunMultiplier /= num;
			boss.BleedDamageMult /= num;
			boss.BleedRateMult /= num;
			boss.Speed_Crawl *= speedMultiplier;
			boss.Speed_Sneak *= speedMultiplier;
			boss.Speed_Walk *= speedMultiplier;
			boss.Speed_Run *= speedMultiplier;
			boss.Speed_Turning *= speedMultiplier;
		}

		private Sosig SpawnFromTemplate(SosigEnemyTemplate template, Vector3 position, Quaternion rotation, int iff, int outfitIndex)
		{
			//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_014a: 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_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: 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_0195: Unknown result type (might be due to invalid IL or missing references)
			SosigEnemyTemplate val = template;
			template = ResolveRuntimeTemplate(template);
			if (!IsUsableTemplate(template))
			{
				Debug.LogError((object)"[Twitch Chat Spawner] The selected Sosig template is incomplete and no runtime template was available.");
				return null;
			}
			GameObject randomObject = GetRandomObject(template.SosigPrefabs);
			if ((Object)(object)randomObject == (Object)null)
			{
				Debug.LogError((object)"[Twitch Chat Spawner] The selected Sosig template has no resolved prefab.");
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(randomObject, position, rotation);
			Sosig componentInChildren = val2.GetComponentInChildren<Sosig>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			SosigConfigTemplate val3 = template.ConfigTemplates[Random.Range(0, template.ConfigTemplates.Count)];
			componentInChildren.Configure(val3);
			componentInChildren.SetIFF(iff);
			componentInChildren.SetOriginalIFFTeam(iff);
			if ((Object)(object)componentInChildren.E != (Object)null)
			{
				componentInChildren.E.IFFCode = iff;
			}
			if (componentInChildren.Priority != null && componentInChildren.Priority.IFFChart != null)
			{
				for (int i = 0; i < componentInChildren.Priority.IFFChart.Length; i++)
				{
					componentInChildren.Priority.IFFChart[i] = i != iff;
				}
			}
			componentInChildren.Inventory.Init();
			EquipWeapon(componentInChildren, GetRandomObject(template.WeaponOptions), position, rotation, initializeHands: true);
			if (Random.value <= template.SecondaryChance)
			{
				EquipWeapon(componentInChildren, GetRandomObject(template.WeaponOptions_Secondary), position, rotation, initializeHands: false);
			}
			if (Random.value <= template.TertiaryChance)
			{
				EquipWeapon(componentInChildren, GetRandomObject(template.WeaponOptions_Tertiary), position, rotation, initializeHands: false);
			}
			componentInChildren.Inventory.FillAllAmmo();
			SosigOutfitConfig val4 = ((outfitIndex != -2) ? GetOutfit(val, outfitIndex) : PickRandomArmor(GetAvailableArmorOutfits()));
			if (outfitIndex == -2 && (Object)(object)val4 == (Object)null)
			{
				Debug.LogWarning((object)"[Twitch Chat Spawner] Runtime armor catalog is unavailable; using the spawn template outfit.");
			}
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = GetOutfit(template, outfitIndex);
			}
			if (!ApplyOutfit(componentInChildren, val4, removeCurrentArmor: false) && (Object)(object)val != (Object)(object)template)
			{
				SosigOutfitConfig outfit = GetOutfit(template, -1);
				if ((Object)(object)outfit != (Object)null && (Object)(object)outfit != (Object)(object)val4)
				{
					Debug.LogWarning((object)"[Twitch Chat Spawner] Using runtime H3VR armor because the selected custom outfit could not be loaded.");
					ApplyOutfit(componentInChildren, outfit, removeCurrentArmor: false);
				}
			}
			RegisterLinkSpawns(componentInChildren, val3);
			return componentInChildren;
		}

		private static SosigEnemyTemplate ResolveRuntimeTemplate(SosigEnemyTemplate template)
		{
			//IL_0051: 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 (HasResolvedSosigPrefab(template))
			{
				return template;
			}
			if ((Object)(object)template == (Object)null)
			{
				return null;
			}
			IM val = Object.FindObjectOfType<IM>();
			if ((Object)(object)val == (Object)null || val.odicSosigObjsByID == null)
			{
				return template;
			}
			if (val.odicSosigObjsByID.TryGetValue(template.SosigEnemyID, out var value) && HasResolvedSosigPrefab(value))
			{
				Debug.Log((object)("[Twitch Chat Spawner] Resolved missing Sosig prefab from H3VR template " + template.SosigEnemyID));
				return value;
			}
			return template;
		}

		private static bool HasResolvedSosigPrefab(SosigEnemyTemplate template)
		{
			if ((Object)(object)template == (Object)null || template.SosigPrefabs == null || template.SosigPrefabs.Count == 0)
			{
				return false;
			}
			for (int i = 0; i < template.SosigPrefabs.Count; i++)
			{
				if ((Object)(object)template.SosigPrefabs[i] != (Object)null)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsUsableTemplate(SosigEnemyTemplate template)
		{
			return (Object)(object)template != (Object)null && template.SosigPrefabs != null && template.SosigPrefabs.Count > 0 && template.ConfigTemplates != null && template.ConfigTemplates.Count > 0;
		}

		private static GameObject GetRandomObject(List<FVRObject> objects)
		{
			if (objects == null || objects.Count == 0)
			{
				return null;
			}
			FVRObject val = objects[Random.Range(0, objects.Count)];
			return (!((Object)(object)val == (Object)null)) ? ((AnvilAsset)val).GetGameObject() : null;
		}

		private static void EquipWeapon(Sosig sosig, GameObject prefab, Vector3 position, Quaternion rotation, bool initializeHands)
		{
			//IL_001f: 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_002a: 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_008d: 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)
			if ((Object)(object)sosig == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab, position + Vector3.up * 0.1f, rotation);
			SosigWeapon component = val.GetComponent<SosigWeapon>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val);
				return;
			}
			component.SetAutoDestroy(true);
			if ((Object)(object)component.O != (Object)null)
			{
				component.O.SpawnLockable = false;
			}
			component.SetAmmoClamping(true);
			component.IsShakeReloadable = false;
			if ((int)component.Type == 0)
			{
				sosig.Inventory.FillAmmoWithType(component.AmmoType);
			}
			if (initializeHands)
			{
				sosig.InitHands();
			}
			sosig.ForceEquip(component);
		}

		private bool TrySpawnAccessory(List<FVRObject> accessories, float chance, SosigLink link)
		{
			//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)
			if ((Object)(object)link == (Object)null || accessories == null || accessories.Count == 0 || Random.value >= chance)
			{
				return false;
			}
			int num = Random.Range(0, accessories.Count);
			for (int i = 0; i < accessories.Count; i++)
			{
				FVRObject val = accessories[(num + i) % accessories.Count];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				GameObject val2 = ResolveObjectPrefab(val);
				if ((Object)(object)val2 == (Object)null)
				{
					WarnMissingObjectOnce(val, "could not resolve its prefab");
					continue;
				}
				GameObject val3 = Object.Instantiate<GameObject>(val2, ((Component)link).transform.position, ((Component)link).transform.rotation);
				SosigWearable component = val3.GetComponent<SosigWearable>();
				if ((Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val3);
					WarnMissingObjectOnce(val, "does not contain a SosigWearable component");
					continue;
				}
				val3.transform.SetParent(((Component)link).transform);
				component.RegisterWearable(link);
				return true;
			}
			return false;
		}

		private static GameObject ResolveObjectPrefab(FVRObject selected)
		{
			string text = ((!string.IsNullOrEmpty(selected.ItemID)) ? selected.ItemID : ((Object)selected).name);
			if (!string.IsNullOrEmpty(text) && resolvedObjectPrefabs.TryGetValue(text, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			string assetName = ((AnvilAsset)selected).m_anvilPrefab.AssetName;
			if (!string.IsNullOrEmpty(assetName))
			{
				int num = Mathf.Max(assetName.LastIndexOf('/'), assetName.LastIndexOf('\\'));
				string text2 = ((num < 0) ? assetName : assetName.Substring(num + 1));
				if (text2.EndsWith(".prefab"))
				{
					text2 = text2.Substring(0, text2.Length - 7);
				}
				GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>();
				foreach (GameObject val in array)
				{
					if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, text2))
					{
						return CacheObjectPrefab(text, val);
					}
				}
			}
			if (!string.IsNullOrEmpty(selected.ItemID) && IM.OD != null && IM.OD.TryGetValue(selected.ItemID, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)value2 != (Object)(object)selected)
			{
				GameObject gameObject = ((AnvilAsset)value2).GetGameObject();
				if ((Object)(object)gameObject != (Object)null)
				{
					return CacheObjectPrefab(text, gameObject);
				}
			}
			GameObject gameObject2 = ((AnvilAsset)selected).GetGameObject();
			if ((Object)(object)gameObject2 != (Object)null)
			{
				return CacheObjectPrefab(text, gameObject2);
			}
			return null;
		}

		private static GameObject CacheObjectPrefab(string key, GameObject prefab)
		{
			if (!string.IsNullOrEmpty(key) && (Object)(object)prefab != (Object)null)
			{
				resolvedObjectPrefabs[key] = prefab;
			}
			return prefab;
		}

		private static void WarnMissingObjectOnce(FVRObject selected, string reason)
		{
			string text = (((Object)(object)selected == (Object)null) ? "unknown armor piece" : ((!string.IsNullOrEmpty(selected.ItemID)) ? selected.ItemID : ((Object)selected).name));
			string item = text + ":" + reason;
			if (missingObjectWarnings.Add(item))
			{
				Debug.LogWarning((object)("[Twitch Chat Spawner] Skipping armor piece " + text + " because it " + reason + "."));
			}
		}

		private static void RegisterLinkSpawns(Sosig sosig, SosigConfigTemplate config)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Invalid comparison between Unknown and I4
			if ((Object)(object)sosig == (Object)null || (Object)(object)config == (Object)null || !config.UsesLinkSpawns || config.LinkSpawns == null || config.LinkSpawnChance == null)
			{
				return;
			}
			int num = Mathf.Min(sosig.Links.Count, Mathf.Min(config.LinkSpawns.Count, config.LinkSpawnChance.Count));
			for (int i = 0; i < num; i++)
			{
				FVRObject val = config.LinkSpawns[i];
				if ((Object)(object)val != (Object)null && (int)val.Category != 50 && Random.value < config.LinkSpawnChance[i])
				{
					sosig.Links[i].RegisterSpawnOnDestroy(val);
				}
			}
		}

		private static Vector3 GetFriendlyFollowPoint()
		{
			//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_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_006f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = GM.CurrentPlayerBody.Head.position;
			float num = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f);
			float num2 = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f);
			return new Vector3(position.x + num, position.y, position.z + num2);
		}

		private static void AttachNamePlate(GameObject namePlatePrefab, Sosig sosig, bool isEnemyNamePlate)
		{
			//IL_0059: 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_0073: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: 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_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: 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)
			if ((Object)(object)namePlatePrefab == (Object)null || (Object)(object)sosig == (Object)null || sosig.Links == null || sosig.Links.Count < 1)
			{
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(namePlatePrefab, ((Component)sosig.Links[0]).transform, false);
			val.transform.localPosition = Vector3.up * 0.45f;
			val.transform.localRotation = Quaternion.identity;
			string text = ((!((Object)(object)ChatWatcher.instance == (Object)null)) ? ChatWatcher.instance.SpawnerName : "Chatter");
			Color val2 = ((!isEnemyNamePlate) ? new Color(0.2f, 1f, 0.2f, 1f) : new Color(1f, 0.15f, 0.15f, 1f));
			Text[] componentsInChildren = val.GetComponentsInChildren<Text>(true);
			Material val3 = null;
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].text = text;
				((Graphic)componentsInChildren[i]).color = val2;
				if ((Object)(object)val3 == (Object)null && (Object)(object)((Graphic)componentsInChildren[i]).material != (Object)null)
				{
					val3 = new Material(((Graphic)componentsInChildren[i]).material);
					if (val3.HasProperty("_EmissionColor"))
					{
						val3.SetColor("_EmissionColor", val2);
					}
					if (val3.HasProperty("_MainColor"))
					{
						val3.SetColor("_MainColor", val2);
					}
					if (val3.HasProperty("_Color"))
					{
						val3.SetColor("_Color", val2);
					}
				}
				if ((Object)(object)val3 != (Object)null)
				{
					((Graphic)componentsInChildren[i]).material = val3;
				}
			}
		}

		private static string CleanAssetName(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "Unnamed";
			}
			return value.Replace("Jedi", string.Empty).Replace("Outfit", string.Empty).Trim();
		}

		public void EmotionalConnection(GameObject nameBox, Sosig sosig, bool enemy)
		{
			AttachNamePlate(nameBox, sosig, enemy);
		}

		public void EmotionalConnectionButEvil(GameObject nameBox, Sosig sosig, bool enemy)
		{
			AttachNamePlate(nameBox, sosig, isEnemyNamePlate: true);
		}
	}
	[BepInPlugin("h3vr.arpy.chatspawner", "Twitch Chat Spawner", "2.1.0")]
	[BepInDependency("nrgill28.Sodalite", "1.5.1")]
	[BepInProcess("h3vr.exe")]
	public class ChatWatcher : BaseUnityPlugin
	{
		private sealed class SosigMovementState
		{
			public Vector3 LastPosition;

			public float LastMovementTime;

			public SosigMovementState(Vector3 position, float time)
			{
				//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)
				LastPosition = position;
				LastMovementTime = time;
			}
		}

		private sealed class FriendlyShot
		{
			public readonly Vector3 Origin;

			public readonly float ExpiresAt;

			public FriendlyShot(Vector3 origin, float expiresAt)
			{
				//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)
				Origin = origin;
				ExpiresAt = expiresAt;
			}
		}

		public static ChatWatcher instance;

		public static List<Sosig> spawnedChatters = new List<Sosig>();

		public static List<Sosig> spawnedEnemyChatters = new List<Sosig>();

		public static List<Sosig> spawnedBosses = new List<Sosig>();

		private static readonly string BasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

		private static LayerMask environmentMask;

		private static readonly List<FriendlyShot> recentFriendlyShots = new List<FriendlyShot>();

		private static readonly string[] FriendlyFallbackNames = new string[10] { "Bun Backup", "Captain Ketchup", "Chat Pal", "Cover Sausage", "Helpful Frank", "Hotdog Hero", "Mustard Medic", "Rescue Weiner", "Sir Snacks", "Tactical Tater" };

		private static readonly string[] EnemyFallbackNames = new string[10] { "Angry Frank", "Baron Bratwurst", "Chat Menace", "Colonel Crispy", "Danger Dog", "Hostile Hotdog", "Major Mustard", "Sneaky Sausage", "The Heckler", "Wurst Enemy" };

		private static readonly string[] BossFallbackNames = new string[10] { "Big Bad Brat", "Boss Beefcake", "Chat Overlord", "Duke of Damage", "Final Frank", "King Ketchup", "Lord Longdog", "Mega Meat", "Supreme Sausage", "The Big Weiner" };

		private ConfigEntry<FileInfo> friendlyNameFile;

		private ConfigEntry<FileInfo> enemyNameFile;

		private ConfigEntry<FileInfo> bossNameFile;

		private ConfigEntry<KeyCode> friendlySpawnKey;

		private ConfigEntry<KeyCode> enemySpawnKey;

		private ConfigEntry<KeyCode> bossSpawnKey;

		private ConfigEntry<bool> autoSpawnFriendly;

		private ConfigEntry<bool> autoSpawnEnemy;

		private ConfigEntry<bool> autoSpawnBoss;

		private ConfigEntry<float> filePollSeconds;

		private ConfigEntry<int> maxLivingPerTeam;

		private ConfigEntry<int> fallbackEnemyIFF;

		private ConfigEntry<bool> protectStreamer;

		private ConfigEntry<float> streamerSafetyRadius;

		private ConfigEntry<bool> preserveChatSosigsInTakeAndHold;

		private ConfigEntry<int> maxActiveBosses;

		private ConfigEntry<float> bossHealthMultiplier;

		private ConfigEntry<float> bossScaleMultiplier;

		private ConfigEntry<float> bossSpeedMultiplier;

		private ConfigEntry<bool> useSteamFriendsAsFallback;

		private ConfigEntry<bool> onlineSteamFriendsOnly;

		private ConfigEntry<bool> randomArmor;

		private ConfigEntry<float> steamFriendRefreshSeconds;

		private GameObject prefabToSpawn;

		private ChatSpawner prefabDefinition;

		private readonly List<WristMenuButton> wristButtons = new List<WristMenuButton>();

		private LockablePanel controlPanel;

		private LockablePanel armorPanel;

		private ArmorSwitchPanel armorPanelUi;

		private TextWidget panelStatusText;

		private TextWidget panelCountText;

		private TextWidget panelSelectionText;

		private ButtonWidget panelFriendlyArmorButton;

		private ButtonWidget panelEnemyTemplateButton;

		private ButtonWidget panelEnemyArmorButton;

		private ButtonWidget panelAutoFriendlyButton;

		private ButtonWidget panelAutoEnemyButton;

		private ButtonWidget panelAutoBossButton;

		private ButtonWidget panelProtectionButton;

		private string panelStatus = "Ready";

		private float nextFilePoll;

		private float nextSosigUpdate;

		private float nextFriendlyFireCheck;

		private long friendlyWriteTicks;

		private long enemyWriteTicks;

		private long bossWriteTicks;

		private int friendlyArmorIndex;

		private int enemyTemplateIndex;

		private int enemyArmorIndex;

		private int previousLivingBossCount;

		private readonly Dictionary<Sosig, float> blockedFriendlySince = new Dictionary<Sosig, float>();

		private readonly Dictionary<Sosig, SosigMovementState> movementStates = new Dictionary<Sosig, SosigMovementState>();

		private readonly List<string> steamFriendNames = new List<string>();

		private Harmony harmony;

		private int steamFriendIndex = -1;

		private float nextSteamFriendRefresh;

		public string SpawnerName = "Chatter";

		public TNH_Manager TNHManager;

		public int enemyIFF = 3;

		public void Awake()
		{
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			instance = this;
			spawnedChatters = new List<Sosig>();
			spawnedEnemyChatters = new List<Sosig>();
			spawnedBosses = new List<Sosig>();
			recentFriendlyShots.Clear();
			movementStates.Clear();
			EnsureDefaultChatDirectory();
			RegisterFilePathConverter();
			friendlyNameFile = ((BaseUnityPlugin)this).Config.Bind<FileInfo>("Chat files", "Friendly name file", new FileInfo(Path.Combine(Paths.ConfigPath, Path.Combine("TwitchChatSpawner", "friendly.txt"))), "File path to the text, JSON, or INI file written by LioranBoard for friendly viewer names.");
			enemyNameFile = ((BaseUnityPlugin)this).Config.Bind<FileInfo>("Chat files", "Enemy name file", new FileInfo(Path.Combine(Paths.ConfigPath, Path.Combine("TwitchChatSpawner", "enemy.txt"))), "File path to the text, JSON, or INI file written by LioranBoard for enemy viewer names.");
			bossNameFile = ((BaseUnityPlugin)this).Config.Bind<FileInfo>("Chat files", "Boss name file", new FileInfo(Path.Combine(Paths.ConfigPath, Path.Combine("TwitchChatSpawner", "boss.txt"))), "File path to the text, JSON, or INI file written by LioranBoard when a viewer becomes the boss.");
			autoSpawnFriendly = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat files", "Auto-spawn friendlies", false, "Spawn a friendly whenever the friendly name file changes.");
			autoSpawnEnemy = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat files", "Auto-spawn enemies", false, "Spawn an enemy whenever the enemy name file changes.");
			autoSpawnBoss = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat files", "Auto-spawn bosses", false, "Spawn a boss whenever the boss name file changes. Only the configured number of bosses may live at once.");
			filePollSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Chat files", "Poll interval seconds", 0.5f, "How often to check the chat name files for a new viewer.");
			useSteamFriendsAsFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Steam friends", "Use friends without chat files", true, "When a chat name file is missing or empty, cycle through your Steam friends instead of using a generic name.");
			onlineSteamFriendsOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Steam friends", "Online friends only", false, "Only use friends who are currently online. Turn this off to include the full friends list.");
			randomArmor = ((BaseUnityPlugin)this).Config.Bind<bool>("Armor", "Random T&H armor", true, "Pick a complete outfit independently for each chat Sosig from the game's registered enemy templates. Includes custom registered outfits. Applies to allies, enemies, and bosses.");
			steamFriendRefreshSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Steam friends", "Refresh interval seconds", 60f, "How often the locally cached Steam friends list may be refreshed.");
			friendlySpawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Friendly spawn key", (KeyCode)112, "Keyboard shortcut for spawning a friendly chatter.");
			enemySpawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Enemy spawn key", (KeyCode)263, "Keyboard shortcut for spawning an enemy chatter.");
			bossSpawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Boss spawn key", (KeyCode)98, "Keyboard shortcut for spawning the current chat viewer as a boss.");
			maxLivingPerTeam = ((BaseUnityPlugin)this).Config.Bind<int>("Safety", "Maximum living per team", 30, "Stops chat events from spawning too many Sosigs. Use 0 for unlimited.");
			fallbackEnemyIFF = ((BaseUnityPlugin)this).Config.Bind<int>("Safety", "Enemy IFF outside Take and Hold", 3, "IFF used when an enemy is spawned outside Take and Hold.");
			protectStreamer = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "Protect streamer from friendly fire", true, "Stops friendly guns when the player is directly between a friendly and its target.");
			streamerSafetyRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "Streamer firing-lane radius", 0.8f, "Width in metres of the protected firing lane around the player's head and torso.");
			preserveChatSosigsInTakeAndHold = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "Preserve chat Sosigs during Take and Hold cleanup", true, "Keeps living chat-spawned allies, enemies, and bosses when a hold clears ordinary Sosigs. Dead chat Sosigs still clean up normally.");
			maxActiveBosses = ((BaseUnityPlugin)this).Config.Bind<int>("Boss", "Maximum active bosses", 1, "Maximum number of living chat bosses allowed at once.");
			bossHealthMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boss", "Health multiplier", 6f, "Multiplies the integrity of every boss body link.");
			bossScaleMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boss", "Scale multiplier", 1.25f, "Makes the boss visually larger. Values above 1.5 may cause physics problems.");
			bossSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boss", "Speed multiplier", 1.1f, "Multiplies the boss movement speeds.");
			InstallRuntimePatches();
			TryResolveSpawnerPrefab();
			environmentMask = LayerMask.op_Implicit(LayerMask.GetMask(new string[1] { "Environment" }));
			SeedFileWatchers();
			InitializeControlPanel();
			RegisterWristMenuButtons();
		}

		private void EnsureDefaultChatDirectory()
		{
			try
			{
				Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "TwitchChatSpawner"));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not create the default chat-file directory: " + ex.Message));
			}
		}

		private static void RegisterFilePathConverter()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			if (!TomlTypeConverter.CanConvert(typeof(FileInfo)))
			{
				Type? typeFromHandle = typeof(FileInfo);
				TypeConverter val = new TypeConverter();
				val.ConvertToString = (object value, Type type) => (value != null) ? ((FileInfo)value).FullName : string.Empty;
				val.ConvertToObject = (string value, Type type) => ConvertToChatFile(value);
				TomlTypeConverter.AddConverter(typeFromHandle, val);
			}
		}

		private static FileInfo ConvertToChatFile(string value)
		{
			string name = ((value != null) ? value.Trim().Trim('"', '\'') : string.Empty);
			name = Environment.ExpandEnvironmentVariables(name);
			if (string.IsNullOrEmpty(name) || string.Equals(name, "null", StringComparison.OrdinalIgnoreCase))
			{
				name = "null";
			}
			if (!Path.IsPathRooted(name))
			{
				string text = Paths.ConfigPath;
				if (string.IsNullOrEmpty(text))
				{
					text = ((!string.IsNullOrEmpty(BasePath)) ? BasePath : Environment.CurrentDirectory);
				}
				name = Path.Combine(text, name);
			}
			return new FileInfo(Path.GetFullPath(name));
		}

		private bool TryResolveSpawnerPrefab()
		{
			if ((Object)(object)prefabToSpawn != (Object)null && (Object)(object)prefabDefinition != (Object)null)
			{
				return true;
			}
			ChatSpawner[] array = Resources.FindObjectsOfTypeAll<ChatSpawner>();
			foreach (ChatSpawner chatSpawner in array)
			{
				if (!((Object)(object)chatSpawner == (Object)null) && !((Object)(object)((Component)chatSpawner).gameObject == (Object)null))
				{
					prefabDefinition = chatSpawner;
					prefabToSpawn = ((Component)chatSpawner).gameObject;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Found spawner prefab " + ((Object)prefabToSpawn).name));
					return true;
				}
			}
			string[] array2 = new string[3] { "TwitchChatSpawnerAssets", "TwitchChatSpawner", "JediSpawner" };
			for (int j = 0; j < array2.Length; j++)
			{
				string text = Path.Combine(BasePath, array2[j]);
				if (File.Exists(text))
				{
					AssetBundle val = AssetBundle.LoadFromFile(text);
					if ((Object)(object)val != (Object)null && TryGetSpawnerFromBundle(val))
					{
						return true;
					}
				}
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Spawner prefab is not loaded yet. Looked beside the plugin for TwitchChatSpawner and JediSpawner bundles; the next spawn attempt will retry.");
			return false;
		}

		private bool TryGetSpawnerFromBundle(AssetBundle bundle)
		{
			string[] array = new string[3] { "Twitch Chat Spawner", "Assets/CompletedBounties/jediSpawner/Twitch Chat Spawner.prefab", "Jedit'sSpawner" };
			for (int i = 0; i < array.Length; i++)
			{
				GameObject val = bundle.LoadAsset<GameObject>(array[i]);
				ChatSpawner chatSpawner = ((!((Object)(object)val == (Object)null)) ? val.GetComponent<ChatSpawner>() : null);
				if (!((Object)(object)chatSpawner == (Object)null))
				{
					prefabToSpawn = val;
					prefabDefinition = chatSpawner;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded spawner prefab " + ((Object)prefabToSpawn).name + " from bundle " + ((Object)bundle).name));
					return true;
				}
			}
			return false;
		}

		private void RegisterWristMenuButtons()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			AddWristButton("TCS: Open Armor Panel", new ButtonClickEvent(OnSpawnArmorPanelButton));
		}

		private void AddWristButton(string label, ButtonClickEvent callback)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			WristMenuButton item = new WristMenuButton(label, callback);
			wristButtons.Add(item);
			WristMenuAPI.Buttons.Add(item);
		}

		private void InitializeControlPanel()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			controlPanel = new LockablePanel();
			controlPanel.Configure += ConfigureControlPanel;
			armorPanel = new LockablePanel();
			armorPanel.Configure += ConfigureArmorPanel;
		}

		private void ConfigureArmorPanel(GameObject panel)
		{
			//IL_0077: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((!((Object)(object)panel == (Object)null)) ? panel.transform.Find("OptionsCanvas_0_Main/Canvas") : null);
			GameObject val2 = ((!((Object)(object)prefabDefinition == (Object)null)) ? prefabDefinition.ArmorPanelPrefab : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Armor panel prefab is missing. Generate it with Tools > Twitch Chat Spawner > Create Armor Panel in MeatKit, then rebuild the bundle.");
				return;
			}
			GameObject val3 = Object.Instantiate<GameObject>(val2, val.position, val.rotation, val.parent);
			val3.transform.localScale = val.localScale;
			ArmorSwitchPanel component = val3.GetComponent<ArmorSwitchPanel>();
			if ((Object)(object)component == (Object)null || (Object)(object)val3.GetComponent<UniversalModPanel>() == (Object)null)
			{
				Object.Destroy((Object)(object)val3);
				((BaseUnityPlugin)this).Logger.LogError((object)"Armor panel prefab needs ArmorSwitchPanel and UniversalModPanel components.");
				return;
			}
			component.Bind(this);
			val3.GetComponent<UniversalModPanel>().Navigate("Armor");
			armorPanelUi = component;
			((Component)val).gameObject.SetActive(false);
			Object.Destroy((Object)(object)((Component)val).gameObject);
			RefreshPanelUi();
		}

		private void ConfigureControlPanel(GameObject panel)
		{
			panelStatusText = null;
			panelCountText = null;
			panelSelectionText = null;
			panelFriendlyArmorButton = null;
			panelEnemyTemplateButton = null;
			panelEnemyArmorButton = null;
			panelAutoFriendlyButton = null;
			panelAutoEnemyButton = null;
			panelAutoBossButton = null;
			panelProtectionButton = null;
			Transform val = ((!((Object)(object)panel == (Object)null)) ? panel.transform.Find("OptionsCanvas_0_Main/Canvas") : null);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not find the Sodalite lockable-panel canvas.");
				return;
			}
			UiWidget.CreateAndConfigureWidget<GridLayoutWidget>(((Component)val).gameObject, (Action<GridLayoutWidget>)delegate(GridLayoutWidget grid)
			{
				//IL_0016: 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_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_006a: 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_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_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_0136: Unknown result type (might be due to invalid IL or missing references)
				//IL_0140: Expected O, but got Unknown
				//IL_014e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0158: Expected O, but got Unknown
				//IL_0166: Unknown result type (might be due to invalid IL or missing references)
				//IL_0170: Expected O, but got Unknown
				//IL_017f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0189: Expected O, but got Unknown
				//IL_019c: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a6: Expected O, but got Unknown
				//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c3: Expected O, but got Unknown
				//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_01df: Expected O, but got Unknown
				//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f7: Expected O, but got Unknown
				//IL_0205: Unknown result type (might be due to invalid IL or missing references)
				//IL_020f: Expected O, but got Unknown
				//IL_021e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0228: Expected O, but got Unknown
				//IL_023b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0245: Expected O, but got Unknown
				//IL_0258: Unknown result type (might be due to invalid IL or missing references)
				//IL_0262: Expected O, but got Unknown
				//IL_0275: Unknown result type (might be due to invalid IL or missing references)
				//IL_027f: Expected O, but got Unknown
				((Transform)((UiWidget)grid).RectTransform).localScale = new Vector3(0.07f, 0.07f, 0.07f);
				((Transform)((UiWidget)grid).RectTransform).localPosition = Vector3.zero;
				((UiWidget)grid).RectTransform.anchoredPosition = Vector2.zero;
				((UiWidget)grid).RectTransform.sizeDelta = new Vector2(528.5714f, 342.85715f);
				((UiWidget)grid).RectTransform.pivot = new Vector2(0.5f, 1f);
				((Transform)((UiWidget)grid).RectTransform).localRotation = Quaternion.identity;
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.cellSize = new Vector2(171f, 50f);
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.spacing = Vector2.one * 4f;
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.startCorner = (Corner)0;
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.startAxis = (Axis)0;
				((LayoutGroup)((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup).childAlignment = (TextAnchor)1;
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.constraint = (Constraint)1;
				((LayoutWidget<GridLayoutGroup>)(object)grid).LayoutGroup.constraintCount = 3;
				AddPanelText(grid, "TWITCH CHAT\nSPAWNER", 20);
				panelCountText = AddPanelText(grid, "Alive\nF: 0  E: 0  B: 0", 16);
				panelStatusText = AddPanelText(grid, panelStatus, 14);
				AddPanelButton(grid, "SPAWN FRIENDLY", new ButtonClickEvent(OnSpawnFriendlyButton));
				AddPanelButton(grid, "SPAWN ENEMY", new ButtonClickEvent(OnSpawnEnemyButton));
				AddPanelButton(grid, "SPAWN BOSS", new ButtonClickEvent(OnSpawnBossButton));
				panelFriendlyArmorButton = AddPanelButton(grid, "Friendly Armor", new ButtonClickEvent(OnNextFriendlyArmorButton));
				panelEnemyTemplateButton = AddPanelButton(grid, "Enemy Type", new ButtonClickEvent(OnNextEnemyTypeButton));
				panelEnemyArmorButton = AddPanelButton(grid, "Enemy Armor", new ButtonClickEvent(OnNextEnemyArmorButton));
				AddPanelButton(grid, "APPLY FRIENDLY\nARMOR", new ButtonClickEvent(OnApplyFriendlyArmorButton));
				AddPanelButton(grid, "REFRESH STEAM\nFRIENDS", new ButtonClickEvent(OnRefreshSteamFriendsButton));
				AddPanelButton(grid, "APPLY ENEMY\nARMOR", new ButtonClickEvent(OnApplyEnemyArmorButton));
				panelAutoFriendlyButton = AddPanelButton(grid, "Auto Friendly", new ButtonClickEvent(OnToggleAutoFriendlyButton));
				panelAutoEnemyButton = AddPanelButton(grid, "Auto Enemy", new ButtonClickEvent(OnToggleAutoEnemyButton));
				panelAutoBossButton = AddPanelButton(grid, "Auto Boss", new ButtonClickEvent(OnToggleAutoBossButton));
				panelProtectionButton = AddPanelButton(grid, "Streamer Protection", new ButtonClickEvent(OnToggleStreamerProtectionButton));
				panelSelectionText = AddPanelText(grid, "Selections", 13);
				AddPanelText(grid, "Point + trigger\nLock icon pins panel", 13);
			});
			RefreshPanelUi();
		}

		private static TextWidget AddPanelText(GridLayoutWidget grid, string value, int maximumFontSize)
		{
			TextWidget result = null;
			((LayoutWidget<GridLayoutGroup>)(object)grid).AddChild<TextWidget>((Action<TextWidget>)delegate(TextWidget text)
			{
				//IL_007e: Unknown result type (might be due to invalid IL or missing references)
				result = text;
				text.Text.text = value;
				text.Text.alignment = (TextAnchor)4;
				text.Text.fontSize = maximumFontSize;
				text.Text.resizeTextForBestFit = true;
				text.Text.resizeTextMinSize = 9;
				text.Text.resizeTextMaxSize = maximumFontSize;
				text.Text.horizontalOverflow = (HorizontalWrapMode)0;
				text.Text.verticalOverflow = (VerticalWrapMode)0;
				((Transform)((UiWidget)text).RectTransform).localRotation = Quaternion.identity;
			});
			return result;
		}

		private static ButtonWidget AddPanelButton(GridLayoutWidget grid, string label, ButtonClickEvent callback)
		{
			ButtonWidget result = null;
			((LayoutWidget<GridLayoutGroup>)(object)grid).AddChild<ButtonWidget>((Action<ButtonWidget>)delegate(ButtonWidget button)
			{
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				result = button;
				button.ButtonText.text = label;
				button.ButtonText.fontSize = 16;
				button.ButtonText.resizeTextForBestFit = true;
				button.ButtonText.resizeTextMinSize = 9;
				button.ButtonText.resizeTextMaxSize = 16;
				button.ButtonText.horizontalOverflow = (HorizontalWrapMode)0;
				button.ButtonText.verticalOverflow = (VerticalWrapMode)0;
				((Component)button.ButtonText).transform.localRotation = Quaternion.identity;
				button.AddButtonListener(callback);
			});
			return result;
		}

		private void RefreshPanelUi()
		{
			if ((Object)(object)armorPanelUi != (Object)null)
			{
				armorPanelUi.Refresh(prefabDefinition, friendlyArmorIndex, enemyTemplateIndex, enemyArmorIndex, panelStatus, randomArmor.Value);
			}
			if (!((Object)(object)panelCountText == (Object)null))
			{
				panelCountText.Text.text = "Alive\nF: " + GetLivingCount(spawnedChatters) + "  E: " + GetLivingCount(spawnedEnemyChatters) + "  B: " + GetLivingCount(spawnedBosses);
				if ((Object)(object)panelStatusText != (Object)null)
				{
					panelStatusText.Text.text = ShortPanelValue(panelStatus, 44);
				}
				string value = ((!((Object)(object)prefabDefinition == (Object)null)) ? prefabDefinition.GetFriendlyOutfitName(friendlyArmorIndex) : "Unavailable");
				string value2 = ((!((Object)(object)prefabDefinition == (Object)null)) ? prefabDefinition.GetEnemyTemplateName(enemyTemplateIndex) : "Unavailable");
				string value3 = ((!((Object)(object)prefabDefinition == (Object)null)) ? prefabDefinition.GetEnemyOutfitName(enemyTemplateIndex, enemyArmorIndex) : "Unavailable");
				if ((Object)(object)panelFriendlyArmorButton != (Object)null)
				{
					panelFriendlyArmorButton.ButtonText.text = "FRIENDLY ARMOR\n" + ShortPanelValue(value, 20);
				}
				if ((Object)(object)panelEnemyTemplateButton != (Object)null)
				{
					panelEnemyTemplateButton.ButtonText.text = "ENEMY TYPE\n" + ShortPanelValue(value2, 20);
				}
				if ((Object)(object)panelEnemyArmorButton != (Object)null)
				{
					panelEnemyArmorButton.ButtonText.text = "ENEMY ARMOR\n" + ShortPanelValue(value3, 20);
				}
				if ((Object)(object)panelAutoFriendlyButton != (Object)null)
				{
					panelAutoFriendlyButton.ButtonText.text = "AUTO FRIENDLY\n" + OnOff(autoSpawnFriendly.Value);
				}
				if ((Object)(object)panelAutoEnemyButton != (Object)null)
				{
					panelAutoEnemyButton.ButtonText.text = "AUTO ENEMY\n" + OnOff(autoSpawnEnemy.Value);
				}
				if ((Object)(object)panelAutoBossButton != (Object)null)
				{
					panelAutoBossButton.ButtonText.text = "AUTO BOSS\n" + OnOff(autoSpawnBoss.Value);
				}
				if ((Object)(object)panelProtectionButton != (Object)null)
				{
					panelProtectionButton.ButtonText.text = "STREAMER PROTECTION\n" + OnOff(protectStreamer.Value);
				}
				if ((Object)(object)panelSelectionText != (Object)null)
				{
					panelSelectionText.Text.text = "F: " + ShortPanelValue(value, 15) + "\nE: " + ShortPanelValue(value2, 10) + " / " + ShortPanelValue(value3, 10);
				}
			}
		}

		private static string OnOff(bool value)
		{
			return (!value) ? "OFF" : "ON";
		}

		private static string ShortPanelValue(string value, int maximumLength)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "None";
			}
			value = value.Replace("\r", " ").Replace("\n", " ").Trim();
			if (value.Length <= maximumLength)
			{
				return value;
			}
			return value.Substring(0, Mathf.Max(1, maximumLength - 3)) + "...";
		}

		private void OnDestroy()
		{
			for (int i = 0; i < wristButtons.Count; i++)
			{
				WristMenuAPI.Buttons.Remove(wristButtons[i]);
			}
			wristButtons.Clear();
			if (controlPanel != null)
			{
				controlPanel.Configure -= ConfigureControlPanel;
			}
			if (armorPanel != null)
			{
				armorPanel.Configure -= ConfigureArmorPanel;
			}
			if (harmony != null)
			{
				harmony.UnpatchSelf();
				harmony = null;
			}
			recentFriendlyShots.Clear();
			movementStates.Clear();
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}

		public void Update()
		{
			//IL_0007: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(friendlySpawnKey.Value))
			{
				SpawnFriendlyFromCurrentFile();
			}
			if (Input.GetKeyDown(enemySpawnKey.Value))
			{
				SpawnEnemyFromCurrentFile();
			}
			if (Input.GetKeyDown(bossSpawnKey.Value))
			{
				SpawnBossFromCurrentFile();
			}
			if (Time.unscaledTime >= nextFilePoll)
			{
				nextFilePoll = Time.unscaledTime + Mathf.Max(0.1f, filePollSeconds.Value);
				PollChatFiles();
			}
			if (Time.unscaledTime >= nextSosigUpdate)
			{
				nextSosigUpdate = Time.unscaledTime + 0.5f;
				UpdateSpawnedSosigs();
				RefreshPanelUi();
			}
			if (protectStreamer.Value && Time.unscaledTime >= nextFriendlyFireCheck)
			{
				nextFriendlyFireCheck = Time.unscaledTime + 0.1f;
				ProtectStreamerFromFriendlyFire();
			}
		}

		public static void RegisterFriendly(Sosig sosig)
		{
			if ((Object)(object)sosig != (Object)null && !spawnedChatters.Contains(sosig))
			{
				spawnedChatters.Add(sosig);
			}
		}

		public static void RegisterEnemy(Sosig sosig)
		{
			if ((Object)(object)sosig != (Object)null && !spawnedEnemyChatters.Contains(sosig))
			{
				spawnedEnemyChatters.Add(sosig);
			}
		}

		public static void RegisterBoss(Sosig sosig)
		{
			if ((Object)(object)sosig != (Object)null && !spawnedBosses.Contains(sosig))
			{
				spawnedBosses.Add(sosig);
			}
		}

		public void SpawnFriendlyFromCurrentFile()
		{
			if (!TryReadChatName(friendlyNameFile.Value, out var chatterName) && !TryGetNextSteamFriendName(out chatterName))
			{
				chatterName = GetRandomFallbackName(FriendlyFallbackNames, "Friendly Chatter");
			}
			SpawnFriendly(chatterName);
		}

		public void SpawnEnemyFromCurrentFile()
		{
			if (!TryReadChatName(enemyNameFile.Value, out var chatterName) && !TryGetNextSteamFriendName(out chatterName))
			{
				chatterName = GetRandomFallbackName(EnemyFallbackNames, "Enemy Chatter");
			}
			SpawnEnemy(chatterName);
		}

		public void SpawnBossFromCurrentFile()
		{
			if (!TryReadChatName(bossNameFile.Value, out var chatterName) && !TryReadChatName(enemyNameFile.Value, out chatterName) && !TryGetNextSteamFriendName(out chatterName))
			{
				chatterName = GetRandomFallbackName(BossFallbackNames, "Chat Boss");
			}
			SpawnBoss(ExtractBossName(chatterName));
		}

		public bool SpawnFriendly(string chatterName)
		{
			//IL_0067: 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)
			PruneNullEntries(spawnedChatters);
			if (!CanSpawn(GetLivingCount(spawnedChatters), "friendly") || !CanUseSpawner())
			{
				return false;
			}
			if (!TryGetPlayerSpawnPoint(1.5f, out var spawnPoint))
			{
				return false;
			}
			SpawnerName = CleanName(chatterName, "Friendly Chatter");
			GameObject val = Object.Instantiate<GameObject>(prefabToSpawn, spawnPoint, Quaternion.identity);
			ChatSpawner component = val.GetComponent<ChatSpawner>();
			Sosig val2 = ((!((Object)(object)component == (Object)null)) ? component.SpawningSequence((!randomArmor.Value) ? friendlyArmorIndex : (-2)) : null);
			Object.Destroy((Object)(object)val);
			SetStatus((!((Object)(object)val2 == (Object)null)) ? ("Spawned " + SpawnerName) : "Friendly spawn failed");
			return (Object)(object)val2 != (Object)null;
		}

		public bool SpawnEnemy(string chatterName)
		{
			//IL_0064: 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)
			PruneNullEntries(spawnedEnemyChatters);
			if (!CanSpawn(GetLivingCount(spawnedEnemyChatters), "enemy") || !CanUseSpawner())
			{
				return false;
			}
			if (!TryGetEnemySpawnPoint(out var spawnPoint, out var iff))
			{
				return false;
			}
			SpawnerName = CleanName(chatterName, "Enemy Chatter");
			GameObject val = Object.Instantiate<GameObject>(prefabToSpawn, spawnPoint, Quaternion.identity);
			ChatSpawner component = val.GetComponent<ChatSpawner>();
			Sosig val2 = ((!((Object)(object)component == (Object)null)) ? component.SpawningSequenceEnemy(iff, enemyTemplateIndex, (!randomArmor.Value) ? enemyArmorIndex : (-2)) : null);
			Object.Destroy((Object)(object)val);
			SetStatus((!((Object)(object)val2 == (Object)null)) ? ("Spawned enemy " + SpawnerName) : "Enemy spawn failed");
			return (Object)(object)val2 != (Object)null;
		}

		public bool SpawnBoss(string chatterName)
		{
			//IL_00ca: 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)
			PruneNullEntries(spawnedBosses);
			int num = Mathf.Max(1, maxActiveBosses.Value);
			if (GetLivingCount(spawnedBosses) >= num)
			{
				SetStatus("A chat boss is already alive");
				return false;
			}
			if (!CanUseSpawner())
			{
				return false;
			}
			int iff;
			Vector3 spawnPoint;
			if ((Object)(object)GM.TNH_Manager == (Object)null)
			{
				iff = fallbackEnemyIFF.Value;
				if (!TryGetPlayerSpawnPoint(8f, out spawnPoint))
				{
					return false;
				}
			}
			else if (!TryGetEnemySpawnPoint(out spawnPoint, out iff))
			{
				return false;
			}
			string text = CleanName(chatterName, "Chat Boss");
			SpawnerName = "BOSS: " + text;
			GameObject val = Object.Instantiate<GameObject>(prefabToSpawn, spawnPoint, Quaternion.identity);
			ChatSpawner component = val.GetComponent<ChatSpawner>();
			Sosig val2 = ((!((Object)(object)component == (Object)null)) ? component.SpawningSequenceBoss(iff, enemyTemplateIndex, (!randomArmor.Value) ? enemyArmorIndex : (-2), bossHealthMultiplier.Value, bossScaleMultiplier.Value, bossSpeedMultiplier.Value) : null);
			Object.Destroy((Object)(object)val);
			previousLivingBossCount = GetLivingCount(spawnedBosses);
			SetStatus((!((Object)(object)val2 == (Object)null)) ? (text + " became the boss") : "Boss spawn failed");
			return (Object)(object)val2 != (Object)null;
		}

		public void CycleFriendlyArmor()
		{
			StepFriendlyArmor(1);
		}

		public void StepFriendlyArmor(int direction)
		{
			if (!((Object)(object)prefabDefinition == (Object)null))
			{
				int friendlyOutfitCount = prefabDefinition.GetFriendlyOutfitCount();
				if (friendlyOutfitCount == 0)
				{
					SetStatus("No friendly armor profiles are configured");
					return;
				}
				friendlyArmorIndex = WrapIndex(friendlyArmorIndex + direction, friendlyOutfitCount);
				SetStatus("Friendly armor: " + prefabDefinition.GetFriendlyOutfitName(friendlyArmorIndex));
			}
		}

		public void CycleEnemyTemplate()
		{
			StepEnemyTemplate(1);
		}

		public void StepEnemyTemplate(int direction)
		{
			if (!((Object)(object)prefabDefinition == (Object)null))
			{
				int enemyTemplateCount = prefabDefinition.GetEnemyTemplateCount();
				if (enemyTemplateCount == 0)
				{
					SetStatus("No enemy types are configured");
					return;
				}
				enemyTemplateIndex = WrapIndex(enemyTemplateIndex + direction, enemyTemplateCount);
				enemyArmorIndex = 0;
				SetStatus("Enemy type: " + prefabDefinition.GetEnemyTemplateName(enemyTemplateIndex) + "; armor: " + prefabDefinition.GetEnemyOutfitName(enemyTemplateIndex, enemyArmorIndex));
			}
		}

		public void CycleEnemyArmor()
		{
			StepEnemyArmor(1);
		}

		public void StepEnemyArmor(int direction)
		{
			if (!((Object)(object)prefabDefinition == (Object)null))
			{
				int enemyOutfitCount = prefabDefinition.GetEnemyOutfitCount(enemyTemplateIndex);
				if (enemyOutfitCount == 0)
				{
					SetStatus("No armor profiles are configured for the selected enemy type");
					return;
				}
				enemyArmorIndex = WrapIndex(enemyArmorIndex + direction, enemyOutfitCount);
				SetStatus("Enemy armor: " + prefabDefinition.GetEnemyOutfitName(enemyTemplateIndex, enemyArmorIndex));
			}
		}

		public void ApplyFriendlyArmorToAll()
		{
			if (!((Object)(object)prefabDefinition == (Object)null) && !((Object)(object)prefabDefinition.t == (Object)null))
			{
				SosigOutfitConfig outfit = prefabDefinition.GetOutfit(prefabDefinition.t, friendlyArmorIndex);
				int num = ApplyOutfitToList(spawnedChatters, outfit);
				SetStatus("Changed armor on " + num + " friendlies");
			}
		}

		public void ToggleRandomArmor()
		{
			randomArmor.Value = !randomArmor.Value;
			SetStatus("Random T&H armor: " + ((!randomArmor.Value) ? "OFF (use selected outfits)" : "ON (one outfit per Sosig)"));
		}

		public void ApplyEnemyArmorToAll()
		{
			if (!((Object)(object)prefabDefinition == (Object)null))
			{
				SosigEnemyTemplate enemyTemplate = prefabDefinition.GetEnemyTemplate(enemyTemplateIndex);
				SosigOutfitConfig outfit = prefabDefinition.GetOutfit(enemyTemplate, enemyArmorIndex);
				int num = ApplyOutfitToList(spawnedEnemyChatters, outfit);
				SetStatus("Changed armor on " + num + " enemies");
			}
		}

		private int ApplyOutfitToList(List<Sosig> sosigs, SosigOutfitConfig outfit)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Invalid comparison between Unknown and I4
			if ((Object)(object)prefabDefinition == (Object)null || (!randomArmor.Value && (Object)(object)outfit == (Object)null))
			{
				return 0;
			}
			List<SosigOutfitConfig> list = ((!randomArmor.Value) ? null : prefabDefinition.GetAvailableArmorOutfits());
			if (randomArmor.Value && list.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Runtime armor catalog is unavailable. Current armor was retained.");
				return 0;
			}
			PruneNullEntries(sosigs);
			int num = 0;
			for (int i = 0; i < sosigs.Count; i++)
			{
				Sosig val = sosigs[i];
				if (!((Object)(object)val == (Object)null) && (int)val.BodyState != 3)
				{
					SosigOutfitConfig outfit2 = ((!randomArmor.Value) ? outfit : prefabDefinition.PickRandomArmor(list));
					if (prefabDefinition.ApplyOutfit(val, outfit2, removeCurrentArmor: true))
					{
						num++;
					}
				}
			}
			return num;
		}

		private void PollChatFiles()
		{
			if (autoSpawnFriendly.Value && HasFileChanged(friendlyNameFile.Value, ref friendlyWriteTicks))
			{
				SpawnFriendlyFromCurrentFile();
			}
			if (autoSpawnEnemy.Value && HasFileChanged(enemyNameFile.Value, ref enemyWriteTicks) && TryReadChatName(enemyNameFile.Value, out var chatterName))
			{
				if (IsBossCommand(chatterName))
				{
					SpawnBoss(ExtractBossName(chatterName));
				}
				else
				{
					SpawnEnemy(chatterName);
				}
			}
			if (autoSpawnBoss.Value && HasFileChanged(bossNameFile.Value, ref bossWriteTicks))
			{
				SpawnBossFromCurrentFile();
			}
		}

		private void SeedFileWatchers()
		{
			friendlyWriteTicks = GetWriteTicks(friendlyNameFile.Value);
			enemyWriteTicks = GetWriteTicks(enemyNameFile.Value);
			bossWriteTicks = GetWriteTicks(bossNameFile.Value);
		}

		private static bool HasFileChanged(FileInfo file, ref long previousTicks)
		{
			long writeTicks = GetWriteTicks(file);
			if (writeTicks == 0)
			{
				return false;
			}
			if (previousTicks == 0)
			{
				previousTicks = writeTicks;
				return false;
			}
			if (writeTicks <= previousTicks)
			{
				return false;
			}
			previousTicks = writeTicks;
			return true;
		}

		private static long GetWriteTicks(FileInfo file)
		{
			try
			{
				return (!IsUsablePath(file) || !File.Exists(file.FullName)) ? 0 : File.GetLastWriteTimeUtc(file.FullName).Ticks;
			}
			catch
			{
				return 0L;
			}
		}

		private static bool TryReadChatName(FileInfo file, out string chatterName)
		{
			chatterName = string.Empty;
			if (!IsUsablePath(file) || !File.Exists(file.FullName))
			{
				return false;
			}
			try
			{
				string contents;
				using (FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
				{
					using StreamReader streamReader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true);
					contents = streamReader.ReadToEnd();
				}
				return TryExtractChatName(contents, out chatterName);
			}
			catch
			{
				return false;
			}
		}

		private static bool TryExtractChatName(string contents, out string chatterName)
		{
			chatterName = string.Empty;
			if (string.IsNullOrEmpty(contents))
			{
				return false;
			}
			contents = contents.Trim().TrimStart(new char[1] { '\ufeff' });
			if (contents.Length == 0)
			{
				return false;
			}
			Match match = Regex.Match(contents, "\"(?:name|user_?name|viewer|chatter|display_?name)\"\\s*:\\s*\"((?:\\\\.|[^\"])*)\"", RegexOptions.IgnoreCase);
			if (match.Success)
			{
				return TryCleanChatName(match.Groups[1].Value, out chatterName);
			}
			if (contents.Length >= 2 && contents[0] == '"' && contents[contents.Length - 1] == '"')
			{
				return TryCleanChatName(contents.Substring(1, contents.Length - 2), out chatterName);
			}
			string[] array = contents.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text[0] == ';' || text[0] == '#' || (text[0] == '[' && text[text.Length - 1] == ']'))
				{
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string key = text.Substring(0, num).Trim().Replace("_", string.Empty)
						.Replace("-", string.Empty)
						.Replace(" ", string.Empty);
					if (IsChatNameKey(key))
					{
						return TryCleanChatName(text.Substring(num + 1), out chatterName);
					}
				}
			}
			for (int j = 0; j < array.Length; j++)
			{
				string text2 = array[j].Trim();
				if (text2.Length != 0 && text2[0] != ';' && text2[0] != '#' && text2[0] != '[' && text2.IndexOf('=') < 0)
				{
					return TryCleanChatName(text2, out chatterName);
				}
			}
			return false;
		}

		private static bool IsChatNameKey(string key)
		{
			return string.Equals(key, "name", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "username", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "viewer", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "viewername", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "chatter", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "displayname", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "twitchname", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "user", StringComparison.OrdinalIgnoreCase);
		}

		private static bool TryCleanChatName(string value, out string chatterName)
		{
			chatterName = ((value != null) ? value.Trim().Trim('"', '\'') : string.Empty);
			chatterName = chatterName.Replace("\\\"", "\"").Replace("\\n", " ").Replace("\\r", " ")
				.Trim();
			if (chatterName.Length > 64)
			{
				chatterName = chatterName.Substring(0, 64);
			}
			if (chatterName.Length == 0 || string.Equals(chatterName, "undefined", StringComparison.OrdinalIgnoreCase) || string.Equals(chatterName, "null", StringComparison.OrdinalIgnoreCase) || string.Equals(chatterName, "none", StringComparison.OrdinalIgnoreCase))
			{
				chatterName = string.Empty;
				return false;
			}
			return true;
		}

		private static bool IsUsablePath(FileInfo file)
		{
			return file != null && !string.IsNullOrEmpty(file.FullName) && !string.Equals(file.Name, "null", StringComparison.OrdinalIgnoreCase);
		}

		private bool TryGetPlayerSpawnPoint(float distance, out Vector3 spawnPoint)
		{
			//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_004e: 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_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_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_00f5: 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_0100: 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_0103: 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_010c: 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)
			spawnPoint = Vector3.zero;
			if ((Object)(object)GM.CurrentPlayerBody == (Object)null || (Object)(object)GM.CurrentPlayerBody.Head == (Object)null)
			{
				SetStatus("Player body is not ready");
				return false;
			}
			Vector3 position = ((Component)GM.CurrentPlayerBody).transform.position;
			Vector3 forward = GM.CurrentPlayerBody.Head.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = ((Component)GM.CurrentPlayerBody).transform.forward;
			}
			((Vector3)(ref forward)).Normalize();
			float[] array = new float[8] { 0f, 45f, -45f, 90f, -90f, 135f, -135f, 180f };
			float[] array2 = new float[3] { 0f, 1.5f, 3f };
			for (int i = 0; i < array2.Length; i++)
			{
				float num = Mathf.Max(1.5f, distance + array2[i]);
				for (int j = 0; j < array.Length; j++)
				{
					Vector3 val = Quaternion.Euler(0f, array[j], 0f) * forward;
					Vector3 candidate = position + val * num;
					if (TryGetWalkableClearPoint(candidate, out spawnPoint))
					{
						return true;
					}
				}
			}
			SetStatus("Could not find clear walkable space near the player");
			return false;
		}

		private static bool TryGetWalkableClearPoint(Vector3 candidate, out Vector3 spawnPoint)
		{
			//IL_0001: 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_0016: 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_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)
			NavMeshHit val = default(NavMeshHit);
			if (NavMesh.SamplePosition(candidate, ref val, 4f, -1) && IsSpawnCapsuleClear(((NavMeshHit)(ref val)).position))
			{
				spawnPoint = ((NavMeshHit)(ref val)).position + Vector3.up * 0.05f;
				return true;
			}
			spawnPoint = Vector3.zero;
			return false;
		}

		private static bool IsSpawnCapsuleClear(Vector3 floorPoint)
		{
			//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_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_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_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_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_0034: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = floorPoint + Vector3.up * 0.45f;
			Vector3 val2 = floorPoint + Vector3.up * 1.65f;
			return !Physics.CheckCapsule(val, val2, 0.35f, LayerMask.op_Implicit(environmentMask));
		}

		private bool TryGetEnemySpawnPoint(out Vector3 spawnPoint, out int iff)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: 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_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)
			iff = fallbackEnemyIFF.Value;
			if ((Object)(object)GM.TNH_Manager != (Object)null)
			{
				TNHManager = GM.TNH_Manager;
			}
			if ((Object)(object)TNHManager != (Object)null && (Object)(object)TNHManager.m_curHoldPoint != (Object)null)
			{
				if ((int)TNHManager.Phase == 1 && TNHManager.m_curHoldPoint.AttackVectors != null && TNHManager.m_curHoldPoint.AttackVectors.Count > 0)
				{
					int index = Random.Range(0, TNHManager.m_curHoldPoint.AttackVectors.Count);
					AttackVector val = TNHManager.m_curHoldPoint.AttackVectors[index];
					if (val != null && val.SpawnPoints_Sosigs_Attack != null && val.SpawnPoints_Sosigs_Attack.Count > 0)
					{
						spawnPoint = val.SpawnPoints_Sosigs_Attack[Random.Range(0, val.SpawnPoints_Sosigs_Attack.Count)].position;
						iff = TNHManager.m_curHoldPoint.m_curPhase.IFFUsed;
						return true;
					}
				}
				if ((int)TNHManager.Phase == 0 && TNHManager.m_curHoldPoint.SpawnPoints_Turrets.Count > 0)
				{
					spawnPoint = ((Component)TNHManager.m_curHoldPoint.SpawnPoints_Turrets[0]).transform.position;
					if (TNHManager.m_curLevel != null && TNHManager.m_curLevel.PatrolChallenge.Patrols.Count > 0)
					{
						iff = TNHManager.m_curLevel.PatrolChallenge.Patrols[0].IFFUsed;
					}
					else
					{
						iff = TNHManager.m_curHoldPoint.m_curPhase.IFFUsed;
					}
					return true;
				}
			}
			return TryGetPlayerSpawnPoint(4f, out spawnPoint);
		}

		private bool CanUseSpawner()
		{
			if (TryResolveSpawnerPrefab())
			{
				return true;
			}
			SetStatus("Spawner prefab is not loaded; check the BepInEx log for the bundle paths that were tried");
			return false;
		}

		private bool CanSpawn(int livingCount, string teamName)
		{
			int num = Mathf.Max(0, maxLivingPerTeam.Value);
			if (num == 0 || livingCount < num)
			{
				return true;
			}
			SetStatus("Maximum " + teamName + " chatter count reached");
			return false;
		}

		private void UpdateSpawnedSosigs()
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Invalid comparison between Unknown and I4
			//IL_00aa: 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_00bc: Invalid comparison between Unknown and I4
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Invalid comparison between Unknown and I4
			//IL_00e4: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Invalid comparison between Unknown and I4
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Invalid comparison between Unknown and I4
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Invalid comparison between Unknown and I4
			//IL_0116: 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_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: 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)
			//IL_025f: Invalid comparison between Unknown and I4
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Invalid comparison between Unknown and I4
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Invalid comparison between Unknown and I4
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			PruneNullEntries(spawnedChatters);
			PruneNullEntries(spawnedEnemyChatters);
			PruneNullEntries(spawnedBosses);
			int livingCount = GetLivingCount(spawnedBosses);
			if (previousLivingBossCount > 0 && livingCount == 0)
			{
				SetStatus("The streamer defeated the chat boss");
			}
			previousLivingBossCount = livingCount;
			if ((Object)(object)GM.CurrentPlayerBody == (Object)null || (Object)(object)GM.CurrentPlayerBody.Head == (Object)null)
			{
				return;
			}
			for (int i = 0; i < spawnedChatters.Count; i++)
			{
				Sosig val = spawnedChatters[i];
				if ((int)val.BodyState == 3)
				{
					val.TickDownToClear(3f);
					continue;
				}
				bool flag = (int)val.CurrentOrder == 0 || (int)val.CurrentOrder == 9 || (int)val.CurrentOrder == 1 || (int)val.CurrentOrder == 4;
				bool flag2 = Vector3.Distance(GM.CurrentPlayerBody.Head.position, val.m_assaultPoint) > 4f;
				if (!val.m_isStunned && (flag || flag2))
				{
					Vector3 followPoint = GetFollowPoint();
					if (TryGetReachableWalkablePoint(((Component)val).transform.position, followPoint, out var destination))
					{
						val.CommandAssaultPoint(destination);
						val.SetCurrentOrder((SosigOrder)7);
					}
				}
				if (val.Priority.HasFreshTarget() && (int)val.CurrentOrder == 3 && val.m_entityRecognition >= 0.65f)
				{
					val.SetCurrentOrder((SosigOrder)2);
				}
				RecoverStuckSosig(val, isFriendly: true);
			}
			for (int j = 0; j < spawnedEnemyChatters.Count; j++)
			{
				Sosig val2 = spawnedEnemyChatters[j];
				if ((int)val2.BodyState == 3)
				{
					val2.TickDownToClear(3f);
					continue;
				}
				if (!val2.m_isStunned && val2.Links != null && val2.Links.Count > 1 && Vector3.Distance(GM.CurrentPlayerBody.Head.position, ((Component)val2.Links[1]).transform.position) > 20f)
				{
					CommandSosigToReachablePoint(val2, ((Component)GM.CurrentPlayerBody).transform.position);
				}
				if (val2.Priority.HasFreshTarget() && (int)val2.CurrentOrder == 3 && val2.m_entityRecognition >= 0.55f)
				{
					val2.SetCurrentOrder((SosigOrder)2);
				}
				if ((int)val2.CurrentOrder == 0 || (int)val2.CurrentOrder == 9 || (int)val2.CurrentOrder == 1)
				{
					CommandSosigToReachablePoint(val2, ((Component)GM.CurrentPlayerBody).transform.position);
				}
				RecoverStuckSosig(val2, isFriendly: false);
			}
			PruneMovementStates();
		}

		private static bool TryGetReachableWalkablePoint(Vector3 from, Vector3 desired, out Vector3 destination)
		{
			//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_000c: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//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_005a: 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)
			destination = Vector3.zero;
			NavMeshHit val = default(NavMeshHit);
			NavMeshHit val2 = default(NavMeshHit);
			if (!NavMesh.SamplePosition(from, ref val, 2.5f, -1) || !NavMesh.SamplePosition(desired, ref val2, 3f, -1))
			{
				return false;
			}
			NavMeshPath val3 = new NavMeshPath();
			if (!NavMesh.CalculatePath(((NavMeshHit)(ref val)).position, ((NavMeshHit)(ref val2)).position, -1, val3) || (int)val3.status != 0)
			{
				return false;
			}
			destination = ((NavMeshHit)(ref val2)).position;
			return true;
		}

		private static bool CommandSosigToReachablePoint(Sosig sosig, Vector3 desired)
		{
			//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_0034: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sosig == (Object)null)
			{
				return false;
			}
			if (!TryGetReachableWalkablePoint(((Component)sosig).transform.position, desired, out var destination))
			{
				return false;
			}
			sosig.CommandAssaultPoint(destination);
			sosig.SetCurrentOrder((SosigOrder)7);
			return true;
		}

		private void RecoverStuckSosig(Sosig sosig, bool isFriendly)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0064: 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_004a: 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_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_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_010f: 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_0110: 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_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: 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_012c: 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)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: 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_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: 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_01b2: 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)
			if ((Object)(object)sosig == (Object)null || sosig.m_isStunned || (int)sosig.BodyState == 3)
			{
				return;
			}
			if (!movementStates.TryGetValue(sosig, out var value))
			{
				movementStates[sosig] = new SosigMovementState(((Component)sosig).transform.position, Time.unscaledTime);
				return;
			}
			if (Vector3.Distance(value.LastPosition, ((Component)sosig).transform.position) >= 0.3f)
			{
				value.LastPosition = ((Component)sosig).transform.position;
				value.LastMovementTime = Time.unscaledTime;
				return;
			}
			Vector3 position = ((Component)GM.CurrentPlayerBody).transform.position;
			float num = Vector3.Distance(((Component)sosig).transform.position, position);
			float num2 = ((!isFriendly) ? 3f : 4f);
			if (num <= num2 || Time.unscaledTime - value.LastMovementTime < 3f)
			{
				return;
			}
			Vector3 desired = ((!isFriendly) ? position : GetFollowPoint());
			if (!CommandSosigToReachablePoint(sosig, desired))
			{
				Vector3 val = ((Component)sosig).transform.position - position;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
				{
					val = GM.CurrentPlayerBody.Head.forward;
				}
				((Vector3)(ref val)).Normalize();
				float num3 = ((!(Random.value < 0.5f)) ? 1f : (-1f));
				desired = position + Quaternion.Euler(0f, 55f * num3, 0f) * val * num2;
				CommandSosigToReachablePoint(sosig, desired);
			}
			value.LastPosition = ((Component)sosig).transform.position;
			value.LastMovementTime = Time.unscaledTime;
		}

		private void PruneMovementStates()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			List<Sosig> list = null;
			foreach (KeyValuePair<Sosig, SosigMovementState> movementState in movementStates)
			{
				if (!((Object)(object)movementState.Key != (Object)null) || (int)movementState.Key.BodyState == 3)
				{
					if (list == null)
					{
						list = new List<Sosig>();
					}
					list.Add(movementState.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					movementStates.Remove(list[i]);
				}
			}
		}

		private void ProtectStreamerFromFriendlyFire()
		{
			//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_004e: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Invalid comparison between Unknown and I4
			//IL_00f6: 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)
			if ((Object)(object)GM.CurrentPlayerBody == (Object)null || (Object)(object)GM.CurrentPlayerBody.Head == (Object)null)
			{
				return;
			}
			PruneNullEntries(spawnedChatters);
			Vector3 playerPoint = GM.CurrentPlayerBody.Head.position - Vector3.up * 0.25f;
			float radius = Mathf.Clamp(streamerSafetyRadius.Value, 0.25f, 2f);
			for (int i = 0; i < spawnedChatters.Count; i++)
			{
				Sosig val = spawnedChatters[i];
				if ((Object)(object)val == (Object)null || (int)val.BodyState == 3)
				{
					continue;
				}
				int playerIFF = GM.CurrentPlayerBody.GetPlayerIFF();
				if (val.GetIFF() != playerIFF)
				{
					val.SetIFF(playerIFF);
					val.SetOriginalIFFTeam(playerIFF);
					if ((Object)(object)val.E != (Object)null)
					{
						val.E.IFFCode = playerIFF;
					}
				}
				val.DoesAggroOnFriendlyFire = false;
				if (!IsPlayerInFriendlyFiringLane(val, playerPoint, radius))
				{
					blockedFriendlySince.Remove(val);
					continue;
				}
				DelayHeldGuns(val, playerIFF);
				if (!blockedFriendlySince.TryGetValue(val, out var value))
				{
					blockedFriendlySince[val] = Time.unscaledTime;
				}
				else if (Time.unscaledTime - value > 1f)
				{
					val.CommandAssaultPoint(GetSafeRepositionPoint(val));
					blockedFriendlySince[val] = Time.unscaledTime;
				}
			}
		}

		private static bool IsPlayerInFriendlyFiringLane(Sosig friendly, Vector3 playerPoint, float radius)
		{
			//IL_0050: 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_0076: 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_007c: 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_00a4: 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_00c5: 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_00ce: 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_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_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_00db: 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_0109: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			if (friendly.Hands == null)
			{
				return false;
			}
			for (int i = 0; i < friendly.Hands.Count; i++)
			{
				SosigHand val = friendly.Hands[i];
				if (val == null || !val.IsHoldingObject || (Object)(object)val.HeldObject == (Object)null || (int)val.HeldObject.Type != 0)
				{
					continue;
				}
				Vector3 position = ((Component)val.HeldObject).transform.position;
				Vector3 val2 = friendly.m_aimPoint - position;
				if (((Vector3)(ref val2)).sqrMagnitude < 1f)
				{
					val2 = ((Component)val.HeldObject).transform.forward * 30f;
				}
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (magnitude < 0.1f)
				{
					continue;
				}
				Vector3 val3 = val2 / magnitude;
				Vector3 val4 = playerPoint - position;
				float num = Vector3.Dot(val4, val3);
				if (!(num <= 0.05f) && !(num > magnitude + 1f))
				{
					Vector3 val5 = position + val3 * n