Decompiled source of PetrichorProtocol v2.16.0

PetrichorProtocol.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HG.BlendableTypes;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Projectile;
using On.RoR2.UI;
using On.RoR2.UI.MainMenu;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Projectile;
using RoR2.UI;
using RoR2.UI.MainMenu;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[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("PetrichorProtocol")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.16.0.0")]
[assembly: AssemblyInformationalVersion("2.16.0+615702a4273eef4ba1c49317a3516c3ccfedacf9")]
[assembly: AssemblyProduct("PetrichorProtocol")]
[assembly: AssemblyTitle("PetrichorProtocol")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.16.0.0")]
[module: UnverifiableCode]
[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 PetrichorProtocol
{
	internal class ChallengeContractsModule : ModuleBase
	{
		public enum Contract
		{
			None,
			KillTheTitan,
			NoHealing,
			SpeedClear,
			EliteHunt,
			StayInside,
			PacifistStart
		}

		public ConfigEntry<float> OfferChance;

		public ConfigEntry<float> RewardGoldMultiplier;

		public ConfigEntry<float> SpeedClearSeconds;

		public ConfigEntry<int> EliteHuntCount;

		public ConfigEntry<float> NoHealingCap;

		private Contract active;

		private bool resolved;

		private float stageStartTime;

		private float healedThisStage;

		private int elitesKilledThisStage;

		private bool titanKilled;

		private bool pacifistBroken;

		private CharacterMaster titanMaster;

		private static readonly Random rng = new Random();

		public override string Name => "Challenge Contracts";

		public override string StandaloneGuid => null;

		public override int Stars => 2;

		public override bool UsesStageStart => true;

		public override bool UsesRunStart => true;

		public override bool UsesDeath => true;

		public override bool UsesHeal => true;

		public override bool UsesReward => true;

		public override bool UsesFixedUpdate => true;

		public override void Bind(ConfigFile cfg)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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
			Enabled = cfg.Bind<bool>("Contracts", "Enabled", false, "Each stage offers one optional objective (a contract). Complete it for a bonus reward; ignoring it costs nothing. A goals-and-rewards layer - off by default.");
			OfferChance = cfg.Bind<float>("Contracts", "OfferChancePerStage", 100f, new ConfigDescription("Chance (percent) that a stage offers a contract at all.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			RewardGoldMultiplier = cfg.Bind<float>("Contracts", "RewardGoldMultiplier", 0.5f, new ConfigDescription("Bonus gold on completing a contract, as a fraction of your current gold (0.5 = +50%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			SpeedClearSeconds = cfg.Bind<float>("Contracts", "SpeedClearSeconds", 120f, new ConfigDescription("For the Speed Clear contract: seconds allowed to activate the teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), Array.Empty<object>()));
			EliteHuntCount = cfg.Bind<int>("Contracts", "EliteHuntCount", 3, new ConfigDescription("For the Elite Hunt contract: how many elites to kill this stage.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 15), Array.Empty<object>()));
			NoHealingCap = cfg.Bind<float>("Contracts", "NoHealingCap", 50f, new ConfigDescription("For the No Healing contract: total healing allowed this stage before it fails.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
		}

		public override void OnRunStart()
		{
			active = Contract.None;
		}

		public override void OnStageStart(Stage stage)
		{
			active = Contract.None;
			resolved = false;
			healedThisStage = 0f;
			elitesKilledThisStage = 0;
			titanKilled = false;
			pacifistBroken = false;
			titanMaster = null;
			stageStartTime = Time.time;
			if (!NetworkServer.active)
			{
				return;
			}
			if (rng.NextDouble() * 100.0 > (double)OfferChance.Value)
			{
				DebugLog($"No contract offered this stage (OfferChance={OfferChance.Value}%)");
				return;
			}
			active = (Contract)(1 + rng.Next(6));
			DebugLog($"Rolled contract: {active}");
			if (active == Contract.KillTheTitan && !TrySpawnTitan())
			{
				DebugLog("KillTheTitan could not find a spawn candidate - falling back to EliteHunt");
				active = Contract.EliteHunt;
			}
			DebugLog($"Offered {active} contract - {Describe(active)}");
			if (NotificationsModule.Gate)
			{
				Chat.AddMessage("<color=#4dd2ff>CONTRACT:</color> " + Describe(active));
			}
		}

		private string Describe(Contract c)
		{
			return c switch
			{
				Contract.KillTheTitan => "Kill the Titan before the teleporter finishes charging.", 
				Contract.NoHealing => $"Heal no more than {NoHealingCap.Value:0} this stage.", 
				Contract.SpeedClear => $"Activate the teleporter within {SpeedClearSeconds.Value:0} seconds.", 
				Contract.EliteHunt => $"Kill {EliteHuntCount.Value} elites this stage.", 
				Contract.StayInside => "Charge the teleporter without leaving the zone.", 
				Contract.PacifistStart => "Do not kill anything for the first 60 seconds.", 
				_ => "", 
			};
		}

		public override float ModifyHeal(HealthComponent target, float amount)
		{
			if (active == Contract.NoHealing && !resolved && Object.op_Implicit((Object)(object)target.body) && ModuleBase.IsPlayer(target.body))
			{
				healedThisStage += amount;
				if (healedThisStage > NoHealingCap.Value)
				{
					DebugLog($"NoHealing contract broken - healed {healedThisStage:0.0}/{NoHealingCap.Value:0} this stage");
					Fail();
				}
			}
			return amount;
		}

		public override void OnDeath(DamageReport report)
		{
			if (active == Contract.None || resolved || report == null)
			{
				return;
			}
			if (active == Contract.PacifistStart && Object.op_Implicit((Object)(object)report.attackerBody) && ModuleBase.IsPlayer(report.attackerBody) && Time.time - stageStartTime < 60f)
			{
				DebugLog($"PacifistStart contract broken - kill at {Time.time - stageStartTime:0.0}s into stage (limit 60s)");
				pacifistBroken = true;
				Fail();
				return;
			}
			if (active == Contract.EliteHunt && Object.op_Implicit((Object)(object)report.victimBody) && report.victimBody.isElite && Object.op_Implicit((Object)(object)report.attackerBody) && ModuleBase.IsPlayer(report.attackerBody))
			{
				elitesKilledThisStage++;
				DebugLog($"EliteHunt progress: {elitesKilledThisStage}/{EliteHuntCount.Value} elites killed");
				if (elitesKilledThisStage >= EliteHuntCount.Value)
				{
					Succeed();
				}
			}
			if (active == Contract.KillTheTitan && (Object)(object)titanMaster != (Object)null && Object.op_Implicit((Object)(object)report.victimBody) && (Object)(object)report.victimBody.master == (Object)(object)titanMaster)
			{
				DebugLog("KillTheTitan contract target killed");
				titanKilled = true;
				Succeed();
			}
		}

		public override void OnFixedUpdateServer()
		{
			if (active == Contract.None || resolved)
			{
				return;
			}
			TeleporterInteraction instance = TeleporterInteraction.instance;
			if (active == Contract.SpeedClear)
			{
				if (Object.op_Implicit((Object)(object)instance) && instance.isCharged)
				{
					DebugLog($"SpeedClear contract succeeded - teleporter charged at {Time.time - stageStartTime:0.0}s (limit {SpeedClearSeconds.Value:0}s)");
					Succeed();
				}
				else if (Time.time - stageStartTime > SpeedClearSeconds.Value)
				{
					DebugLog($"SpeedClear contract failed - {SpeedClearSeconds.Value:0}s elapsed without charging teleporter");
					Fail();
				}
			}
			if (active == Contract.StayInside && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.holdoutZoneController) && ((Behaviour)instance.holdoutZoneController).isActiveAndEnabled && instance.holdoutZoneController.charge > 0f && instance.holdoutZoneController.charge < 1f)
			{
				bool flag = false;
				foreach (PlayerCharacterMasterController instance2 in PlayerCharacterMasterController.instances)
				{
					CharacterBody val = ((Object.op_Implicit((Object)(object)instance2) && Object.op_Implicit((Object)(object)instance2.master)) ? instance2.master.GetBody() : null);
					if (Object.op_Implicit((Object)(object)val) && instance.holdoutZoneController.IsBodyInChargingRadius(val))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					DebugLog($"StayInside contract failed - no player inside charging radius at {instance.holdoutZoneController.charge * 100f:0}% charge");
					Fail();
				}
				else if (instance.isCharged)
				{
					DebugLog("StayInside contract succeeded - teleporter fully charged with a player inside the zone throughout");
					Succeed();
				}
			}
			if (active == Contract.PacifistStart && !pacifistBroken && Time.time - stageStartTime >= 60f)
			{
				DebugLog("PacifistStart contract succeeded - 60s elapsed with no kills");
				Succeed();
			}
			if (active == Contract.KillTheTitan && !titanKilled && Object.op_Implicit((Object)(object)instance) && instance.isCharged)
			{
				DebugLog("KillTheTitan contract failed - teleporter charged before the titan was killed");
				Fail();
			}
		}

		private void Succeed()
		{
			if (!resolved)
			{
				resolved = true;
				DebugLog($"{active} contract resolved: SUCCESS - granting reward (RewardGoldMultiplier={RewardGoldMultiplier.Value})");
				GrantReward();
				if (NotificationsModule.Gate)
				{
					Chat.AddMessage("<color=#7fff9d>CONTRACT COMPLETE!</color> Reward granted.");
				}
			}
		}

		private void Fail()
		{
			if (!resolved)
			{
				resolved = true;
				DebugLog($"{active} contract resolved: FAILED - no penalty applied");
				if (NotificationsModule.Gate)
				{
					Chat.AddMessage("<color=#ff7a7a>Contract failed.</color> No penalty - better luck next stage.");
				}
			}
		}

		private void GrantReward()
		{
			foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
			{
				if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.master))
				{
					uint money = instance.master.money;
					uint num = (uint)Mathf.Max(25f, (float)instance.master.money * RewardGoldMultiplier.Value);
					instance.master.GiveMoney(num);
					DebugLog($"Granted {num} bonus gold to {((Object)instance.master).name} (had {money}, now {instance.master.money})");
				}
			}
		}

		public override float RewardMultiplier(DamageReport report)
		{
			if (active == Contract.None || resolved)
			{
				return 1f;
			}
			return 1.1f;
		}

		private bool TrySpawnTitan()
		{
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			List<CharacterBody> list = new List<CharacterBody>();
			foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList)
			{
				if (Object.op_Implicit((Object)(object)readOnlyInstances) && ModuleBase.IsHostileEnemy(readOnlyInstances) && !readOnlyInstances.isBoss && Object.op_Implicit((Object)(object)readOnlyInstances.healthComponent) && readOnlyInstances.healthComponent.alive)
				{
					list.Add(readOnlyInstances);
				}
			}
			if (list.Count == 0)
			{
				DebugLog("TrySpawnTitan found no eligible non-boss hostile enemy on the stage");
				return false;
			}
			list.Sort((CharacterBody a, CharacterBody b) => b.maxHealth.CompareTo(a.maxHealth));
			CharacterBody val = list[rng.Next(Mathf.Min(3, list.Count))];
			titanMaster = val.master;
			DebugLog($"TrySpawnTitan chose {((Object)val).name} (base maxHealth {val.maxHealth:0}) from a pool of {list.Count} candidates");
			ModelLocator modelLocator = val.modelLocator;
			if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform))
			{
				Transform transform = ((Component)modelLocator.modelTransform).gameObject.transform;
				transform.localScale *= 3f;
			}
			if ((Object)(object)val.inventory != (Object)null)
			{
				EliteDef fire = Elites.Fire;
				if (Object.op_Implicit((Object)(object)fire) && Object.op_Implicit((Object)(object)fire.eliteEquipmentDef))
				{
					val.inventory.SetEquipmentIndex(fire.eliteEquipmentDef.equipmentIndex, false);
				}
				val.inventory.GiveItemPermanent(Items.BoostHp, 120);
				val.inventory.GiveItemPermanent(Items.BoostDamage, 30);
			}
			val.RecalculateStats();
			if (Object.op_Implicit((Object)(object)val.healthComponent))
			{
				val.healthComponent.HealFraction(1f, default(ProcChainMask));
			}
			DebugLog($"Titan ready: {((Object)val).name} scaled 3x, Fire elite affix, +120 BoostHp, +30 BoostDamage, final maxHealth {val.maxHealth:0}");
			return (Object)(object)titanMaster != (Object)null;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("dileppy.petrichorprotocol", "PetrichorProtocol", "2.16.0")]
	public class PetrichorProtocolPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "dileppy.petrichorprotocol";

		public const string PluginName = "PetrichorProtocol";

		public const string PluginVersion = "2.16.0";

		internal static PetrichorProtocolPlugin Instance;

		internal static ManualLogSource Log;

		internal static readonly List<ModuleBase> Modules = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookBodyStart = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookRecalc = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookProjectile = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookBullet = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookSpawnEffect = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookReward = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookDeath = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookRunStart = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookStageStart = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookHeal = new List<ModuleBase>();

		private static readonly List<ModuleBase> hookFixed = new List<ModuleBase>();

		private static readonly List<(string name, int stars)> externalRegistered = new List<(string, int)>();

		public static ConfigEntry<bool> EnableRewardScaling;

		public static ConfigEntry<float> MaxRewardMultiplier;

		public static ConfigEntry<bool> ShowRunSummary;

		public static ConfigEntry<bool> DebugLogging;

		private static int riskScore;

		private static float protocolRewardMult = 1f;

		private static bool spawnEffectHooked;

		private static void BuildHookLists()
		{
			hookBodyStart.Clear();
			hookRecalc.Clear();
			hookProjectile.Clear();
			hookBullet.Clear();
			hookSpawnEffect.Clear();
			hookReward.Clear();
			hookDeath.Clear();
			hookRunStart.Clear();
			hookStageStart.Clear();
			hookHeal.Clear();
			hookFixed.Clear();
			foreach (ModuleBase module in Modules)
			{
				if (module.Active)
				{
					if (module.UsesBodyStart)
					{
						hookBodyStart.Add(module);
					}
					if (module.UsesRecalcStats)
					{
						hookRecalc.Add(module);
					}
					if (module.UsesProjectile)
					{
						hookProjectile.Add(module);
					}
					if (module.UsesBulletFire)
					{
						hookBullet.Add(module);
					}
					if (module.UsesSpawnEffect)
					{
						hookSpawnEffect.Add(module);
					}
					if (module.UsesReward)
					{
						hookReward.Add(module);
					}
					if (module.UsesDeath)
					{
						hookDeath.Add(module);
					}
					if (module.UsesRunStart)
					{
						hookRunStart.Add(module);
					}
					if (module.UsesStageStart)
					{
						hookStageStart.Add(module);
					}
					if (module.UsesHeal)
					{
						hookHeal.Add(module);
					}
					if (module.UsesFixedUpdate)
					{
						hookFixed.Add(module);
					}
				}
			}
		}

		public static void RegisterModifier(string displayName, int stars)
		{
			externalRegistered.Add((displayName, Mathf.Clamp(stars, 1, 5)));
		}

		private void Awake()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Expected O, but got Unknown
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Expected O, but got Unknown
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Expected O, but got Unknown
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Expected O, but got Unknown
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Expected O, but got Unknown
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			EnableRewardScaling = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "EnableRewardScaling", true, "Scale gold and XP up based on the total Risk Score of active features.");
			MaxRewardMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Protocol", "MaxRewardMultiplier", 3f, new ConfigDescription("The highest the gold/XP reward multiplier can reach, no matter how high your Risk Score climbs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
			ShowRunSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "ShowRunSummary", true, "Announce the Risk Score, tier, and active features in chat at run start.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "DebugLogging", false, "Write extra Protocol diagnostics to the console log. Only useful for troubleshooting; leave off normally.");
			Modules.Add(new ShrunkenSurvivorModule());
			Modules.Add(new EnlargedSurvivorModule());
			Modules.Add(new ShrunkenEnemiesModule());
			Modules.Add(new EnlargedEnemiesModule());
			Modules.Add(new BiggerBulletsModule());
			Modules.Add(new EnemyMutationsModule());
			Modules.Add(new BossMutationsModule());
			Modules.Add(new PresetsModule());
			Modules.Add(new CombosModule());
			Modules.Add(new CurseDeckModule());
			Modules.Add(new HazardsModule());
			Modules.Add(new WorldEventsModule());
			Modules.Add(new StagePersonalitiesModule());
			Modules.Add(new TeleporterRulesModule());
			Modules.Add(new EnemySwarmModule());
			Modules.Add(new ItemDietModule());
			Modules.Add(new LootMutationsModule());
			Modules.Add(new SizeRouletteModule());
			Modules.Add(new ChallengeContractsModule());
			Modules.Add(new RandomizerModule());
			Modules.Add(new NotificationsModule());
			Modules.Add(new RunTitlesModule());
			Modules.Add(new HudModule());
			Modules.Add(new TitleBrandingModule());
			foreach (ModuleBase module in Modules)
			{
				module.Bind(((BaseUnityPlugin)this).Config);
			}
			foreach (ModuleBase module2 in Modules)
			{
				if (module2.Enabled != null)
				{
					module2.Enabled.SettingChanged += delegate
					{
						BuildHookLists();
						RefreshDynamicHooks();
						RefreshRiskLine();
					};
				}
			}
			BuildHookLists();
			CharacterBody.Start += new hook_Start(CharacterBody_Start);
			CharacterBody.RecalculateStats += new hook_RecalculateStats(CharacterBody_RecalculateStats);
			ProjectileController.Start += new hook_Start(ProjectileController_Start);
			BulletAttack.Fire += new hook_Fire(BulletAttack_Fire);
			DeathRewards.OnKilledServer += new hook_OnKilledServer(DeathRewards_OnKilledServer);
			GlobalEventManager.onCharacterDeathGlobal += OnCharacterDeath;
			Run.onRunStartGlobal += OnRunStart;
			Stage.onStageStartGlobal += OnStageStart;
			HealthComponent.Heal += new hook_Heal(HealthComponent_Heal);
			RefreshDynamicHooks();
			((Component)this).gameObject.AddComponent<DebugMenuController>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("The Petrichor Protocol v{0} online. {1} modules loaded.", "2.16.0", Modules.Count));
		}

		private void Start()
		{
			foreach (ModuleBase module in Modules)
			{
				module.StandaloneAlsoInstalled = module.StandaloneGuid != null && Chainloader.PluginInfos.ContainsKey(module.StandaloneGuid);
				if (module.StandaloneAlsoInstalled)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Protocol: standalone " + module.Name + " is also installed. Protocol settings take precedence; the standalone mod is dormant."));
				}
			}
			RiskOfOptionsBridge.TryRegisterAll(((BaseUnityPlugin)this).Config);
		}

		internal static void RefreshDynamicHooks()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			if ((Object)(object)Instance == (Object)null)
			{
				return;
			}
			bool flag = false;
			for (int i = 0; i < Modules.Count; i++)
			{
				if (Modules[i].WantsSpawnEffectHook)
				{
					flag = true;
					break;
				}
			}
			if (flag == spawnEffectHooked)
			{
				return;
			}
			try
			{
				if (flag)
				{
					EffectManager.SpawnEffect_GameObject_EffectData_bool += new hook_SpawnEffect_GameObject_EffectData_bool(Instance.EffectManager_SpawnEffect);
				}
				else
				{
					EffectManager.SpawnEffect_GameObject_EffectData_bool -= new hook_SpawnEffect_GameObject_EffectData_bool(Instance.EffectManager_SpawnEffect);
				}
				spawnEffectHooked = flag;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)("Protocol: SpawnEffect hook " + (flag ? "attached (tracer scaling on)" : "detached (not needed - zero cost)") + "."));
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)$"Protocol: SpawnEffect hook toggle failed: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			CharacterBody.Start -= new hook_Start(CharacterBody_Start);
			CharacterBody.RecalculateStats -= new hook_RecalculateStats(CharacterBody_RecalculateStats);
			ProjectileController.Start -= new hook_Start(ProjectileController_Start);
			BulletAttack.Fire -= new hook_Fire(BulletAttack_Fire);
			DeathRewards.OnKilledServer -= new hook_OnKilledServer(DeathRewards_OnKilledServer);
			GlobalEventManager.onCharacterDeathGlobal -= OnCharacterDeath;
			Run.onRunStartGlobal -= OnRunStart;
			Stage.onStageStartGlobal -= OnStageStart;
			HealthComponent.Heal -= new hook_Heal(HealthComponent_Heal);
			if (spawnEffectHooked)
			{
				EffectManager.SpawnEffect_GameObject_EffectData_bool -= new hook_SpawnEffect_GameObject_EffectData_bool(EffectManager_SpawnEffect);
				spawnEffectHooked = false;
			}
			foreach (ModuleBase module in Modules)
			{
				try
				{
					module.Unhook();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"{module.Name}.Unhook: {arg}");
				}
			}
		}

		private void CharacterBody_Start(orig_Start orig, CharacterBody self)
		{
			orig.Invoke(self);
			for (int i = 0; i < hookBodyStart.Count; i++)
			{
				ModuleBase moduleBase = hookBodyStart[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnBodyStart(self);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnBodyStart: {arg}");
					}
				}
			}
		}

		private void CharacterBody_RecalculateStats(orig_RecalculateStats orig, CharacterBody self)
		{
			orig.Invoke(self);
			for (int i = 0; i < hookRecalc.Count; i++)
			{
				ModuleBase moduleBase = hookRecalc[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnRecalcStats(self);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnRecalcStats: {arg}");
					}
				}
			}
		}

		private void ProjectileController_Start(orig_Start orig, ProjectileController self)
		{
			orig.Invoke(self);
			for (int i = 0; i < hookProjectile.Count; i++)
			{
				ModuleBase moduleBase = hookProjectile[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnProjectileStart(self);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnProjectileStart: {arg}");
					}
				}
			}
		}

		private void BulletAttack_Fire(orig_Fire orig, BulletAttack self)
		{
			bool flag = false;
			for (int i = 0; i < hookBullet.Count; i++)
			{
				if (flag)
				{
					break;
				}
				ModuleBase moduleBase = hookBullet[i];
				if (moduleBase.Active)
				{
					try
					{
						flag = moduleBase.OnBulletFire(self, orig);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnBulletFire: {arg}");
					}
				}
			}
			if (!flag)
			{
				orig.Invoke(self);
			}
		}

		private void EffectManager_SpawnEffect(orig_SpawnEffect_GameObject_EffectData_bool orig, GameObject effectPrefab, EffectData effectData, bool transmit)
		{
			for (int i = 0; i < hookSpawnEffect.Count; i++)
			{
				ModuleBase moduleBase = hookSpawnEffect[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnSpawnEffect(effectPrefab, effectData);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnSpawnEffect: {arg}");
					}
				}
			}
			orig.Invoke(effectPrefab, effectData, transmit);
		}

		private void DeathRewards_OnKilledServer(orig_OnKilledServer orig, DeathRewards self, DamageReport report)
		{
			float num = 1f;
			for (int i = 0; i < hookReward.Count; i++)
			{
				ModuleBase moduleBase = hookReward[i];
				if (moduleBase.Active)
				{
					try
					{
						num *= moduleBase.RewardMultiplier(report);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.RewardMultiplier: {arg}");
					}
				}
			}
			if (EnableRewardScaling.Value)
			{
				num *= protocolRewardMult;
			}
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				num = 1f;
			}
			num = Mathf.Clamp(num, 0f, 1000f);
			if (num != 1f)
			{
				self.goldReward = (uint)((float)self.goldReward * num);
				self.expReward = (uint)((float)self.expReward * num);
			}
			orig.Invoke(self, report);
		}

		private void OnCharacterDeath(DamageReport report)
		{
			for (int i = 0; i < hookDeath.Count; i++)
			{
				ModuleBase moduleBase = hookDeath[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnDeath(report);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnDeath: {arg}");
					}
				}
			}
		}

		private void OnStageStart(Stage stage)
		{
			for (int i = 0; i < hookStageStart.Count; i++)
			{
				ModuleBase moduleBase = hookStageStart[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnStageStart(stage);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnStageStart: {arg}");
					}
				}
			}
		}

		private void FixedUpdate()
		{
			if (hookFixed.Count == 0 || !NetworkServer.active || (Object)(object)Run.instance == (Object)null)
			{
				return;
			}
			for (int i = 0; i < hookFixed.Count; i++)
			{
				ModuleBase moduleBase = hookFixed[i];
				if (moduleBase.Active)
				{
					try
					{
						moduleBase.OnFixedUpdateServer();
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.OnFixedUpdateServer: {arg}");
					}
				}
			}
		}

		private float HealthComponent_Heal(orig_Heal orig, HealthComponent self, float amount, ProcChainMask procChainMask, bool nonRegen)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < hookHeal.Count; i++)
			{
				ModuleBase moduleBase = hookHeal[i];
				if (moduleBase.Active)
				{
					try
					{
						amount = moduleBase.ModifyHeal(self, amount);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{moduleBase.Name}.ModifyHeal: {arg}");
					}
				}
			}
			return orig.Invoke(self, amount, procChainMask, nonRegen);
		}

		internal static string TierName(int score)
		{
			if (score <= 0)
			{
				return "Vanilla";
			}
			if (score <= 5)
			{
				return "Casual Chaos";
			}
			if (score <= 10)
			{
				return "Risky";
			}
			if (score <= 15)
			{
				return "Brutal";
			}
			if (score <= 20)
			{
				return "Nightmare";
			}
			return "Apocalypse";
		}

		private void OnRunStart(Run run)
		{
			foreach (ModuleBase module in Modules)
			{
				if (module.Active)
				{
					try
					{
						module.OnRunStart();
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{module.Name}.OnRunStart: {arg}");
					}
				}
			}
			BuildHookLists();
			RefreshDynamicHooks();
			List<(string, int)> list = RefreshRiskLine();
			if (!ShowRunSummary.Value || list.Count == 0 || !NotificationsModule.Gate)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<color=#7fdbca>THE PETRICHOR PROTOCOL</color> - Risk Score <color=#ffd700>").Append(riskScore).Append("</color>");
			stringBuilder.Append(" - Tier: <color=#ff6b6b>").Append(TierName(riskScore)).Append("</color>");
			if (EnableRewardScaling.Value)
			{
				stringBuilder.Append(" - Rewards <color=#ffd700>x").Append(protocolRewardMult.ToString("F2")).Append("</color>");
			}
			Chat.AddMessage(stringBuilder.ToString());
			StringBuilder stringBuilder2 = new StringBuilder("Active: ");
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder2.Append(", ");
				}
				stringBuilder2.Append(list[i].Item1).Append(" (").Append(new string('*', list[i].Item2))
					.Append(")");
			}
			Chat.AddMessage(stringBuilder2.ToString());
		}

		private List<(string, int)> RefreshRiskLine()
		{
			riskScore = 0;
			List<(string, int)> list = new List<(string, int)>();
			foreach (ModuleBase module in Modules)
			{
				if (module.Stars > 0 && module.FeatureActive)
				{
					riskScore += module.Stars;
					list.Add((module.Name + (module.StandaloneAlsoInstalled ? " (protocol-controlled)" : ""), module.Stars));
				}
			}
			foreach (var (item, num) in externalRegistered)
			{
				riskScore += num;
				list.Add((item, num));
			}
			protocolRewardMult = ((riskScore <= 5) ? (1f + (float)riskScore * 0.02f) : (1f + 0.004f * (float)riskScore * (float)riskScore));
			protocolRewardMult = Mathf.Min(protocolRewardMult, MaxRewardMultiplier.Value);
			HudModule.RiskLine = $"<color=#ffd700>Risk {riskScore}</color> - <color=#ff6b6b>{TierName(riskScore)}</color>";
			return list;
		}
	}
	internal abstract class ModuleBase
	{
		internal class SizeMarker : MonoBehaviour
		{
			public Vector3 baseScale;
		}

		internal class AimOriginMarker : MonoBehaviour
		{
			public Vector3 baseAimLocalPos;

			public Vector3 modelPivotToBodyRoot;
		}

		public ConfigEntry<bool> Enabled;

		public bool StandaloneAlsoInstalled;

		public abstract string Name { get; }

		public abstract string StandaloneGuid { get; }

		public abstract int Stars { get; }

		public bool Active
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		public bool FeatureActive => Active;

		public virtual bool UsesBodyStart => false;

		public virtual bool UsesRecalcStats => false;

		public virtual bool UsesProjectile => false;

		public virtual bool UsesBulletFire => false;

		public virtual bool UsesSpawnEffect => false;

		public virtual bool UsesReward => false;

		public virtual bool UsesDeath => false;

		public virtual bool UsesRunStart => false;

		public virtual bool UsesStageStart => false;

		public virtual bool UsesHeal => false;

		public virtual bool UsesFixedUpdate => false;

		public virtual bool WantsSpawnEffectHook => false;

		public abstract void Bind(ConfigFile cfg);

		protected void DebugLog(string message)
		{
			if (PetrichorProtocolPlugin.DebugLogging != null && PetrichorProtocolPlugin.DebugLogging.Value)
			{
				PetrichorProtocolPlugin.Log.LogInfo((object)("[" + Name + "] " + message));
			}
		}

		public virtual void OnBodyStart(CharacterBody body)
		{
		}

		public virtual void OnRecalcStats(CharacterBody body)
		{
		}

		public virtual void OnProjectileStart(ProjectileController proj)
		{
		}

		public virtual bool OnBulletFire(BulletAttack attack, orig_Fire orig)
		{
			return false;
		}

		public virtual void OnSpawnEffect(GameObject prefab, EffectData data)
		{
		}

		public virtual float RewardMultiplier(DamageReport report)
		{
			return 1f;
		}

		public virtual void OnDeath(DamageReport report)
		{
		}

		public virtual void OnRunStart()
		{
		}

		public virtual void OnStageStart(Stage stage)
		{
		}

		public virtual float ModifyHeal(HealthComponent target, float amount)
		{
			return amount;
		}

		public virtual void OnFixedUpdateServer()
		{
		}

		public virtual void Unhook()
		{
		}

		protected static bool IsPlayer(CharacterBody body)
		{
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master))
			{
				return Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController);
			}
			return false;
		}

		protected static bool IsHostileEnemy(CharacterBody body)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.teamComponent))
			{
				return false;
			}
			TeamIndex teamIndex = body.teamComponent.teamIndex;
			if ((int)teamIndex != 2 && (int)teamIndex != 3)
			{
				return (int)teamIndex == 4;
			}
			return true;
		}

		protected static void ApplyAbsoluteScale(GameObject go, float factor)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)go))
			{
				return;
			}
			Scene scene = go.scene;
			if (((Scene)(ref scene)).IsValid())
			{
				SizeMarker sizeMarker = go.GetComponent<SizeMarker>();
				if (!Object.op_Implicit((Object)(object)sizeMarker))
				{
					sizeMarker = go.AddComponent<SizeMarker>();
					sizeMarker.baseScale = go.transform.localScale;
				}
				go.transform.localScale = sizeMarker.baseScale * factor;
			}
		}

		private static void ApplyAimOriginScale(CharacterBody body, Transform modelTransform, float factor)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0082: 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_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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.aimOriginTransform) && Object.op_Implicit((Object)(object)modelTransform))
			{
				AimOriginMarker aimOriginMarker = ((Component)body).gameObject.GetComponent<AimOriginMarker>();
				if (!Object.op_Implicit((Object)(object)aimOriginMarker))
				{
					aimOriginMarker = ((Component)body).gameObject.AddComponent<AimOriginMarker>();
					aimOriginMarker.baseAimLocalPos = body.aimOriginTransform.localPosition;
					aimOriginMarker.modelPivotToBodyRoot = body.transform.position - modelTransform.position;
				}
				Vector3 val = (aimOriginMarker.modelPivotToBodyRoot + aimOriginMarker.baseAimLocalPos) * factor;
				body.aimOriginTransform.localPosition = val - aimOriginMarker.modelPivotToBodyRoot;
			}
		}

		protected static void ApplyModelScale(CharacterBody body, float factor)
		{
			ModelLocator modelLocator = body.modelLocator;
			if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform))
			{
				ApplyAbsoluteScale(((Component)modelLocator.modelTransform).gameObject, factor);
				if (IsPlayer(body))
				{
					ApplyAimOriginScale(body, modelLocator.modelTransform, factor);
				}
			}
		}
	}
	internal static class RiskOfOptionsBridge
	{
		private static readonly HashSet<string> KeepEnabledSections = new HashSet<string> { "HUD", "Notifications", "RunTitles", "Protocol" };

		private static string CategoryFor(string section)
		{
			switch (section)
			{
			case "Presets":
				return "0. Presets (Start Here)";
			case "EnlargedSurvivor":
			case "ShrunkenSurvivor":
			case "EnlargedEnemies":
			case "ShrunkenEnemies":
			case "BiggerBullets":
			case "SizeRoulette":
				return "1. Size & Scale";
			case "BossMutations":
			case "EnemyMutations":
				return "2. Mutations";
			case "Hazards":
			case "TeleporterRules":
			case "CurseDeck":
			case "EnemySwarm":
			case "WorldEvents":
			case "StagePersonalities":
				return "3. Environment & Events";
			case "LootMutations":
			case "ItemDiet":
				return "4. Items & Loot";
			case "Contracts":
			case "Randomizer":
			case "Combos":
				return "5. Randomizer, Combos & Contracts";
			case "TitleBranding":
			case "Notifications":
			case "RunTitles":
			case "HUD":
				return "6. Interface & Feedback";
			default:
				return "7. Protocol Core";
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void TryRegisterAll(ConfigFile cfg)
		{
			if (!Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
			{
				return;
			}
			try
			{
				RegisterAll(cfg);
			}
			catch (Exception ex)
			{
				PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol: Risk of Options registration failed (" + ex.Message + "). Config remains file-based."));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void RegisterAll(ConfigFile cfg)
		{
			int num = 0;
			List<ConfigEntryBase> list = new List<ConfigEntryBase>(((IDictionary<ConfigDefinition, ConfigEntryBase>)cfg).Values);
			list.Sort(delegate(ConfigEntryBase a, ConfigEntryBase b)
			{
				int num2 = string.CompareOrdinal(CategoryFor(a.Definition.Section), CategoryFor(b.Definition.Section));
				if (num2 != 0)
				{
					return num2;
				}
				int num3 = string.CompareOrdinal(a.Definition.Section, b.Definition.Section);
				if (num3 != 0)
				{
					return num3;
				}
				bool flag = a.Definition.Key == "Enabled";
				bool flag2 = b.Definition.Key == "Enabled";
				return (flag != flag2) ? ((!flag) ? 1 : (-1)) : string.CompareOrdinal(a.Definition.Key, b.Definition.Key);
			});
			foreach (ConfigEntryBase item in list)
			{
				if (!(item.Definition.Section == "DebugMenu") && TryAddOption(item))
				{
					num++;
				}
			}
			TryAddResetButton(cfg);
			TryAddPresetButtons();
			PetrichorProtocolPlugin.Log.LogInfo((object)$"Protocol: {num} settings registered with Risk of Options, bucketed into categories.");
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void TryAddPresetButtons()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			PresetsModule presets = null;
			foreach (ModuleBase module in PetrichorProtocolPlugin.Modules)
			{
				if (module is PresetsModule presetsModule)
				{
					presets = presetsModule;
					break;
				}
			}
			if (presets == null)
			{
				return;
			}
			foreach (PresetsModule.Preset value in Enum.GetValues(typeof(PresetsModule.Preset)))
			{
				if (value == PresetsModule.Preset.None)
				{
					continue;
				}
				PresetsModule.Preset captured = value;
				try
				{
					ModSettingsManager.AddOption((BaseOption)new GenericButtonOption(PresetsModule.Prettify(captured), "0. Presets (Start Here)", PresetsModule.Flavor(captured), "Apply", (UnityAction)delegate
					{
						presets.ApplyNow(captured);
					}));
				}
				catch (Exception ex)
				{
					PetrichorProtocolPlugin.Log.LogWarning((object)$"Protocol: could not add preset button for {captured} ({ex.Message}).");
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void TryAddResetButton(ConfigFile cfg)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			try
			{
				ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("Reset all settings to default", "7. Protocol Core", "Turn every gameplay feature off and restore all sliders and options to their original values, for a genuine clean slate. The HUD and notifications stay on. Use this to clear a complex setup and rebuild from scratch, enabling just the features you want.", "Reset", (UnityAction)delegate
				{
					ResetAllToDefault(cfg);
				}));
			}
			catch (Exception ex)
			{
				PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol: could not add reset button (" + ex.Message + ")."));
			}
		}

		internal static void ResetAllToDefault(ConfigFile cfg)
		{
			int num = 0;
			int num2 = 0;
			foreach (ConfigEntryBase item in new List<ConfigEntryBase>(((IDictionary<ConfigDefinition, ConfigEntryBase>)cfg).Values))
			{
				try
				{
					item.BoxedValue = item.DefaultValue;
					num++;
					if (item.Definition.Key == "Enabled" && item.SettingType == typeof(bool) && !KeepEnabledSections.Contains(item.Definition.Section))
					{
						item.BoxedValue = false;
						num2++;
					}
				}
				catch (Exception ex)
				{
					PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol reset: could not reset " + item.Definition.Section + "/" + item.Definition.Key + " (" + ex.Message + ")."));
				}
			}
			cfg.Save();
			PetrichorProtocolPlugin.Log.LogInfo((object)$"Protocol: reset {num} settings to default; {num2} gameplay features turned off.");
			try
			{
				Chat.AddMessage("<color=#7fdbca>THE PETRICHOR PROTOCOL</color> - reset complete. All gameplay features are now off - pick the ones you want.");
			}
			catch
			{
			}
		}

		private static string FriendlySection(string section)
		{
			switch (section)
			{
			case "HUD":
				return "HUD";
			case "Protocol":
				return "Protocol Core";
			case "Hazards":
				return "Environmental Hazards";
			default:
			{
				StringBuilder stringBuilder = new StringBuilder(section.Length + 4);
				for (int i = 0; i < section.Length; i++)
				{
					char c = section[i];
					if (i > 0 && char.IsUpper(c) && !char.IsUpper(section[i - 1]))
					{
						stringBuilder.Append(' ');
					}
					stringBuilder.Append(c);
				}
				return stringBuilder.ToString();
			}
			}
		}

		private static string FriendlyName(string section, string key)
		{
			string text = FriendlySection(section);
			if (key == "Enabled")
			{
				return text;
			}
			string text2 = SpaceCamel(key);
			return text + ": " + text2;
		}

		private static string SpaceCamel(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 4);
			for (int i = 0; i < s.Length; i++)
			{
				char c = s[i];
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(s[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool TryAddOption(ConfigEntryBase entry)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			//IL_00db: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			try
			{
				string section = entry.Definition.Section;
				string category = CategoryFor(section);
				string name = FriendlyName(section, entry.Definition.Key);
				Type settingType = entry.SettingType;
				if (settingType == typeof(bool))
				{
					ModSettingsManager.AddOption((BaseOption)new CheckBoxOption((ConfigEntry<bool>)(object)entry, new CheckBoxConfig
					{
						category = category,
						name = name
					}));
				}
				else if (settingType == typeof(float))
				{
					ModSettingsManager.AddOption((BaseOption)new SliderOption((ConfigEntry<float>)(object)entry, new SliderConfig
					{
						category = category,
						name = name
					}));
				}
				else if (settingType == typeof(int))
				{
					ModSettingsManager.AddOption((BaseOption)new IntSliderOption((ConfigEntry<int>)(object)entry, new IntSliderConfig
					{
						category = category,
						name = name
					}));
				}
				else
				{
					if (!settingType.IsEnum)
					{
						return false;
					}
					ModSettingsManager.AddOption((BaseOption)new ChoiceOption(entry, new ChoiceConfig
					{
						category = category,
						name = name
					}));
				}
				return true;
			}
			catch
			{
				return false;
			}
		}
	}
	internal class DebugMenuController : MonoBehaviour
	{
		internal ConfigEntry<KeyCode> ToggleKey;

		internal bool WindowOpen;

		private Rect windowRect = new Rect(0f, 0f, 640f, 480f);

		private bool openedFromPauseMenu;

		private GUISkin scaledSkin;

		private GUIStyle scaledToolbarStyle;

		private GUIStyle scaledWrapLabelStyle;

		private float scaledSkinFor = -1f;

		private RectTransform pauseMenuMainPanelToRestore;

		private int selectedTab;

		private static readonly string[] TabNames = new string[7] { "Presets", "Size & Scale", "Mutations", "Environment & Events", "Items & Loot", "Randomizer/Combos/Contracts", "Interface & Feedback" };

		private Vector2 scrollPos;

		private bool awaitingRebind;

		private static readonly Dictionary<Type, string[]> EnumNameCache = new Dictionary<Type, string[]>();

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

		private static readonly Dictionary<string, List<ConfigEntryBase>> SectionCache = new Dictionary<string, List<ConfigEntryBase>>();

		private float UiScale => Mathf.Clamp((float)Screen.height / 1080f, 0.8f, 2.5f);

		private void EnsureScaledSkin()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_00fd: 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_010e: Expected O, but got Unknown
			float uiScale = UiScale;
			if (!((Object)(object)scaledSkin != (Object)null) || !Mathf.Approximately(uiScale, scaledSkinFor))
			{
				scaledSkin = Object.Instantiate<GUISkin>(GUI.skin);
				int fontSize = Mathf.RoundToInt(15f * uiScale);
				int fontSize2 = Mathf.RoundToInt(18f * uiScale);
				scaledSkin.label.fontSize = fontSize;
				scaledSkin.button.fontSize = fontSize;
				scaledSkin.toggle.fontSize = fontSize;
				scaledSkin.window.fontSize = fontSize2;
				scaledSkin.horizontalSlider.fixedHeight = 16f * uiScale;
				scaledSkin.horizontalSliderThumb.fixedHeight = 16f * uiScale;
				scaledToolbarStyle = new GUIStyle(scaledSkin.FindStyle("toolbarbutton") ?? scaledSkin.button)
				{
					fontSize = fontSize
				};
				scaledWrapLabelStyle = new GUIStyle(scaledSkin.label)
				{
					wordWrap = true
				};
				scaledSkinFor = uiScale;
			}
		}

		private void ResetWindowSizeAndCenter()
		{
			((Rect)(ref windowRect)).width = (float)Screen.width * 0.5f * 0.83f;
			((Rect)(ref windowRect)).height = (float)Screen.height * 0.5f;
			((Rect)(ref windowRect)).x = (float)Screen.width * 0.03f;
			((Rect)(ref windowRect)).y = ((float)Screen.height - ((Rect)(ref windowRect)).height) / 2f;
		}

		private static string[] EnumNamesFor(Type t)
		{
			if (!EnumNameCache.TryGetValue(t, out var value))
			{
				value = Enum.GetNames(t);
				EnumNameCache[t] = value;
			}
			return value;
		}

		private static void RenderEntry(ConfigEntryBase entry)
		{
			string text = SpaceCamel(entry.Definition.Key);
			Type settingType = entry.SettingType;
			if (settingType == typeof(bool))
			{
				ConfigEntry<bool> obj = (ConfigEntry<bool>)(object)entry;
				obj.Value = GUILayout.Toggle(obj.Value, text, Array.Empty<GUILayoutOption>());
			}
			else if (settingType == typeof(float))
			{
				ConfigEntry<float> val = (ConfigEntry<float>)(object)entry;
				float num = 0f;
				float num2 = 1f;
				ConfigDescription description = entry.Description;
				if (((description != null) ? description.AcceptableValues : null) is AcceptableValueRange<float> val2)
				{
					num = val2.MinValue;
					num2 = val2.MaxValue;
				}
				else
				{
					WarnNoRange(entry);
				}
				GUILayout.Label($"{text}: {val.Value:0.00}", Array.Empty<GUILayoutOption>());
				val.Value = GUILayout.HorizontalSlider(val.Value, num, num2, Array.Empty<GUILayoutOption>());
			}
			else if (settingType == typeof(int))
			{
				ConfigEntry<int> val3 = (ConfigEntry<int>)(object)entry;
				int num3 = 0;
				int num4 = 100;
				ConfigDescription description2 = entry.Description;
				if (((description2 != null) ? description2.AcceptableValues : null) is AcceptableValueRange<int> val4)
				{
					num3 = val4.MinValue;
					num4 = val4.MaxValue;
				}
				else
				{
					WarnNoRange(entry);
				}
				GUILayout.Label($"{text}: {val3.Value}", Array.Empty<GUILayoutOption>());
				val3.Value = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)val3.Value, (float)num3, (float)num4, Array.Empty<GUILayoutOption>()));
			}
			else if (settingType.IsEnum)
			{
				string[] array = EnumNamesFor(settingType);
				int num5 = Array.IndexOf<string>(array, entry.BoxedValue.ToString());
				if (num5 < 0)
				{
					num5 = 0;
				}
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.Label(text + ": " + array[num5], Array.Empty<GUILayoutOption>());
				if (GUILayout.Button("<", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) }))
				{
					entry.BoxedValue = Enum.Parse(settingType, array[(num5 - 1 + array.Length) % array.Length]);
				}
				if (GUILayout.Button(">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) }))
				{
					entry.BoxedValue = Enum.Parse(settingType, array[(num5 + 1) % array.Length]);
				}
				GUILayout.EndHorizontal();
			}
		}

		private static void WarnNoRange(ConfigEntryBase entry)
		{
			string item = entry.Definition.Section + "/" + entry.Definition.Key;
			if (!WarnedNoRange.Contains(item))
			{
				WarnedNoRange.Add(item);
				PetrichorProtocolPlugin.Log.LogWarning((object)("DebugMenu: " + entry.Definition.Section + "/" + entry.Definition.Key + " has no AcceptableValueRange - using a default 0-1/0-100 slider range that may not fit its real values."));
			}
		}

		private static string SpaceCamel(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 4);
			for (int i = 0; i < s.Length; i++)
			{
				char c = s[i];
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(s[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString();
		}

		private static string FriendlySection(string section)
		{
			return section switch
			{
				"HUD" => "HUD", 
				"Protocol" => "Protocol Core", 
				"Hazards" => "Environmental Hazards", 
				_ => SpaceCamel(section), 
			};
		}

		private static List<ConfigEntryBase> EntriesForSections(ConfigFile cfg, params string[] sections)
		{
			string key = string.Join("|", sections);
			if (SectionCache.TryGetValue(key, out var value))
			{
				return value;
			}
			HashSet<string> hashSet = new HashSet<string>(sections);
			List<ConfigEntryBase> list = new List<ConfigEntryBase>();
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)cfg)
			{
				if (hashSet.Contains(item.Key.Section))
				{
					list.Add(item.Value);
				}
			}
			list.Sort(delegate(ConfigEntryBase a, ConfigEntryBase b)
			{
				int num = string.CompareOrdinal(a.Definition.Section, b.Definition.Section);
				if (num != 0)
				{
					return num;
				}
				bool flag = a.Definition.Key == "Enabled";
				bool flag2 = b.Definition.Key == "Enabled";
				return (flag != flag2) ? ((!flag) ? 1 : (-1)) : string.CompareOrdinal(a.Definition.Key, b.Definition.Key);
			});
			SectionCache[key] = list;
			return list;
		}

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			ToggleKey = ((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config.Bind<KeyCode>("DebugMenu", "ToggleKey", (KeyCode)284, "Opens/closes the PetrichorProtocol debug menu. Default F3 (not F2 - SpawnBox, a separate Dileppy mod, already uses F2).");
			PauseScreenController.Awake += new hook_Awake(PauseScreenController_Awake);
		}

		private void Update()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Invalid comparison between Unknown and I4
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (openedFromPauseMenu && PauseScreenController.instancesList.Count == 0)
			{
				WindowOpen = false;
				openedFromPauseMenu = false;
				pauseMenuMainPanelToRestore = null;
			}
			if (awaitingRebind)
			{
				if (Input.GetKeyDown((KeyCode)27))
				{
					awaitingRebind = false;
				}
				else
				{
					if (!Input.anyKeyDown)
					{
						return;
					}
					foreach (KeyCode value in Enum.GetValues(typeof(KeyCode)))
					{
						if ((int)value < 323 && Input.GetKeyDown(value))
						{
							ToggleKey.Value = value;
							awaitingRebind = false;
							break;
						}
					}
				}
			}
			else if (Input.GetKeyDown(ToggleKey.Value))
			{
				bool windowOpen = WindowOpen;
				WindowOpen = !WindowOpen;
				if (WindowOpen && !windowOpen)
				{
					ResetWindowSizeAndCenter();
				}
				if (!WindowOpen)
				{
					RestorePauseMenuIfNeeded();
				}
			}
		}

		private void OnGUI()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (WindowOpen)
			{
				EnsureScaledSkin();
				GUISkin skin = GUI.skin;
				GUI.skin = scaledSkin;
				GUILayout.Window(((Object)this).GetInstanceID(), windowRect, new WindowFunction(DrawWindow), "PetrichorProtocol", Array.Empty<GUILayoutOption>());
				GUI.skin = skin;
			}
		}

		private void DrawPresetsTab()
		{
			ConfigFile config = ((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config;
			PresetsModule presetsModule = null;
			foreach (ModuleBase module in PetrichorProtocolPlugin.Modules)
			{
				if (module is PresetsModule presetsModule2)
				{
					presetsModule = presetsModule2;
					break;
				}
			}
			if (presetsModule == null)
			{
				GUILayout.Label("Presets module not found.", Array.Empty<GUILayoutOption>());
				return;
			}
			foreach (ConfigEntryBase item in EntriesForSections(config, "Presets"))
			{
				if (item.Definition.Key == "SelectedPreset")
				{
					GUILayout.Label("Currently selected: " + PresetsModule.Prettify(presetsModule.Selected.Value), Array.Empty<GUILayoutOption>());
				}
				else
				{
					RenderEntry(item);
				}
			}
			GUILayout.Space(8f);
			GUILayout.Label("Apply a preset:", Array.Empty<GUILayoutOption>());
			float num = Mathf.Max(200f, ((Rect)(ref windowRect)).width - 60f);
			foreach (PresetsModule.Preset value in Enum.GetValues(typeof(PresetsModule.Preset)))
			{
				if (value == PresetsModule.Preset.None)
				{
					continue;
				}
				PresetsModule.Preset p = value;
				GUILayout.Label(PresetsModule.Prettify(p) + " - " + PresetsModule.Flavor(p), scaledWrapLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) });
				if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }))
				{
					try
					{
						presetsModule.ApplyNow(p);
					}
					catch (Exception arg)
					{
						PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: preset apply failed - {arg}");
					}
				}
				GUILayout.Space(6f);
			}
			GUILayout.Space(8f);
			if (GUILayout.Button("Reset all settings to default", Array.Empty<GUILayoutOption>()))
			{
				RiskOfOptionsBridge.ResetAllToDefault(config);
			}
		}

		private static void DrawSectionedTab(params string[] sections)
		{
			foreach (ConfigEntryBase item in EntriesForSections(((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config, sections))
			{
				if (item.Definition.Key == "Enabled")
				{
					GUILayout.Space(6f);
					GUILayout.Label(FriendlySection(item.Definition.Section), Array.Empty<GUILayoutOption>());
				}
				RenderEntry(item);
			}
		}

		private void DrawInterfaceFeedbackTab()
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			DrawSectionedTab("RunTitles", "HUD", "Notifications", "TitleBranding");
			GUILayout.Space(10f);
			GUILayout.Label("— Protocol Core —", Array.Empty<GUILayoutOption>());
			foreach (ConfigEntryBase item in EntriesForSections(((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config, "Protocol", "DebugMenu"))
			{
				if (!(item.Definition.Section == "DebugMenu") || !(item.Definition.Key == "ToggleKey"))
				{
					RenderEntry(item);
				}
			}
			GUILayout.Space(6f);
			if (awaitingRebind)
			{
				GUILayout.Label("Press any key (Esc to cancel)...", Array.Empty<GUILayoutOption>());
				return;
			}
			GUILayout.Label($"Toggle Key: {ToggleKey.Value}", Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Rebind", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
			{
				awaitingRebind = true;
			}
		}

		private void DrawWindow(int id)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			selectedTab = GUILayout.Toolbar(selectedTab, TabNames, scaledToolbarStyle, Array.Empty<GUILayoutOption>());
			scrollPos = GUILayout.BeginScrollView(scrollPos, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(((Rect)(ref windowRect)).height - 100f) });
			try
			{
				switch (selectedTab)
				{
				case 0:
					DrawPresetsTab();
					break;
				case 1:
					DrawSectionedTab("ShrunkenSurvivor", "EnlargedSurvivor", "ShrunkenEnemies", "EnlargedEnemies", "BiggerBullets", "SizeRoulette");
					break;
				case 2:
					DrawSectionedTab("EnemyMutations", "BossMutations");
					break;
				case 3:
					DrawSectionedTab("CurseDeck", "Hazards", "WorldEvents", "StagePersonalities", "TeleporterRules", "EnemySwarm");
					break;
				case 4:
					DrawSectionedTab("ItemDiet", "LootMutations");
					break;
				case 5:
					DrawSectionedTab("Combos", "Randomizer", "Contracts");
					break;
				case 6:
					DrawInterfaceFeedbackTab();
					break;
				default:
					GUILayout.Label(TabNames[selectedTab] + " - added in a later task", Array.Empty<GUILayoutOption>());
					break;
				}
			}
			catch (Exception arg)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: tab '{TabNames[selectedTab]}' failed to render - {arg}");
				GUILayout.Label("This tab failed to render. See the log (" + TabNames[selectedTab] + ").", Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
			{
				WindowOpen = false;
				RestorePauseMenuIfNeeded();
			}
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 20f));
		}

		private void PauseScreenController_Awake(orig_Awake orig, PauseScreenController self)
		{
			orig.Invoke(self);
			try
			{
				AddPauseMenuButton(self);
			}
			catch (Exception arg)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: failed to add pause menu button - {arg}");
			}
		}

		private void AddPauseMenuButton(PauseScreenController controller)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			if ((Object)(object)controller.exitGameButton == (Object)null)
			{
				PetrichorProtocolPlugin.Log.LogWarning((object)"DebugMenu: PauseScreenController.exitGameButton was null, cannot add pause menu button.");
				return;
			}
			GameObject obj = Object.Instantiate<GameObject>(controller.exitGameButton, controller.exitGameButton.transform.parent);
			((Object)obj).name = "PetrichorProtocolPauseButton";
			LanguageTextMeshController component = obj.GetComponent<LanguageTextMeshController>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			HGTextMeshProUGUI componentInChildren = obj.GetComponentInChildren<HGTextMeshProUGUI>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)componentInChildren).text = "PetrichorProtocol";
			}
			HGButton component2 = obj.GetComponent<HGButton>();
			if ((Object)(object)component2 == (Object)null)
			{
				PetrichorProtocolPlugin.Log.LogWarning((object)"DebugMenu: cloned pause menu button has no HGButton component, cannot wire click.");
				return;
			}
			for (int i = 0; i < ((UnityEventBase)((Button)component2).onClick).GetPersistentEventCount(); i++)
			{
				((UnityEventBase)((Button)component2).onClick).SetPersistentListenerState(i, (UnityEventCallState)0);
			}
			((UnityEventBase)((Button)component2).onClick).RemoveAllListeners();
			((UnityEvent)((Button)component2).onClick).AddListener((UnityAction)delegate
			{
				OnPauseMenuButtonClicked(controller);
			});
		}

		private void OnPauseMenuButtonClicked(PauseScreenController controller)
		{
			try
			{
				((Component)controller.mainPanel).gameObject.SetActive(false);
				pauseMenuMainPanelToRestore = controller.mainPanel;
				openedFromPauseMenu = true;
				WindowOpen = true;
				ResetWindowSizeAndCenter();
			}
			catch (Exception arg)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: pause menu button click failed - {arg}");
			}
		}

		private void LateUpdate()
		{
			if (openedFromPauseMenu && (Object)(object)pauseMenuMainPanelToRestore != (Object)null)
			{
				((Component)pauseMenuMainPanelToRestore).gameObject.SetActive(false);
			}
		}

		private void RestorePauseMenuIfNeeded()
		{
			if (openedFromPauseMenu)
			{
				openedFromPauseMenu = false;
				if ((Object)(object)pauseMenuMainPanelToRestore != (Object)null)
				{
					((Component)pauseMenuMainPanelToRestore).gameObject.SetActive(true);
				}
				pauseMenuMainPanelToRestore = null;
			}
		}
	}
	internal class TeleporterRulesModule : ModuleBase
	{
		public enum TeleRule
		{
			None,
			ShrinkingZone,
			BloodZone,
			EnemyFavoredZone,
			Overcharged
		}

		public ConfigEntry<float> RuleChance;

		private TeleRule active;

		private HoldoutZoneController zone;

		private float baseRadiusCache = -1f;

		private static readonly Random rng = new Random();

		public override string Name => "Teleporter Rules";

		public override string StandaloneGuid => null;

		public override int Stars => 2;

		public override bool UsesStageStart => true;

		public override bool UsesFixedUpdate => true;

		public override bool UsesRecalcStats => true;

		public override bool UsesHeal => true;

		public override void Bind(ConfigFile cfg)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("TeleporterRules", "Enabled", false, "Each teleporter event can roll a rule modifier: Shrinking Zone, Blood Zone, Enemy-Favored Zone, Overcharged.");
			RuleChance = cfg.Bind<float>("TeleporterRules", "TeleporterRuleChance", 50f, new ConfigDescription("Chance (percent) that starting a teleporter event applies one random rule modifier to it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			HoldoutZoneController.OnEnable += new hook_OnEnable(Zone_OnEnable);
		}

		public override void Unhook()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			HoldoutZoneController.OnEnable -= new hook_OnEnable(Zone_OnEnable);
		}

		public override void OnStageStart(Stage stage)
		{
			active = TeleRule.None;
			zone = null;
			baseRadiusCache = -1f;
		}

		private void Zone_OnEnable(orig_OnEnable orig, HoldoutZoneController self)
		{
			orig.Invoke(self);
			if (!base.Active || !NetworkServer.active || !Object.op_Implicit((Object)(object)TeleporterInteraction.instance) || (Object)(object)((Component)self).gameObject != (Object)(object)((Component)TeleporterInteraction.instance.holdoutZoneController).gameObject)
			{
				return;
			}
			double num = rng.NextDouble() * 100.0;
			if (num > (double)RuleChance.Value)
			{
				DebugLog($"Teleporter rule rolled {num:0.0}% vs {RuleChance.Value:0.0}% chance - no rule applied this event");
				active = TeleRule.None;
				return;
			}
			active = (TeleRule)(1 + rng.Next(4));
			zone = self;
			baseRadiusCache = self.baseRadius;
			DebugLog($"Teleporter rule rolled {num:0.0}% vs {RuleChance.Value:0.0}% chance - applying {active} (baseRadius={baseRadiusCache:0.0})");
			if (NotificationsModule.Gate)
			{
				string arg = active switch
				{
					TeleRule.ShrinkingZone => "The zone shrinks as it charges. It gets personal at the end.", 
					TeleRule.BloodZone => "Inside the zone: +25% damage dealt, healing cut to 60%.", 
					TeleRule.EnemyFavoredZone => "Enemies inside the zone gain 40 armor. Kite them out or fight uphill.", 
					TeleRule.Overcharged => "Charges 50% faster - but the boss is much stronger.", 
					_ => "", 
				};
				Chat.AddMessage($"<color=#ff9d5c>TELEPORTER RULE: {active}</color> - {arg}");
			}
		}

		public override void OnFixedUpdateServer()
		{
			if (active == TeleRule.None || !Object.op_Implicit((Object)(object)zone) || !((Behaviour)zone).isActiveAndEnabled)
			{
				return;
			}
			try
			{
				if (active == TeleRule.ShrinkingZone && baseRadiusCache > 0f)
				{
					zone.baseRadius = Mathf.Lerp(baseRadiusCache, baseRadiusCache * 0.35f, zone.charge);
				}
				else if (active == TeleRule.Overcharged)
				{
					zone.charge = Mathf.Min(1f, zone.charge + 0.5f * Time.fixedDeltaTime * ChargeRatePerSecond(zone));
				}
			}
			catch (Exception arg)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: zone tick failed: {arg}");
			}
		}

		private static float ChargeRatePerSecond(HoldoutZoneController z)
		{
			if (!(z.baseChargeDuration > 0f))
			{
				return 0f;
			}
			return 1f / z.baseChargeDuration;
		}

		public bool InZone(CharacterBody body)
		{
			if (active != TeleRule.None && Object.op_Implicit((Object)(object)zone) && ((Behaviour)zone).isActiveAndEnabled)
			{
				return zone.IsBodyInChargingRadius(body);
			}
			return false;
		}

		public override void OnRecalcStats(CharacterBody self)
		{
			if (active == TeleRule.None || !Object.op_Implicit((Object)(object)zone))
			{
				return;
			}
			switch (active)
			{
			case TeleRule.BloodZone:
				if (ModuleBase.IsPlayer(self) && InZone(self))
				{
					self.damage *= 1.25f;
				}
				break;
			case TeleRule.EnemyFavoredZone:
				if (ModuleBase.IsHostileEnemy(self) && InZone(self))
				{
					self.armor += 40f;
				}
				break;
			case TeleRule.Overcharged:
				if (self.isBoss)
				{
					self.maxHealth *= 1.5f;
					self.damage *= 1.4f;
				}
				break;
			}
		}

		public override float ModifyHeal(HealthComponent target, float amount)
		{
			if (active != TeleRule.BloodZone || !Object.op_Implicit((Object)(object)target.body) || !ModuleBase.IsPlayer(target.body) || !InZone(target.body))
			{
				return amount;
			}
			return amount * 0.6f;
		}
	}
	internal class StagePersonalitiesModule : ModuleBase
	{
		public enum Personality
		{
			None,
			LowGravity,
			TreasureStorm,
			MonsterMigration,
			BossTerritory
		}

		public ConfigEntry<float> PersonalityChance;

		public ConfigEntry<float> RewardBonus;

		private Personality active;

		private Personality previous;

		private Vector3 baseGravity;

		private bool gravityStored;

		private float nextMigrationPulse;

		private static readonly Random rng = new Random();

		public override string Name => "Stage Personalities";

		public override string StandaloneGuid => null;

		public override int Stars => 2;

		public override bool UsesStageStart => true;

		public override bool UsesRunStart => true;

		public override bool UsesRecalcStats => true;

		public override bool UsesFixedUpdate => true;

		public override bool UsesReward => true;

		public override void OnRunStart()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (gravityStored)
			{
				Physics.gravity = baseGravity;
				gravityStored = false;
			}
			active = Personality.None;
			previous = Personality.None;
		}

		public override void Bind(ConfigFile cfg)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("StagePersonalities", "Enabled", false, "Every stage can roll a temporary identity: Low Gravity, Treasure Storm, Monster Migration, Boss Territory.");
			PersonalityChance = cfg.Bind<float>("StagePersonalities", "PersonalityChance", 60f, new ConfigDescription("Chance (percent) that a stage takes on one random personality when you arrive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			RewardBonus = cfg.Bind<float>("StagePersonalities", "RewardMultiplier", 0.2f, new ConfigDescription("Extra gold/XP granted while a stage personality is active, as a fraction (0.2 = +20%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			SceneDirector.Start += new hook_Start(SceneDirector_Start);
		}

		public override void Unhook()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			SceneDirector.Start -= new hook_Start(SceneDirector_Start);
		}

		public override void OnStageStart(Stage stage)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: 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_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			if (gravityStored)
			{
				Physics.gravity = baseGravity;
				gravityStored = false;
			}
			active = Personality.None;
			if (!NetworkServer.active)
			{
				return;
			}
			double num = rng.NextDouble() * 100.0;
			if (num > (double)PersonalityChance.Value)
			{
				DebugLog($"Stage personality rolled {num:0.0}% vs {PersonalityChance.Value:0.0}% chance - stage stays plain");
				return;
			}
			Personality personality;
			do
			{
				personality = (Personality)(1 + rng.Next(4));
			}
			while (personality == previous);
			active = personality;
			previous = personality;
			nextMigrationPulse = Time.time + 25f;
			DebugLog($"Stage personality rolled {num:0.0}% vs {PersonalityChance.Value:0.0}% chance - applying {active} (reward bonus +{RewardBonus.Value:P0})");
			if (active == Personality.LowGravity)
			{
				if (!gravityStored)
				{
					baseGravity = Physics.gravity;
					gravityStored = true;
				}
				Physics.gravity = baseGravity * 0.55f;
				DebugLog($"Low Gravity applied - gravity set to {Physics.gravity} (base {baseGravity} x0.55)");
			}
			else if (active == Personality.BossTerritory)
			{
				List<MasterIndex> list = new List<MasterIndex>();
				foreach (CharacterMaster allAiMaster in MasterCatalog.allAiMasters)
				{
					if (Object.op_Implicit((Object)(object)allAiMaster) && Object.op_Implicit((Object)(object)allAiMaster.bodyPrefab))
					{
						CharacterBody component = allAiMaster.bodyPrefab.GetComponent<CharacterBody>();
						if (Object.op_Implicit((Object)(object)component) && component.isChampion)
						{
							list.Add(allAiMaster.masterIndex);
						}
					}
				}
				if (list.Count > 0)
				{
					GameObject masterPrefab = MasterCatalog.GetMasterPrefab(list[rng.Next(list.Count)]);
					ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances;
					Vector3 val = Vector3.zero;
					if (instances != null && instances.Count > 0 && Object.op_Implicit((Object)(object)instances[0]) && Object.op_Implicit((Object)(object)instances[0].master))
					{
						CharacterBody body = instances[0].master.GetBody();
						if (Object.op_Implicit((Object)(object)body))
						{
							val = body.corePosition + new Vector3(60f, 10f, 60f);
						}
					}
					CharacterMaster val2 = new MasterSummon
					{
						masterPrefab = masterPrefab,
						position = val,
						rotation = Quaternion.identity,
						teamIndexOverride = (TeamIndex)2,
						ignoreTeamMemberLimit = true
					}.Perform();
					if ((Object)(object)val2 == (Object)null)
					{
						PetrichorProtocolPlugin.Log.LogWarning((object)(Name + ": BossTerritory summon failed this stage."));
					}
					else
					{
						CharacterBody body2 = val2.GetBody();
						DebugLog(string.Format("Boss Territory spawned {0} at {1} ({2} champion candidates available)", Object.op_Implicit((Object)(object)body2) ? Util.GetBestBodyName(((Component)body2).gameObject) : "unknown body", val, list.Count));
					}
				}
				else
				{
					DebugLog("Boss Territory rolled but no champion-class master candidates were found - nothing spawned");
				}
			}
			if (NotificationsModule.Gate)
			{
				string arg = active switch
				{
					Personality.LowGravity => "Gravity is light here. Enjoy the airtime.", 
					Personality.TreasureStorm => "Riches everywhere - and the enemies know you're greedy.", 
					Personality.MonsterMigration => "The swarms run deep on this one.", 
					Personality.BossTerritory => "Something enormous already lives here.", 
					_ => "", 
				};
				Chat.AddMessage($"<color=#7fd4a8>STAGE PERSONALITY: {active}</color> - {arg} <color=#ffd700>(+{RewardBonus.Value:P0} rewards)</color>");
			}
		}

		private void SceneDirector_Start(orig_Start orig, SceneDirector self)
		{
			if (base.Active && NetworkServer.active && active == Personality.TreasureStorm)
			{
				try
				{
					int interactableCredit = self.interactableCredit;
					self.interactableCredit = (int)((float)self.interactableCredit * 1.6f);
					DebugLog($"Treasure Storm boosted interactable credit {interactableCredit} -> {self.interactableCredit} (x1.6)");
				}
				catch (Exception arg)
				{
					PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: credit boost failed: {arg}");
				}
			}
			orig.Invoke(self);
		}

		public override void OnRecalcStats(CharacterBody self)
		{
			if (active == Personality.TreasureStorm && ModuleBase.IsHostileEnemy(self))
			{
				self.maxHealth *= 1.15f;
				self.damage *= 1.15f;
			}
		}

		public override void OnFixedUpdateServer()
		{
			if (active != Personality.MonsterMigration || Time.time < nextMigrationPulse)
			{
				return;
			}
			nextMigrationPulse = Time.time + 25f;
			try
			{
				float num = 40f * (Object.op_Implicit((Object)(object)Run.instance) ? Run.instance.difficultyCoefficient : 1f);
				int num2 = 0;
				foreach (CombatDirector instances in CombatDirector.instancesList)
				{
					if (Object.op_Implicit((Object)(object)instances))
					{
						instances.monsterCredit += num;
						num2++;
					}
				}
				DebugLog($"Monster Migration pulse added {num:0.0} credit to {num2} combat director(s) - next pulse in 25s");
			}
			catch (Exception arg)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: migration pulse failed: {arg}");
			}
		}

		public override float RewardMultiplier(DamageReport report)
		{
			if (active == Personality.None)
			{
				return 1f;
			}
			return 1f + RewardBonus.Value;
		}
	}
	internal class WorldEventsModule : ModuleBase
	{
		public enum WorldEvent
		{
			None,
			TheHunt,
			SupplyDrop,
			MutationWave,
			TitanArrival,
			GravityFailure,
			BloodFrenzy
		}

		public class HunterMarker : MonoBehaviour
		{
		}

		private class TitanSizeMarker : MonoBehaviour
		{
			public Vector3 baseScale;
		}

		public class TitanMarker : MonoBehaviour
		{
		}

		public ConfigEntry<float> MinInterval;

		public ConfigEntry<float> MaxInterval;

		public ConfigEntry<float> EventDuration;

		internal WorldEvent active;

		private float nextEventTime;

		private float eventEndTime;

		private Vector3 baseGravity;

		private bool gravityStored;

		private CharacterMaster hunter;

		private static readonly Random rng = new Random();

		public override string Name => "World Events";

		public override string StandaloneGuid => null;

		public override int Stars => 3;

		public override bool UsesRunStart => true;

		public override bool UsesStageStart => true;

		public override bool UsesFixedUpdate => true;

		public override bool UsesRecalcStats => true;

		public override bool UsesReward => true;

		public bool MutationWaveActive => active == WorldEvent.MutationWave;

		public override void Bind(ConfigFile cfg)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("WorldEvents", "Enabled", false, "Every few minutes a temporary world event can trigger: The Hunt, Supply Drop, Mutation Wave, Titan Arrival, Gravity Failure, Blood Frenzy.");
			MinInterval = cfg.Bind<float>("WorldEvents", "MinimumTimeBetweenEvents", 120f, new ConfigDescription("Shortest wait, in seconds, before another world event can fire. The actual gap is random between this and the maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), Array.Empty<object>()));
			MaxInterval = cfg.Bind<float>("WorldEvents", "MaximumTimeBetweenEvents", 300f, new ConfigDescription("Longest wait, in seconds, before a world event must fire. The actual gap is random between the minimum and this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 900f), Array.Empty<object>()));
			EventDuration = cfg.Bind<float>("WorldEvents", "EventDuration", 60f, new ConfigDescription("How long a timed world event stays active, in seconds (The Hunt runs until the hunter dies; instant events like Supply Drop ignore this).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 180f), Array.Empty<object>()));
			HealthComponent.TakeDamage += new hook_TakeDamage(HealthComponent_TakeDamage);
		}

		public override void Unhook()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			HealthComponent.TakeDamage -= new hook_TakeDamage(HealthComponent_TakeDamage);
		}

		public override void OnRunStart()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (gravityStored)
			{
				Physics.gravity = baseGravity;
				gravityStored = false;
			}
			active = WorldEvent.None;
			ScheduleNext();
		}

		public override void OnStageStart(Stage stage)
		{
			EndEvent(silent: true);
			ScheduleNext();
		}

		private void ScheduleNext()
		{
			float num = Mathf.Min(MinInterval.Value, MaxInterval.Value);
			float num2 = Mathf.Max(MinInterval.Value, MaxInterval.Value);
			float num3 = num + (float)rng.NextDouble() * (num2 - num);
			nextEventTime = Time.time + num3;
			DebugLog($"Next world event scheduled in {num3:0.0}s (range {num:0.0}-{num2:0.0}s)");
		}

		public override void OnFixedUpdateServer()
		{
			if (active != WorldEvent.None)
			{
				bool flag = active == WorldEvent.TheHunt && ((Object)(object)hunter == (Object)null || !hunter.hasBody);
				if (Time.time >= eventEndTime || flag)
				{
					EndEvent(silent: false);
				}
			}
			else if (!(Time.time < nextEventTime))
			{
				WorldEvent worldEvent = (WorldEvent)(1 + rng.Next(6));
				DebugLog($"World event timer elapsed - picked {worldEvent}");
				StartEvent(worldEvent);
			}
		}

		private void StartEvent(WorldEvent e)
		{
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			active = e;
			eventEndTime = Time.time + EventDuration.Value;
			string arg = "";
			try
			{
				switch (e)
				{
				case WorldEvent.TheHunt:
					hunter = SummonHunter();
					if ((Object)(object)hunter == (Object)null)
					{
						DebugLog("The Hunt failed to find a champion candidate or valid target - skipped, rescheduling");
						active = WorldEvent.None;
						ScheduleNext();
						return;
					}
					eventEndTime = Time.time + 600f;
					arg = "A hunter has your scent. Kill it for a rich bounty.";
					DebugLog("The Hunt started - hunter " + Util.GetBestBodyName(((Component)hunter.GetBody()).gameObject) + " summoned, +Fire elite equipment, +30 BoostHp, +15 BoostDamage");
					break;
				case WorldEvent.SupplyDrop:
					if (!SpawnSupplyDrop())
					{
						DebugLog("Supply Drop rolled but spawn failed (no chest spawn card or no valid director spawn) - skipped, rescheduling");
						active = WorldEvent.None;
						ScheduleNext();
						return;
					}
					eventEndTime = Time.time + 1f;
					arg = "Extra supplies have appeared somewhere on the map.";
					DebugLog("Supply Drop spawned a chest near the lead player");
					break;
				case WorldEvent.MutationWave:
					arg = "Mutation rates are spiking. Everything spawning now is wrong.";
					DebugLog($"Mutation Wave started - active for {EventDuration.Value:0.0}s (EnemyMutationsModule reads MutationWaveActive)");
					break;
				case WorldEvent.TitanArrival:
					if (!TitanizeRandomEnemy())
					{
						DebugLog("Titan Arrival rolled but no eligible non-boss enemy was alive on the stage - skipped, rescheduling");
						active = WorldEvent.None;
						ScheduleNext();
						return;
					}
					eventEndTime = Time.time + 1f;
					arg = "One of them just became enormous.";
					DebugLog("Titan Arrival applied - one enemy scaled to 2.6x model size, x3.5 max health, x1.6 damage, x0.8 move speed");
					break;
				case WorldEvent.GravityFailure:
					if (!gravityStored)
					{
						baseGravity = Physics.gravity;
						gravityStored = true;
					}
					Physics.gravity = baseGravity * 0.45f;
					arg = "Gravity is failing. Temporarily. Probably.";
					DebugLog($"Gravity Failure started - gravity set to {Physics.gravity} (base {baseGravity} x0.45), lasts {EventDuration.Value:0.0}s");
					break;
				case WorldEvent.BloodFrenzy:
					MarkAllStatsDirty();
					arg = "Everything attacks faster and dies easier. Including you.";
					DebugLog($"Blood Frenzy started - x1.5 attack speed for everyone, x1.35 damage taken via TakeDamage hook, lasts {EventDuration.Value:0.0}s");
					break;
				}
				if (NotificationsModule.Gate)
				{
					Chat.AddMessage($"<color=#ff5c5c>WORLD EVENT: {e}</color> - {arg}");
				}
			}
			catch (Exception arg2)
			{
				PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: event start failed: {arg2}");
				active = WorldEvent.None;
				ScheduleNext();
			}
		}

		private void EndEvent(bool silent)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (active == WorldEvent.None)
			{
				ScheduleNext();
				return;
			}
			WorldEvent worldEvent = active;
			if (worldEvent == WorldEvent.GravityFailure && gravityStored)
			{
				Physics.gravity = baseGravity;
			}
			if (worldEvent == WorldEvent.TheHunt && (Object)(object)hunter != (Object)null && hunter.hasBody)
			{
				hunter.TrueKill();
			}
			hunter = null;
			active = WorldEvent.None;
			if (worldEvent == WorldEvent.BloodFrenzy)
			{
				MarkAllStatsDirty();
			}
			DebugLog($"World event {worldEvent} ended (silent={silent})");
			if (!silent && NotificationsModule.Gate && worldEvent != WorldEvent.SupplyDrop && worldEvent != WorldEvent.TitanArrival)
			{
				Chat.AddMessage($"<color=#8a8a8a>World event over: {worldEvent}.</color>");
			}
			ScheduleNext();
		}

		private static void MarkAllStatsDirty()
		{
			foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList)
			{
				if (Object.op_Implicit((Object)(object)readOnlyInstances))
				{
					readOnlyInstances.MarkAllStatsDirty();
				}
			}
		}

		private CharacterMaster SummonHunter()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			List<MasterIndex> list = new List<MasterIndex>();
			foreach (CharacterMaster allAiMaster in MasterCatalog.allAiMasters)
			{
				if (Object.op_Implicit((Object)(object)allAiMaster) && Object.op_Implicit((Object)(object)allAiMaster.bodyPrefab))
				{
					CharacterBody component = allAiMaster.bodyPrefab.GetComponent<CharacterBody>();
					if (Object.op_Implicit((Object)(object)component) && component.isChampion)
					{
						list.Add(allAiMaster.masterIndex);
					}
				}
			}
			if (list.Count == 0)
			{
				return null;
			}
			ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances;
			if (instances == null || instances.Count == 0)
			{
				return null;
			}
			CharacterBody val = (Object.op_Implicit((Object)(object)instances[0].master) ? instances[0].master.GetBody() : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			CharacterMaster val2 = new MasterSummon
			{
				masterPrefab = MasterCatalog.GetMasterPrefab(list[rng.Next(list.Count)]),
				position = val.corePosition + new Vector3(40f, 8f, 40f),
				rotation = Quaternion.identity,
				teamIndexOverride = (TeamIndex)2,
				ignoreTeamMemberLimit = true
			}.Perform();
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			if (Object.op_Implicit((Object)(object)val2.inventory))
			{
				EliteDef fire = Elites.Fire;
				if (Object.op_Implicit((Object)(object)fire) && Object.op_Implicit((Object)(object)fire.eliteEquipmentDef))
				{
					val2.inventory.SetEquipmentIndex(fire.eliteEquipmentDef.equipmentIndex, false);
				}
				val2.inventory.GiveItemPermanent(Items.BoostHp, 30);
				val2.inventory.GiveItemPermanent(Items.BoostDamage, 15);
			}
			((Component)val2).gameObject.AddComponent<HunterMarker>();
			return val2;
		}

		private bool SpawnSupplyDrop()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			InteractableSpawnCard val = LegacyResourcesAPI.Load<InteractableSpawnCard>("SpawnCards/InteractableSpawnCard/iscChest1");
			if (!Object.op_Implicit((Object)(object)val))
			{
				PetrichorProtocolPlugin.Log.LogWarning((object)(Name + ": chest spawn card not found in this game version - skipping Supply Drop."));
				return false;
			}
			ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances;
			if (instances == null || instances.Count == 0)
			{
				return false;
			}
			CharacterBody val2 = (Object.op_Implicit((Object)(object)instances[0].master) ? instances[0].master.GetBody() : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return false;
			}
			DirectorPlacementRule val3 = new DirectorPlacementRule
			{
				placementMode = (PlacementMode)1,
				position = val2.corePosition,
				minDistance = 15f,
				maxDistance = 60f
			};
			DirectorSpawnRequest val4 = new DirectorSpawnRequest((SpawnCard)(object)val, val3, new Xoroshiro128Plus((ulong)rng.Next()));
			return (Object)(object)(Object.op_Implicit((Object)(object)DirectorCore.instance) ? DirectorCore.instance.TrySpawnObject(val4) : null) != (Object)null;
		}

		private bool TitanizeRandomEnemy()
		{
			//IL_0155: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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)
			List<CharacterBody> list = new List<CharacterBody>();
			foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList)
			{
				if (Object.op_Implicit((Object)(object)readOnlyInstances) && ModuleBase.IsHostileEnemy(readOnlyInstances) && !readOnlyInstances.isBoss && Object.op_Implicit((Object)(object)readOnlyInstances.healthComponent) && readOnlyInstances.healthComponent.alive)
				{
					list.Add(readOnlyInstances);
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			CharacterBody val = list[rng.Next(list.Count)];
			DebugLog($"Titan Arrival chose {Util.GetBestBodyName(((Component)val).gameObject)} out of {list.Count} eligible enemies");
			ModelLocator modelLocator = val.modelLocator;
			if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform))
			{
				GameObject gameObject = ((Component)modelLocator.modelTransform).gameObject;
				TitanSizeMarker titanSizeMarker = gameObject.GetComponent<TitanSizeMarker>();
				if (!Object.op_Implicit((Object)(object)titanSizeMarker))
				{
					titanSizeMarker = gameObject.AddComponent<TitanSizeMarker>();
					titanSizeMarker.baseScale = gameObject.transform.localScale;
				}
				gameObject.transform.localScale = titanSizeMarker.baseScale * 2.6f;
			}
			((Component)val).gameObject.AddComponent<TitanMarker>();
			val.RecalculateStats();
			if (Object.op_Implicit((Object)(object)val.healthComponent))
			{
				val.healthComponent.HealFraction(1f, default(ProcChainMask));
			}
			return true;
		}

		public override void OnRecalcStats(CharacterBody self)
		{
			if (Object.op_Implicit((Object)(object)((Component)self).GetComponent<TitanMarker>()))
			{
				self.maxHealth *= 3.5f;
				self.damage *= 1.6f;
				self.moveSpeed *= 0.8f;
			}
			if (active == WorldEvent.BloodFrenzy)
			{
				self.attackSpeed *= 1.5f;
			}
		}

		private void HealthComponent_TakeDamage(orig_TakeDamage orig, HealthComponent self, DamageInfo damageInfo)
		{
			if (base.Active && active == WorldEvent.BloodFrenzy && damageInfo != null)
			{
				damageInfo.damage *= 1.35f;
			}
			orig.Invoke(self, damageInfo);
		}

		public override float RewardMultiplier(DamageReport report)
		{
			if ((Object)(object)report?.victimBody == (Object)null)
			{
				return 1f;
			}
			if (Object.op_Implicit((Object)(object)((Component)report.victimBody).GetComponent<HunterMarker>()))
			{
				return 6f;
			}
			if (Object.op_Implicit((Object)(object)((