Decompiled source of LostSurvivor v1.0.2

plugins/LostSurvivor.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.CharacterAI;
using RoR2.ExpansionManagement;
using RoR2.Navigation;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LostSurvivor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LostSurvivor")]
[assembly: AssemblyTitle("LostSurvivor")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LostSurvivor
{
	internal static class LostSurvivorConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> SpawnChance;

		public static ConfigEntry<int> MaxPerStage;

		public static ConfigEntry<float> SpawnDelay;

		public static ConfigEntry<float> MinDistanceFromPlayers;

		public static ConfigEntry<bool> AnnounceSpawn;

		public static ConfigEntry<string> SurvivorBlacklist;

		public static ConfigEntry<int> ItemsPerStage;

		public static ConfigEntry<bool> RandomizeItemCount;

		public static ConfigEntry<float> WhiteWeight;

		public static ConfigEntry<float> GreenWeight;

		public static ConfigEntry<float> RedWeight;

		public static ConfigEntry<float> BossWeight;

		public static ConfigEntry<bool> BankKillGold;

		public static ConfigEntry<int> CopiesPerStageCleared;

		public static ConfigEntry<bool> GiveMinionLeash;

		public static ConfigEntry<bool> AnnounceBefriend;

		private static string _blacklistSource;

		private static HashSet<string> _blacklist = new HashSet<string>();

		public static HashSet<string> Blacklist
		{
			get
			{
				if (_blacklistSource != SurvivorBlacklist.Value)
				{
					_blacklistSource = SurvivorBlacklist.Value;
					_blacklist = new HashSet<string>(from s in (_blacklistSource ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
						select s.Trim() into s
						where s.Length > 0
						select s, StringComparer.OrdinalIgnoreCase);
				}
				return _blacklist;
			}
		}

		public static void Init(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("1. General", "Enabled", true, "Master switch. When false, no Lost Survivors spawn. Survivors you have already befriended stay with you.");
			SpawnChance = cfg.Bind<float>("1. General", "Spawn Chance", 15f, "Percent chance (0-100) that a Lost Survivor appears when you enter a stage.\n0 = never, 100 = every stage. Rolled once per spawn slot (see Max Per Stage).");
			MaxPerStage = cfg.Bind<int>("1. General", "Max Per Stage", 2, "How many times the spawn chance is rolled per stage, i.e. the most Lost Survivors that can appear on one stage.\nExample: 2 with a 50% chance -> 25% none, 50% one, 25% two.");
			SpawnDelay = cfg.Bind<float>("1. General", "Spawn Delay", 4f, "Seconds after the stage starts before the Lost Survivor is placed.\nA short delay lets players finish spawning so the distance check below works.");
			MinDistanceFromPlayers = cfg.Bind<float>("1. General", "Min Distance From Players", 60f, "The Lost Survivor spawns on a random ground node at least this many meters from every player,\nso you have to go find it. Falls back to any valid node if the map is too small.");
			AnnounceSpawn = cfg.Bind<bool>("1. General", "Announce Spawn", true, "Post a chat message when a Lost Survivor appears (it does NOT say where).");
			SurvivorBlacklist = cfg.Bind<string>("1. General", "Survivor Blacklist", "", "Comma-separated BODY names that should never appear, e.g. \"MercBody, CaptainBody\".\nVanilla body names: CommandoBody, HuntressBody, Bandit2Body, ToolbotBody, EngiBody, MageBody, MercBody,\nTreebotBody, LoaderBody, CrocoBody, CaptainBody, RailgunnerBody, VoidSurvivorBody, SeekerBody,\nFalseSonBody, ChefBody. The exact names in YOUR game are printed to the BepInEx log at the start of each run\n(line \"Lost Survivor pool: ...\"). Modded survivors are included only if they ship an AI master.");
			ItemsPerStage = cfg.Bind<int>("2. Wild Items", "Items Per Stage", 4, "Item cap scales with the stage you are on: max items = Items Per Stage x current stage number.\nWith 4: stage 1 -> up to 4, stage 2 -> up to 8, stage 3 -> up to 12, and so on (loops keep counting).");
			RandomizeItemCount = cfg.Bind<bool>("2. Wild Items", "Randomize Item Count", true, "true  = the Lost Survivor gets a random amount from 1 up to the max above.\nfalse = it always gets exactly the max.");
			WhiteWeight = cfg.Bind<float>("2. Wild Items", "White Weight", 76f, "Relative odds that each rolled item is Common (white).");
			GreenWeight = cfg.Bind<float>("2. Wild Items", "Green Weight", 16f, "Relative odds that each rolled item is Uncommon (green).");
			RedWeight = cfg.Bind<float>("2. Wild Items", "Red Weight", 4f, "Relative odds that each rolled item is Legendary (red).");
			BossWeight = cfg.Bind<float>("2. Wild Items", "Boss Weight", 4f, "Relative odds that each rolled item is Boss (yellow). 0 disables.\nWeights are relative: 76/16/4/4 means 76%, 16%, 4%, 4%. Items tagged AI Blacklist are always skipped.");
			BankKillGold = cfg.Bind<bool>("3. Companion", "Bank Kill Gold", true, "true  = gold from enemies the wild Lost Survivor kills is held by it instead of going to players.\n        Whoever befriends it receives the whole stash.\nfalse = its kills pay out to players normally and there is no stash.");
			CopiesPerStageCleared = cfg.Bind<int>("3. Companion", "Copies Per Stage Cleared", 3, "When a befriended survivor follows you into a new stage, it copies random items from your inventory.\nCopies gained = this value x stages completed. With 3: entering stage 3 (2 cleared) grants 6 copies.\nItems you hold more of are more likely to be picked. Set 0 to disable.");
			GiveMinionLeash = cfg.Bind<bool>("3. Companion", "Leash To Owner", true, "Gives befriended survivors the hidden drone leash, so they teleport back to you if they fall far behind.");
			AnnounceBefriend = cfg.Bind<bool>("3. Companion", "Announce Befriend", true, "Post a chat message when someone befriends a Lost Survivor, including how much gold it handed over.");
		}
	}
	internal class LostSurvivorInteractable : MonoBehaviour, IInteractable, IDisplayNameProvider
	{
		private CharacterBody body;

		private GameObject interactTrigger;

		public static void TryAttach(CharacterBody body)
		{
			//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_002f: Invalid comparison between Unknown and I4
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			if (Object.op_Implicit((Object)(object)body) && !body.isPlayerControlled)
			{
				TeamIndex val = (TeamIndex)((!Object.op_Implicit((Object)(object)body.teamComponent)) ? (-1) : ((int)body.teamComponent.teamIndex));
				if (((int)val == 1 || (int)val == -1) && (int)SurvivorCatalog.GetSurvivorIndexFromBodyIndex(body.bodyIndex) != -1 && (!NetworkServer.active || LostSurvivorTag.IsWild(body.master)) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<LostSurvivorInteractable>()))
				{
					((Component)body).gameObject.AddComponent<LostSurvivorInteractable>();
				}
			}
		}

		private void Awake()
		{
			body = ((Component)this).GetComponent<CharacterBody>();
		}

		private void Start()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			interactTrigger = new GameObject("LostSurvivorInteractTrigger");
			interactTrigger.layer = LayerIndex.pickups.intVal;
			interactTrigger.transform.SetParent(((Component)this).transform, false);
			interactTrigger.transform.localPosition = (Object.op_Implicit((Object)(object)body) ? ((Component)this).transform.InverseTransformPoint(body.corePosition) : Vector3.zero);
			SphereCollider obj = interactTrigger.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = Mathf.Max(2f, Object.op_Implicit((Object)(object)body) ? (body.bestFitRadius * 1.25f) : 2f);
			interactTrigger.AddComponent<EntityLocator>().entity = ((Component)this).gameObject;
		}

		private void FixedUpdate()
		{
			CharacterMaster val = (Object.op_Implicit((Object)(object)body) ? body.master : null);
			if (Object.op_Implicit((Object)(object)val) && (Object.op_Implicit((Object)(object)val.playerCharacterMasterController) || (Object.op_Implicit((Object)(object)val.minionOwnership) && Object.op_Implicit((Object)(object)val.minionOwnership.ownerMaster))))
			{
				Object.Destroy((Object)(object)this);
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)interactTrigger))
			{
				Object.Destroy((Object)(object)interactTrigger);
			}
		}

		private bool IsWild()
		{
			if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.healthComponent) || !body.healthComponent.alive)
			{
				return false;
			}
			CharacterMaster master = body.master;
			if (!Object.op_Implicit((Object)(object)master))
			{
				return false;
			}
			if (NetworkServer.active)
			{
				return LostSurvivorTag.IsWild(master);
			}
			if (!Object.op_Implicit((Object)(object)master.playerCharacterMasterController) && Object.op_Implicit((Object)(object)master.minionOwnership))
			{
				return !Object.op_Implicit((Object)(object)master.minionOwnership.ownerMaster);
			}
			return false;
		}

		public string GetContextString(Interactor activator)
		{
			uint num = ((Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master)) ? body.master.money : 0u);
			string text = (Object.op_Implicit((Object)(object)body) ? body.GetDisplayName() : "Survivor");
			if (num == 0)
			{
				return "Befriend " + text;
			}
			return $"Befriend {text} <color=#FFE880>(${num})</color>";
		}

		public Interactability GetInteractability(Interactor activator)
		{
			if (IsWild())
			{
				CharacterBody val = (Object.op_Implicit((Object)(object)activator) ? ((Component)activator).GetComponent<CharacterBody>() : null);
				if (Object.op_Implicit((Object)(object)val) && val.isPlayerControlled)
				{
					return (Interactability)2;
				}
				return (Interactability)0;
			}
			return (Interactability)0;
		}

		public void OnInteractionBegin(Interactor activator)
		{
			LostSurvivorPlugin.Befriend(body, activator);
		}

		public bool ShouldIgnoreSpherecastForInteractibility(Interactor activator)
		{
			return false;
		}

		public bool ShouldShowOnScanner()
		{
			return false;
		}

		public bool ShouldProximityHighlight()
		{
			return false;
		}

		public int GetInteractablePriority(Interactor activator)
		{
			return 0;
		}

		public bool ShouldSpawnLesserInteractionRewards(Interactor interactor)
		{
			return false;
		}

		public string GetDisplayName()
		{
			if (!Object.op_Implicit((Object)(object)body))
			{
				return "Lost Survivor";
			}
			return body.GetDisplayName();
		}
	}
	internal class LostSurvivorPersistence : MonoBehaviour
	{
		private CharacterMaster master;

		private float nextCheck;

		private void Awake()
		{
			master = ((Component)this).GetComponent<CharacterMaster>();
		}

		private void FixedUpdate()
		{
			if ((nextCheck -= Time.fixedDeltaTime) > 0f)
			{
				return;
			}
			nextCheck = 0.5f;
			if (!Object.op_Implicit((Object)(object)master))
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			CharacterMaster val = (Object.op_Implicit((Object)(object)master.minionOwnership) ? master.minionOwnership.ownerMaster : null);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.playerCharacterMasterController))
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)((Component)this).GetComponent<MasterSuicideOnTimer>()) && (!Object.op_Implicit((Object)(object)master.inventory) || master.inventory.GetItemCountPermanent(Items.GummyCloneIdentifier) <= 0))
			{
				if (!Util.IsDontDestroyOnLoad(((Component)this).gameObject))
				{
					Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				}
				if (NetworkServer.active && !Object.op_Implicit((Object)(object)((Component)this).GetComponent<LostSurvivorTag>()))
				{
					LostSurvivorTag lostSurvivorTag = ((Component)this).gameObject.AddComponent<LostSurvivorTag>();
					lostSurvivorTag.befriended = true;
					lostSurvivorTag.lastCopyClearCount = (Object.op_Implicit((Object)(object)Run.instance) ? Run.instance.stageClearCount : 0);
					lostSurvivorTag.RequestRegroup();
					if (LostSurvivorConfig.GiveMinionLeash.Value && Object.op_Implicit((Object)(object)master.inventory) && master.inventory.GetItemCountPermanent(Items.MinionLeash) == 0)
					{
						master.inventory.GiveItemPermanent(Items.MinionLeash, 1);
					}
				}
			}
			((Behaviour)this).enabled = false;
		}
	}
	[BepInPlugin("com.lostsurvivor.LostSurvivor", "LostSurvivor", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class LostSurvivorPlugin : BaseUnityPlugin
	{
		public const string GUID = "com.lostsurvivor.LostSurvivor";

		public const string NAME = "LostSurvivor";

		public const string VERSION = "1.0.1";

		private const string RiskOfOptionsGuid = "com.rune580.riskofoptions";

		internal static ManualLogSource Log;

		internal static LostSurvivorPlugin Instance;

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LostSurvivorConfig.Init(((BaseUnityPlugin)this).Config);
			if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
			{
				RiskOfOptionsCompat.Register();
			}
			new Harmony("com.lostsurvivor.LostSurvivor").PatchAll(typeof(GoldBankPatch));
			Stage.onServerStageBegin += OnServerStageBegin;
			CharacterBody.onBodyStartGlobal += LostSurvivorInteractable.TryAttach;
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(PrepareSurvivorMasterPrefabs));
			Run.onRunStartGlobal += delegate
			{
				Log.LogInfo((object)("Lost Survivor pool: " + string.Join(", ", from s in SurvivorPool()
					select BodyCatalog.GetBodyName(BodyCatalog.FindBodyIndex(s.bodyPrefab)))));
			};
		}

		private static void PrepareSurvivorMasterPrefabs()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (SurvivorDef allSurvivorDef in SurvivorCatalog.allSurvivorDefs)
			{
				if (!Object.op_Implicit((Object)(object)allSurvivorDef) || !Object.op_Implicit((Object)(object)allSurvivorDef.bodyPrefab))
				{
					continue;
				}
				BodyIndex val = BodyCatalog.FindBodyIndex(allSurvivorDef.bodyPrefab);
				if ((int)val == -1)
				{
					continue;
				}
				MasterIndex val2 = MasterCatalog.FindAiMasterIndexForBody(val);
				if (!((MasterIndex)(ref val2)).isValid)
				{
					continue;
				}
				GameObject masterPrefab = MasterCatalog.GetMasterPrefab(val2);
				if (Object.op_Implicit((Object)(object)masterPrefab))
				{
					if (!Object.op_Implicit((Object)(object)masterPrefab.GetComponent<AIOwnership>()))
					{
						masterPrefab.AddComponent<AIOwnership>();
					}
					if (!Object.op_Implicit((Object)(object)masterPrefab.GetComponent<LostSurvivorPersistence>()))
					{
						masterPrefab.AddComponent<LostSurvivorPersistence>();
					}
					num++;
				}
			}
			Log.LogInfo((object)$"Prepared {num} survivor AI masters.");
		}

		private void OnServerStageBegin(Stage stage)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			if (NetworkServer.active && Object.op_Implicit((Object)(object)Run.instance))
			{
				LostSurvivorTag.GrantStageCopies();
				if (LostSurvivorConfig.Enabled.Value && Object.op_Implicit((Object)(object)stage.sceneDef) && (int)stage.sceneDef.sceneType == 1)
				{
					((MonoBehaviour)this).StartCoroutine(SpawnRoutine(stage));
				}
			}
		}

		private IEnumerator SpawnRoutine(Stage stage)
		{
			yield return (object)new WaitForSeconds(Mathf.Max(0f, LostSurvivorConfig.SpawnDelay.Value));
			if (!NetworkServer.active || (Object)(object)Stage.instance != (Object)(object)stage || !Object.op_Implicit((Object)(object)Run.instance))
			{
				yield break;
			}
			for (int i = 0; i < Mathf.Max(0, LostSurvivorConfig.MaxPerStage.Value); i++)
			{
				if (Random.Range(0f, 100f) < LostSurvivorConfig.SpawnChance.Value)
				{
					TrySpawnLostSurvivor();
				}
			}
		}

		internal static List<SurvivorDef> SurvivorPool()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			List<SurvivorDef> list = new List<SurvivorDef>();
			foreach (SurvivorDef orderedSurvivorDef in SurvivorCatalog.orderedSurvivorDefs)
			{
				if (!Object.op_Implicit((Object)(object)orderedSurvivorDef) || orderedSurvivorDef.hidden || !Object.op_Implicit((Object)(object)orderedSurvivorDef.bodyPrefab))
				{
					continue;
				}
				BodyIndex val = BodyCatalog.FindBodyIndex(orderedSurvivorDef.bodyPrefab);
				if ((int)val == -1 || LostSurvivorConfig.Blacklist.Contains(BodyCatalog.GetBodyName(val)))
				{
					continue;
				}
				ExpansionDef requiredExpansion = orderedSurvivorDef.GetRequiredExpansion();
				if (!Object.op_Implicit((Object)(object)requiredExpansion) || !Object.op_Implicit((Object)(object)Run.instance) || Run.instance.IsExpansionEnabled(requiredExpansion))
				{
					MasterIndex val2 = MasterCatalog.FindAiMasterIndexForBody(val);
					if (((MasterIndex)(ref val2)).isValid)
					{
						list.Add(orderedSurvivorDef);
					}
				}
			}
			return list;
		}

		private static void TrySpawnLostSurvivor()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			List<SurvivorDef> list = SurvivorPool();
			if (list.Count == 0)
			{
				Log.LogWarning((object)"No eligible survivors to spawn.");
				return;
			}
			SurvivorDef val = list[Random.Range(0, list.Count)];
			GameObject masterPrefab = MasterCatalog.GetMasterPrefab(MasterCatalog.FindAiMasterIndexForBody(BodyCatalog.FindBodyIndex(val.bodyPrefab)));
			if (!TryFindSpawnPosition(val.bodyPrefab.GetComponent<CharacterBody>().hullClassification, out var position))
			{
				Log.LogWarning((object)("Couldn't find a spawn node for " + val.cachedName));
				return;
			}
			CharacterMaster val2 = new MasterSummon
			{
				masterPrefab = masterPrefab,
				position = position,
				rotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f),
				teamIndexOverride = (TeamIndex)1,
				ignoreTeamMemberLimit = true,
				preSpawnSetupCallback = delegate(CharacterMaster m)
				{
					((Component)m).gameObject.AddComponent<LostSurvivorTag>();
					m.NetworkhideAllyCard = true;
					GiveWildItems(m.inventory);
				}
			}.Perform();
			if (Object.op_Implicit((Object)(object)val2))
			{
				Log.LogInfo((object)$"Spawned lost {val.cachedName} at {position} with {val2.inventory.GetTotalItemCount()} items.");
				if (LostSurvivorConfig.AnnounceSpawn.Value)
				{
					Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
					{
						baseToken = "<color=#9FD8FF>A lost " + Language.GetString(val.displayNameToken) + " is wandering this stage...</color>"
					});
				}
			}
		}

		private static bool TryFindSpawnPosition(HullClassification hull, out Vector3 position)
		{
			//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_0037: 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_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_0111: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			NodeGraph val = (Object.op_Implicit((Object)(object)SceneInfo.instance) ? SceneInfo.instance.groundNodes : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			List<NodeIndex> activeNodesForHullMaskWithFlagConditions = val.GetActiveNodesForHullMaskWithFlagConditions((HullMask)(1 << (int)hull), (NodeFlags)0, (NodeFlags)4);
			if (activeNodesForHullMaskWithFlagConditions.Count == 0)
			{
				return false;
			}
			List<Vector3> source = (from val3 in PlayerCharacterMasterController.instances
				select (!Object.op_Implicit((Object)(object)val3.master)) ? null : val3.master.GetBodyObject() into b
				where Object.op_Implicit((Object)(object)b)
				select b.transform.position).ToList();
			float minSqr = LostSurvivorConfig.MinDistanceFromPlayers.Value;
			minSqr *= minSqr;
			Vector3 p = default(Vector3);
			for (int num = 0; num < 64; num++)
			{
				NodeIndex val2 = activeNodesForHullMaskWithFlagConditions[Random.Range(0, activeNodesForHullMaskWithFlagConditions.Count)];
				if (val.GetNodePosition(val2, ref p) && source.All(delegate(Vector3 pp)
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					//IL_0002: Unknown result type (might be due to invalid IL or missing references)
					//IL_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)
					Vector3 val3 = pp - p;
					return ((Vector3)(ref val3)).sqrMagnitude >= minSqr;
				}))
				{
					position = p;
					return true;
				}
			}
			return val.GetNodePosition(activeNodesForHullMaskWithFlagConditions[Random.Range(0, activeNodesForHullMaskWithFlagConditions.Count)], ref position);
		}

		private static void GiveWildItems(Inventory inv)
		{
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			int num = Run.instance.stageClearCount + 1;
			int num2 = Mathf.Max(0, LostSurvivorConfig.ItemsPerStage.Value) * num;
			if (num2 <= 0)
			{
				return;
			}
			int num3 = (LostSurvivorConfig.RandomizeItemCount.Value ? Random.Range(1, num2 + 1) : num2);
			List<(List<ItemIndex>, float)> list = new List<(List<ItemIndex>, float)>
			{
				(Filter(Run.instance.availableTier1DropList), LostSurvivorConfig.WhiteWeight.Value),
				(Filter(Run.instance.availableTier2DropList), LostSurvivorConfig.GreenWeight.Value),
				(Filter(Run.instance.availableTier3DropList), LostSurvivorConfig.RedWeight.Value),
				(Filter(Run.instance.availableBossDropList), LostSurvivorConfig.BossWeight.Value)
			}.Where<(List<ItemIndex>, float)>(((List<ItemIndex> items, float weight) t) => t.items.Count > 0 && t.weight > 0f).ToList();
			float num4 = list.Sum<(List<ItemIndex>, float)>(((List<ItemIndex> items, float weight) t) => t.weight);
			if (num4 <= 0f)
			{
				return;
			}
			for (int num5 = 0; num5 < num3; num5++)
			{
				float num6 = Random.Range(0f, num4);
				foreach (var (list2, num7) in list)
				{
					if (!((num6 -= num7) > 0f))
					{
						inv.GiveItemPermanent(list2[Random.Range(0, list2.Count)], 1);
						break;
					}
				}
			}
		}

		private static List<ItemIndex> Filter(List<PickupIndex> pickups)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			List<ItemIndex> list = new List<ItemIndex>();
			if (pickups == null)
			{
				return list;
			}
			foreach (PickupIndex pickup in pickups)
			{
				PickupDef pickupDef = PickupCatalog.GetPickupDef(pickup);
				if (pickupDef != null && (int)pickupDef.itemIndex != -1 && IsAiUsable(ItemCatalog.GetItemDef(pickupDef.itemIndex)))
				{
					list.Add(pickupDef.itemIndex);
				}
			}
			return list;
		}

		internal static bool IsAiUsable(ItemDef def)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			if (Object.op_Implicit((Object)(object)def) && !def.hidden && (int)def.tier != 5 && !def.ContainsTag((ItemTag)4) && !def.ContainsTag((ItemTag)13) && !def.ContainsTag((ItemTag)9))
			{
				return !def.ContainsTag((ItemTag)10);
			}
			return false;
		}

		internal static void Befriend(CharacterBody body, Interactor activator)
		{
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			if (!NetworkServer.active || !Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.master))
			{
				return;
			}
			CharacterMaster master = body.master;
			LostSurvivorTag component = ((Component)master).GetComponent<LostSurvivorTag>();
			if (!Object.op_Implicit((Object)(object)component) || component.befriended)
			{
				return;
			}
			CharacterBody val = (Object.op_Implicit((Object)(object)activator) ? ((Component)activator).GetComponent<CharacterBody>() : null);
			CharacterMaster val2 = (Object.op_Implicit((Object)(object)val) ? val.master : null);
			if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.playerCharacterMasterController))
			{
				component.befriended = true;
				master.NetworkhideAllyCard = false;
				component.lastCopyClearCount = Run.instance.stageClearCount;
				uint money = master.money;
				master.money = 0u;
				if (money != 0)
				{
					val2.GiveMoney(money);
				}
				master.minionOwnership.SetOwner(val2);
				Object.DontDestroyOnLoad((Object)(object)((Component)master).gameObject);
				BaseAI component2 = ((Component)master).GetComponent<BaseAI>();
				(((Component)master).GetComponent<AIOwnership>() ?? ((Component)master).gameObject.AddComponent<AIOwnership>()).ownerMaster = val2;
				if (Object.op_Implicit((Object)(object)component2))
				{
					component2.leader.gameObject = ((Component)val).gameObject;
				}
				if (LostSurvivorConfig.GiveMinionLeash.Value && master.inventory.GetItemCountPermanent(Items.MinionLeash) == 0)
				{
					master.inventory.GiveItemPermanent(Items.MinionLeash, 1);
				}
				if (LostSurvivorConfig.AnnounceBefriend.Value)
				{
					SimpleChatMessage val3 = new SimpleChatMessage();
					val3.baseToken = "<color=#9FFFA8>" + Util.GetBestMasterName(val2) + " befriended the lost " + body.GetDisplayName() + ((money != 0) ? $" and received <color=#FFE880>${money}</color>!" : "!") + "</color>";
					Chat.SendBroadcastChat((ChatMessageBase)(object)val3);
				}
			}
		}
	}
	internal class LostSurvivorTag : MonoBehaviour
	{
		public bool befriended;

		public int lastCopyClearCount;

		public bool needsRegroup;

		private float regroupTimeout;

		private static readonly List<LostSurvivorTag> all = new List<LostSurvivorTag>();

		public void RequestRegroup()
		{
			needsRegroup = true;
			regroupTimeout = 30f;
		}

		private void FixedUpdate()
		{
			//IL_00c0: 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_00dc: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if (!needsRegroup || !NetworkServer.active)
			{
				return;
			}
			if ((regroupTimeout -= Time.fixedDeltaTime) <= 0f)
			{
				needsRegroup = false;
				return;
			}
			CharacterMaster component = ((Component)this).GetComponent<CharacterMaster>();
			CharacterBody val = (Object.op_Implicit((Object)(object)component) ? component.GetBody() : null);
			CharacterMaster val2 = ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.minionOwnership)) ? component.minionOwnership.ownerMaster : null);
			CharacterBody val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetBody() : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val3) && !Object.op_Implicit((Object)(object)val3.currentVehicle) && (!Object.op_Implicit((Object)(object)val3.characterMotor) || val3.characterMotor.isGrounded))
			{
				Vector3 val4 = (Vector3)(((??)TeleportHelper.FindSafeTeleportDestination(val3.footPosition, val, new Xoroshiro128Plus((ulong)Random.Range(1, int.MaxValue)))) ?? val3.footPosition);
				TeleportHelper.TeleportBody(val, val4, false);
				needsRegroup = false;
			}
		}

		private void Awake()
		{
			all.Add(this);
		}

		private void OnDestroy()
		{
			all.Remove(this);
		}

		public static bool IsWild(CharacterMaster m)
		{
			if (!Object.op_Implicit((Object)(object)m))
			{
				return false;
			}
			LostSurvivorTag component = ((Component)m).GetComponent<LostSurvivorTag>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return !component.befriended;
			}
			return false;
		}

		public static void GrantStageCopies()
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_0105: 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)
			int stageClearCount = Run.instance.stageClearCount;
			int num = Mathf.Max(0, LostSurvivorConfig.CopiesPerStageCleared.Value);
			LostSurvivorTag[] array = all.ToArray();
			foreach (LostSurvivorTag lostSurvivorTag in array)
			{
				if (!Object.op_Implicit((Object)(object)lostSurvivorTag) || !lostSurvivorTag.befriended || lostSurvivorTag.lastCopyClearCount >= stageClearCount)
				{
					continue;
				}
				lostSurvivorTag.lastCopyClearCount = stageClearCount;
				lostSurvivorTag.RequestRegroup();
				CharacterMaster component = ((Component)lostSurvivorTag).GetComponent<CharacterMaster>();
				CharacterMaster val = ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.minionOwnership)) ? component.minionOwnership.ownerMaster : null);
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.inventory) || num == 0)
				{
					continue;
				}
				List<ItemIndex> list = new List<ItemIndex>();
				foreach (ItemIndex item in val.inventory.itemAcquisitionOrder)
				{
					if (LostSurvivorPlugin.IsAiUsable(ItemCatalog.GetItemDef(item)))
					{
						int itemCountPermanent = val.inventory.GetItemCountPermanent(item);
						for (int j = 0; j < itemCountPermanent; j++)
						{
							list.Add(item);
						}
					}
				}
				if (list.Count != 0)
				{
					int num2 = num * stageClearCount;
					for (int k = 0; k < num2; k++)
					{
						component.inventory.GiveItemPermanent(list[Random.Range(0, list.Count)], 1);
					}
					LostSurvivorPlugin.Log.LogInfo((object)$"{((Object)component).name} copied {num2} items from {((Object)val).name}.");
				}
			}
		}
	}
	[HarmonyPatch(typeof(DeathRewards), "OnKilledServer")]
	internal static class GoldBankPatch
	{
		[HarmonyPrefix]
		private static void Prefix(DeathRewards __instance, DamageReport damageReport, out uint __state)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Invalid comparison between Unknown and I4
			__state = 0u;
			if (!LostSurvivorConfig.BankKillGold.Value || damageReport == null)
			{
				return;
			}
			CharacterMaster val = null;
			if (LostSurvivorTag.IsWild(damageReport.attackerMaster))
			{
				val = damageReport.attackerMaster;
			}
			else if (LostSurvivorTag.IsWild(damageReport.attackerOwnerMaster))
			{
				val = damageReport.attackerOwnerMaster;
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				__state = __instance.goldReward;
				if (__state != 0)
				{
					uint num = (((int)Run.instance.selectedDifficulty >= 8) ? ((uint)((float)__state * 0.8f)) : __state);
					CharacterMaster obj = val;
					obj.money += num;
					__instance.goldReward = 0u;
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(DeathRewards __instance, uint __state)
		{
			if (__state != 0)
			{
				__instance.goldReward = __state;
			}
		}
	}
	internal static class RiskOfOptionsCompat
	{
		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void Register()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_011e: 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_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Sometimes a lost survivor is wandering the stage. Find it, press Interact to befriend it, and take the gold it collected.");
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.Enabled));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.SpawnChance, new StepSliderConfig
			{
				min = 0f,
				max = 100f,
				increment = 1f,
				FormatString = "{0:0}%"
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LostSurvivorConfig.MaxPerStage, new IntSliderConfig
			{
				min = 1,
				max = 5
			}));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.SpawnDelay, new StepSliderConfig
			{
				min = 0f,
				max = 30f,
				increment = 0.5f,
				FormatString = "{0:0.0}s"
			}));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.MinDistanceFromPlayers, new StepSliderConfig
			{
				min = 0f,
				max = 250f,
				increment = 5f,
				FormatString = "{0:0}m"
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.AnnounceSpawn));
			ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(LostSurvivorConfig.SurvivorBlacklist));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LostSurvivorConfig.ItemsPerStage, new IntSliderConfig
			{
				min = 0,
				max = 20
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.RandomizeItemCount));
			StepSliderConfig val = new StepSliderConfig
			{
				min = 0f,
				max = 100f,
				increment = 1f,
				FormatString = "{0:0}"
			};
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.WhiteWeight, val));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.GreenWeight, val));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.RedWeight, val));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(LostSurvivorConfig.BossWeight, val));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.BankKillGold));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LostSurvivorConfig.CopiesPerStageCleared, new IntSliderConfig
			{
				min = 0,
				max = 10
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.GiveMinionLeash));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LostSurvivorConfig.AnnounceBefriend));
		}
	}
}