Decompiled source of MobStatsUI v5.3.1

MobStatsUI.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MobStatsUI
{
	public class RageNetMsg : MessageBase
	{
		public NetworkInstanceId id;

		public float size;

		public float ms;

		public float atk;

		public float hp;

		public float dmg;

		public override void Serialize(NetworkWriter w)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			w.Write(id);
			w.Write(size);
			w.Write(ms);
			w.Write(atk);
			w.Write(hp);
			w.Write(dmg);
		}

		public override void Deserialize(NetworkReader r)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			id = r.ReadNetworkId();
			size = r.ReadSingle();
			ms = r.ReadSingle();
			atk = r.ReadSingle();
			hp = r.ReadSingle();
			dmg = r.ReadSingle();
		}
	}
	public class HelloMsg : MessageBase
	{
		public override void Serialize(NetworkWriter w)
		{
		}

		public override void Deserialize(NetworkReader r)
		{
		}
	}
	public class ConfigChunkMsg : MessageBase
	{
		public int ver;

		public int idx;

		public int total;

		public string part = "";

		public override void Serialize(NetworkWriter w)
		{
			w.Write(ver);
			w.Write(idx);
			w.Write(total);
			w.Write(part ?? "");
		}

		public override void Deserialize(NetworkReader r)
		{
			ver = r.ReadInt32();
			idx = r.ReadInt32();
			total = r.ReadInt32();
			part = r.ReadString();
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("shlyapi.MobStatsUI", "MobStatsUI", "5.3.1")]
	public class MobStatsUIPlugin : BaseUnityPlugin
	{
		private struct Snap
		{
			public float baseMaxHealth;

			public float levelMaxHealth;

			public float baseRegen;

			public float levelRegen;

			public float baseMaxShield;

			public float levelMaxShield;

			public float baseMoveSpeed;

			public float levelMoveSpeed;

			public float baseAttackSpeed;

			public float levelAttackSpeed;

			public float baseDamage;

			public float levelDamage;

			public float baseArmor;

			public float baseCrit;

			public float baseAcceleration;

			public float baseJumpPower;

			public float levelJumpPower;

			public bool champion;
		}

		private class Rage
		{
			public float size;

			public float ms;

			public float atk;

			public float hp;

			public float dmg;

			public float born;
		}

		private class Tracked
		{
			private float orig;

			private float last = float.NaN;

			public float Get(float current, Func<float, float> f)
			{
				if (float.IsNaN(last) || !Mathf.Approximately(current, last))
				{
					orig = current;
				}
				last = f(orig);
				return last;
			}
		}

		private class DirState
		{
			public Tracked credit = new Tracked();

			public Tracked minS = new Tracked();

			public Tracked maxS = new Tracked();

			public Tracked minR = new Tracked();

			public Tracked maxR = new Tracked();

			public Tracked skip = new Tracked();

			public Tracked elite = new Tracked();

			public Tracked dist = new Tracked();
		}

		private class StageRule
		{
			public ConfigEntry<float> first;

			public ConfigEntry<float> every;

			public ConfigEntry<float> max;

			public ConfigEntry<float> mult;
		}

		private class RunStats
		{
			public int kills;

			public int ragesSpawned;

			public int ragesKilled;

			public int expired;

			public int items;

			public float gold;

			public float xp;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action <0>__RestoreLocal;

			public static Action <1>__OnGameLoaded;

			public static Action<CharacterBody> <2>__OnBodyStart;

			public static Action<DamageReport> <3>__OnDeath;

			public static NetworkMessageDelegate <4>__OnHello;

			public static NetworkMessageDelegate <5>__OnRageMsg;

			public static NetworkMessageDelegate <6>__OnCfgMsg;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static EventHandler <>9__118_0;

			public static EventHandler <>9__119_0;

			public static Action<Run> <>9__120_0;

			public static Action<Run, RunReport> <>9__120_1;

			public static UnityAction <>9__120_2;

			public static EventHandler<SettingChangedEventArgs> <>9__120_3;

			public static Predicate<CharacterBody> <>9__148_0;

			public static Func<CharacterBody, bool> <>9__152_1;

			public static Func<CharacterBody, bool> <>9__152_2;

			public static Func<CharacterBody, bool> <>9__152_0;

			public static Func<ConfigEntryBase, bool> <>9__156_0;

			public static Func<ConfigEntryBase, string> <>9__156_1;

			public static Func<ConfigEntryBase, bool> <>9__159_0;

			public static Func<CharacterBody, bool> <>9__163_0;

			internal void <Num>b__118_0(object s, EventArgs a)
			{
				Changed();
			}

			internal void <Toggle>b__119_0(object s, EventArgs a)
			{
				Changed();
			}

			internal void <Awake>b__120_0(Run run)
			{
				stats = new RunStats();
			}

			internal void <Awake>b__120_1(Run run, RunReport report)
			{
				PrintStats();
			}

			internal void <Awake>b__120_2()
			{
				ResetAll();
				ApplyAll();
			}

			internal void <Awake>b__120_3(object sender, SettingChangedEventArgs args)
			{
				if (!suppress)
				{
					cfgDirty = true;
					cfgDirtyAt = Time.unscaledTime;
				}
			}

			internal bool <CheckElites>b__148_0(CharacterBody b)
			{
				return !Object.op_Implicit((Object)(object)b);
			}

			internal bool <TryRage>b__152_1(CharacterBody k)
			{
				if (Object.op_Implicit((Object)(object)k) && Object.op_Implicit((Object)(object)k.healthComponent))
				{
					return !k.healthComponent.alive;
				}
				return true;
			}

			internal bool <TryRage>b__152_2(CharacterBody k)
			{
				return !Object.op_Implicit((Object)(object)k);
			}

			internal bool <TryRage>b__152_0(CharacterBody b)
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)b) && IsEnemy(b) && Object.op_Implicit((Object)(object)b.healthComponent) && b.healthComponent.alive && !rages.ContainsKey(b) && snaps.ContainsKey(b.bodyIndex) && !b.isChampion)
				{
					if (rageElite.Value)
					{
						return b.isElite;
					}
					return true;
				}
				return false;
			}

			internal bool <SerializeSynced>b__156_0(ConfigEntryBase e)
			{
				return !localOnly.Contains(e);
			}

			internal string <SerializeSynced>b__156_1(ConfigEntryBase e)
			{
				return Uri.EscapeDataString(e.Definition.Section + "/" + e.Definition.Key) + "=" + Uri.EscapeDataString(e.GetSerializedValue());
			}

			internal bool <ApplySynced>b__159_0(ConfigEntryBase e)
			{
				return !localOnly.Contains(e);
			}

			internal bool <CleanupRages>b__163_0(CharacterBody k)
			{
				if (Object.op_Implicit((Object)(object)k) && Object.op_Implicit((Object)(object)k.healthComponent))
				{
					return !k.healthComponent.alive;
				}
				return true;
			}
		}

		public const string Guid = "shlyapi.MobStatsUI";

		public const string Name = "MobStatsUI";

		public const string Version = "5.3.1";

		private static MobStatsUIPlugin I;

		private static readonly Dictionary<BodyIndex, Snap> snaps = new Dictionary<BodyIndex, Snap>();

		private static readonly HashSet<BodyIndex> survivors = new HashSet<BodyIndex>();

		private static readonly Dictionary<int, DirState> dirStates = new Dictionary<int, DirState>();

		private static readonly Dictionary<CharacterBody, Rage> rages = new Dictionary<CharacterBody, Rage>();

		private static readonly Dictionary<CharacterBody, Vector3> origScale = new Dictionary<CharacterBody, Vector3>();

		private static readonly HashSet<CharacterBody> expired = new HashSet<CharacterBody>();

		private static readonly HashSet<CharacterBody> eliteDone = new HashSet<CharacterBody>();

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

		private static RunStats stats = new RunStats();

		private static bool ready;

		private static bool suppress;

		private static bool modEnabled = true;

		private static int origMonsterCap = -1;

		private static int lastStage = -1;

		private static float timeFactor = 1f;

		private float dirTimer;

		private float rageTimer;

		private float logTimer;

		private static ConfigEntry<float> health;

		private static ConfigEntry<float> regen;

		private static ConfigEntry<float> shield;

		private static ConfigEntry<float> moveSpeed;

		private static ConfigEntry<float> attackSpeed;

		private static ConfigEntry<float> damage;

		private static ConfigEntry<float> accel;

		private static ConfigEntry<float> jump;

		private static ConfigEntry<float> armorAdd;

		private static ConfigEntry<float> critAdd;

		private static ConfigEntry<float> sizeMult;

		private static ConfigEntry<float> timeScale;

		private static ConfigEntry<bool> affectSurvivors;

		private static ConfigEntry<float> bossHealth;

		private static ConfigEntry<float> bossDamage;

		private static ConfigEntry<float> bossSpeed;

		private static ConfigEntry<float> bossAttack;

		private static ConfigEntry<float> bossGold;

		private static ConfigEntry<float> bossXp;

		private static ConfigEntry<float> bossDrops;

		private static ConfigEntry<float> bossWeight;

		private static ConfigEntry<float> eliteHealth;

		private static ConfigEntry<float> eliteDamage;

		private static ConfigEntry<float> creditMult;

		private static ConfigEntry<float> spawnSpeed;

		private static ConfigEntry<float> perSeries;

		private static ConfigEntry<float> capMult;

		private static ConfigEntry<float> eliteMult;

		private static ConfigEntry<float> distMult;

		private static ConfigEntry<float> goldMult;

		private static ConfigEntry<float> xpMult;

		private static ConfigEntry<float> goldCap;

		private static ConfigEntry<float> xpCap;

		private static ConfigEntry<float> dropChance;

		private static ConfigEntry<float> dropW1;

		private static ConfigEntry<float> dropW2;

		private static ConfigEntry<float> dropW3;

		private static ConfigEntry<bool> rageOn;

		private static ConfigEntry<bool> rageAnnounce;

		private static ConfigEntry<bool> rageElite;

		private static ConfigEntry<float> rageInterval;

		private static ConfigEntry<float> rageChance;

		private static ConfigEntry<float> rageMax;

		private static ConfigEntry<float> rageLifetime;

		private static ConfigEntry<float> rageHp;

		private static ConfigEntry<float> rageDmg;

		private static ConfigEntry<float> rageSMin;

		private static ConfigEntry<float> rageSMax;

		private static ConfigEntry<float> rageMMin;

		private static ConfigEntry<float> rageMMax;

		private static ConfigEntry<float> rageAMin;

		private static ConfigEntry<float> rageAMax;

		private static ConfigEntry<float> rageGold;

		private static ConfigEntry<float> rageXp;

		private static ConfigEntry<float> rageDropChance;

		private static ConfigEntry<float> rageDropCount;

		private static ConfigEntry<float> rageW1;

		private static ConfigEntry<float> rageW2;

		private static ConfigEntry<float> rageW3;

		private static ConfigEntry<float> shFirst;

		private static ConfigEntry<float> shEvery;

		private static ConfigEntry<float> shMax;

		private static StageRule stHp;

		private static StageRule stDmg;

		private static StageRule stMs;

		private static StageRule stAtk;

		private static StageRule stCredit;

		private static ConfigEntry<KeyboardShortcut> toggleKey;

		private static ConfigEntry<bool> statsOn;

		private static ConfigEntry<bool> debugLog;

		private static ConfigEntry<float> checkInterval;

		private const short MsgRage = 29101;

		private const short MsgCfg = 29102;

		private const short MsgHello = 29103;

		private static NetworkClient registeredClient;

		private static bool serverHandlers;

		private static bool helloPending;

		private static bool syncedActive;

		private static bool cfgDirty;

		private static float helloAt;

		private static float cfgDirtyAt;

		private static int helloSent;

		private static int syncVersion;

		private static int cfgVer = -1;

		private static int cfgGot;

		private static string[] cfgParts;

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

		private static readonly HashSet<ConfigEntryBase> localOnly = new HashSet<ConfigEntryBase>();

		private static readonly Dictionary<DirectorCard, Tracked> bossWeights = new Dictionary<DirectorCard, Tracked>();

		private static string Clean(string k)
		{
			char[] array = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };
			foreach (char c in array)
			{
				k = k.Replace(c.ToString(), "");
			}
			return k;
		}

		private static void Changed()
		{
			if (!suppress)
			{
				ApplyAll();
			}
		}

		private ConfigEntry<float> Num(string section, string key, float def, string desc, float min, float max = 100000f, bool apply = true)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_003c: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			ConfigEntry<float> val = ((BaseUnityPlugin)this).Config.Bind<float>(section, Clean(key), def, desc);
			FloatFieldConfig val2 = new FloatFieldConfig();
			((NumericFieldConfig<float>)val2).Min = min;
			((NumericFieldConfig<float>)val2).Max = max;
			((NumericFieldConfig<float>)val2).FormatString = "{0:0.##}";
			ModSettingsManager.AddOption((BaseOption)new FloatFieldOption(val, val2));
			if (apply)
			{
				val.SettingChanged += delegate
				{
					Changed();
				};
			}
			all.Add((ConfigEntryBase)(object)val);
			return val;
		}

		private ConfigEntry<bool> Toggle(string section, string key, bool def, string desc, bool apply = false)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>(section, Clean(key), def, desc);
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(val));
			if (apply)
			{
				val.SettingChanged += delegate
				{
					Changed();
				};
			}
			all.Add((ConfigEntryBase)(object)val);
			return val;
		}

		private void Awake()
		{
			//IL_0ad0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0af3: Expected O, but got Unknown
			//IL_0c03: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c0d: Expected O, but got Unknown
			//IL_0bf8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bfd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c03: Expected O, but got Unknown
			I = this;
			health = Num("Mobs", "Max Health x", 1f, "Multiplier of base+per-level max health of all mobs", 0f);
			regen = Num("Mobs", "Regen x", 1f, "Multiplier of health regen", 0f);
			shield = Num("Mobs", "Max Shield x", 1f, "Multiplier of max shield", 0f);
			moveSpeed = Num("Mobs", "Move Speed x", 1f, "Multiplier of movement speed", 0f);
			attackSpeed = Num("Mobs", "Attack Speed x", 1f, "Multiplier of attack speed", 0f);
			damage = Num("Mobs", "Damage x", 1f, "Multiplier of damage (this is also how hard mobs hit players)", 0f);
			accel = Num("Mobs", "Acceleration x", 1f, "Multiplier of acceleration", 0f);
			jump = Num("Mobs", "Jump Power x", 1f, "Multiplier of jump power", 0f);
			armorAdd = Num("Mobs", "Armor +", 0f, "Flat armor added to all mobs", -1000f);
			critAdd = Num("Mobs", "Crit Chance +", 0f, "Flat crit chance (%) added to all mobs", 0f);
			sizeMult = Num("Mobs", "Size x", 1f, "Model size multiplier of all mobs (visual, host-side)", 0.1f, 50f);
			timeScale = Num("Mobs", "Growth per minute (%)", 0f, "Mobs get this % more health and damage for every minute of the run, on top of normal difficulty", 0f);
			affectSurvivors = Toggle("Mobs", "Also affect survivors", def: false, "If off, only non-survivor bodies are changed", apply: true);
			bossHealth = Num("Bosses", "Boss Health x", 1f, "Extra multiplier for champions/bosses (stacks with Mobs)", 0f);
			bossDamage = Num("Bosses", "Boss Damage x", 1f, "Extra multiplier for bosses", 0f);
			bossSpeed = Num("Bosses", "Boss Move Speed x", 1f, "Extra multiplier for bosses", 0f);
			bossAttack = Num("Bosses", "Boss Attack Speed x", 1f, "Extra multiplier for bosses", 0f);
			eliteHealth = Num("Elites", "Elite Health x", 1f, "Extra health multiplier for elite mobs (stacks with Mobs)", 0f);
			eliteDamage = Num("Elites", "Elite Damage x", 1f, "Extra damage multiplier for elite mobs (stacks with Mobs)", 0f);
			eliteMult = Num("Elites", "Elite chance x", 1f, "Higher = more elites (director elite bias is divided by this)", 0.01f);
			creditMult = Num("Spawning", "Spawn credits x", 1f, "Director gets credits faster: more / stronger mobs", 0f);
			spawnSpeed = Num("Spawning", "Spawn speed x", 1f, "Pauses between spawn waves are divided by this", 0.01f);
			perSeries = Num("Spawning", "Mobs per wave x", 1f, "Max mobs spawned in a row before the director pauses", 0.1f);
			capMult = Num("Spawning", "Mob cap x", 1f, "Limit of simultaneously alive monsters (default ~40)", 0.1f);
			distMult = Num("Spawning", "Spawn distance x", 1f, "Lower = mobs spawn closer to players, higher = farther", 0.1f, 100f);
			goldMult = Num("Rewards and drops", "Gold from mobs x", 1f, "Extra gold for kills. Values below 1 do nothing", 1f, 100000f, apply: false);
			xpMult = Num("Rewards and drops", "XP from mobs x", 1f, "Extra experience for kills. Values below 1 do nothing", 1f, 100000f, apply: false);
			goldCap = Num("Rewards and drops", "Max extra gold per kill (0 for no limit)", 0f, "Safety cap for the bonus gold of one kill", 0f, 100000000f, apply: false);
			xpCap = Num("Rewards and drops", "Max extra XP per kill (0 for no limit)", 0f, "Safety cap for the bonus experience of one kill", 0f, 100000000f, apply: false);
			dropChance = Num("Rewards and drops", "Item drop chance per kill (%)", 0f, "Every mob kill can drop an item", 0f, 100f, apply: false);
			dropW1 = Num("Rewards and drops", "Drop weight: white", 80f, "Relative chance of white items", 0f, 100000f, apply: false);
			dropW2 = Num("Rewards and drops", "Drop weight: green", 18f, "Relative chance of green items", 0f, 100000f, apply: false);
			dropW3 = Num("Rewards and drops", "Drop weight: red", 2f, "Relative chance of red items", 0f, 100000f, apply: false);
			bossWeight = Num("Bosses", "Boss spawn weight x", 1f, "How often the director picks bosses (champions) next to normal mobs on this stage. 2 means about twice as often. 1 means unchanged", 0f);
			bossGold = Num("Bosses", "Boss gold x", 1f, "Extra gold multiplier for killing bosses (champions)", 1f, 100000f, apply: false);
			bossXp = Num("Bosses", "Boss XP x", 1f, "Extra experience multiplier for killing bosses", 1f, 100000f, apply: false);
			bossDrops = Num("Bosses", "Boss extra drops", 0f, "Additional items dropped by a boss (uses the rage drop weights)", 0f, 100f, apply: false);
			rageOn = Toggle("Rage mob", "Enable rage mob", def: false, "Every interval a random mob becomes a rage mob");
			rageAnnounce = Toggle("Rage mob", "Announce in chat", def: true, "Chat message when a rage mob appears");
			rageElite = Toggle("Rage mob", "Only elites", def: true, "Only an elite mob can become the rage mob");
			rageInterval = Num("Rage mob", "Interval (seconds)", 120f, "How often a rage mob is attempted", 1f, 100000f, apply: false);
			rageChance = Num("Rage mob", "Chance per attempt (%)", 100f, "Chance that an attempt succeeds", 0f, 100f, apply: false);
			rageMax = Num("Rage mob", "Max alive at once", 1f, "Limit of rage mobs on the map", 1f, 1000f, apply: false);
			rageLifetime = Num("Rage mob", "Lifetime (seconds, 0 for unlimited)", 90f, "The rage mob disappears without rewards after this many seconds", 0f, 100000f, apply: false);
			rageHp = Num("Rage mob", "Health x", 3f, "Health multiplier", 0f, 100000f, apply: false);
			rageDmg = Num("Rage mob", "Damage x", 1.5f, "Damage multiplier", 0f, 100000f, apply: false);
			rageSMin = Num("Rage mob", "Size min", 1.5f, "Random size range (min)", 0.1f, 50f, apply: false);
			rageSMax = Num("Rage mob", "Size max", 3f, "Random size range (max)", 0.1f, 50f, apply: false);
			rageMMin = Num("Rage mob", "Move speed min", 1.2f, "Random move speed multiplier (min)", 0f, 100f, apply: false);
			rageMMax = Num("Rage mob", "Move speed max", 2.5f, "Random move speed multiplier (max)", 0f, 100f, apply: false);
			rageAMin = Num("Rage mob", "Attack speed min", 1.2f, "Random attack speed multiplier (min)", 0f, 100f, apply: false);
			rageAMax = Num("Rage mob", "Attack speed max", 2.5f, "Random attack speed multiplier (max)", 0f, 100f, apply: false);
			rageGold = Num("Rage mob", "Gold x", 3f, "Extra gold multiplier for the rage mob, on top of normal", 1f, 100000f, apply: false);
			rageXp = Num("Rage mob", "XP x", 3f, "Extra experience multiplier for the rage mob, on top of normal", 1f, 100000f, apply: false);
			rageDropChance = Num("Rage mob", "Drop chance (%)", 100f, "Chance that rage mob drops items", 0f, 100f, apply: false);
			rageDropCount = Num("Rage mob", "Drop count", 1f, "How many items drop", 1f, 100f, apply: false);
			rageW1 = Num("Rage mob", "Drop weight: white", 40f, "Relative chance of white items", 0f, 100000f, apply: false);
			rageW2 = Num("Rage mob", "Drop weight: green", 45f, "Relative chance of green items", 0f, 100000f, apply: false);
			rageW3 = Num("Rage mob", "Drop weight: red", 15f, "Relative chance of red items", 0f, 100000f, apply: false);
			shFirst = Num("Stages", "First upgrade at stage", 5f, "The first upgrade happens when this stage starts. Stages before it are not changed", 1f, 1000f);
			shEvery = Num("Stages", "Upgrade every (stages)", 5f, "After the first upgrade, mobs are upgraded again every this many stages", 1f, 1000f);
			shMax = Num("Stages", "Max upgrades (0 for unlimited)", 0f, "Stop upgrading after this many upgrades", 0f, 1000f);
			stHp = MakeRule("Stages", "Health", 1.5f, "Health", apply: true);
			stDmg = MakeRule("Stages", "Damage", 1f, "Damage", apply: true);
			stMs = MakeRule("Stages", "Move speed", 1f, "Move speed", apply: true);
			stAtk = MakeRule("Stages", "Attack speed", 1f, "Attack speed", apply: true);
			stCredit = MakeRule("Stages", "Spawn credits", 1f, "Spawn credits (more credits means more mobs)", apply: false);
			statsOn = Toggle("General", "Run summary in chat", def: true, "Prints statistics when the run ends");
			toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Toggle key", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Enable or disable the whole mod");
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(toggleKey));
			all.Add((ConfigEntryBase)(object)toggleKey);
			Run.onRunStartGlobal += delegate
			{
				stats = new RunStats();
			};
			Run.onClientGameOverGlobal += delegate
			{
				PrintStats();
			};
			checkInterval = Num("General", "Check interval (seconds)", 1f, "How often director settings and time growth are re-applied", 0.1f, 60f, apply: false);
			debugLog = Toggle("General", "Debug log", def: false, "Writes director info to the BepInEx console every 5 seconds");
			localOnly.Add((ConfigEntryBase)(object)toggleKey);
			localOnly.Add((ConfigEntryBase)(object)debugLog);
			localOnly.Add((ConfigEntryBase)(object)statsOn);
			localOnly.Add((ConfigEntryBase)(object)checkInterval);
			object obj = <>c.<>9__120_2;
			if (obj == null)
			{
				UnityAction val = delegate
				{
					ResetAll();
					ApplyAll();
				};
				<>c.<>9__120_2 = val;
				obj = (object)val;
			}
			ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("Reset to vanilla", "General", "Set every setting of this mod back to the default (vanilla) value", "Reset", (UnityAction)obj));
			LoadIcon();
			ModSettingsManager.SetModDescription("Global multipliers for mob stats, spawning, drops, stage scaling and a rage mob (live).");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				if (!suppress)
				{
					cfgDirty = true;
					cfgDirtyAt = Time.unscaledTime;
				}
			};
			Application.quitting += RestoreLocal;
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(OnGameLoaded));
		}

		private StageRule MakeRule(string sec, string n, float mult, string what, bool apply)
		{
			return new StageRule
			{
				mult = Num(sec, n + ": x per upgrade", mult, what + " is multiplied by this at every upgrade (1.5 means +50% each time, and it stacks, 1 means no change)", 0f, 100000f, apply),
				first = shFirst,
				every = shEvery,
				max = shMax
			};
		}

		private void LoadIcon()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
				string path = Path.Combine(directoryName, "icon.png");
				if (!File.Exists(path))
				{
					path = Path.Combine(Directory.GetParent(directoryName).FullName, "icon.png");
				}
				if (!File.Exists(path))
				{
					LogWarn("icon.png was not found next to the dll, the menu will show no picture");
					return;
				}
				Texture2D val = new Texture2D(2, 2);
				if (ImageConversion.LoadImage(val, File.ReadAllBytes(path)))
				{
					ModSettingsManager.SetModIcon(Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)));
				}
			}
			catch (Exception ex)
			{
				LogWarn("Icon failed: " + ex.Message);
			}
		}

		private static void Say(string m)
		{
			if ((Object)(object)I != (Object)null)
			{
				((BaseUnityPlugin)I).Logger.LogInfo((object)("[MobStatsUI] " + m));
			}
			try
			{
				Chat.AddMessage("<color=#80ff80>[MobStatsUI] " + m + "</color>");
			}
			catch
			{
			}
		}

		private static void LogWarn(string m)
		{
			if ((Object)(object)I != (Object)null)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("[MobStatsUI] " + m));
			}
		}

		private static void Broadcast(string msg)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			try
			{
				if (NetworkServer.active)
				{
					Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
					{
						baseToken = msg
					});
				}
				else
				{
					Chat.AddMessage(msg);
				}
			}
			catch
			{
			}
		}

		private static void ResetAll()
		{
			suppress = true;
			foreach (ConfigEntryBase item in all)
			{
				item.BoxedValue = item.DefaultValue;
			}
			suppress = false;
		}

		private static void OnGameLoaded()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			foreach (SurvivorDef allSurvivorDef in SurvivorCatalog.allSurvivorDefs)
			{
				if (!((Object)(object)allSurvivorDef == (Object)null) && !((Object)(object)allSurvivorDef.bodyPrefab == (Object)null))
				{
					CharacterBody component = allSurvivorDef.bodyPrefab.GetComponent<CharacterBody>();
					if (Object.op_Implicit((Object)(object)component))
					{
						survivors.Add(component.bodyIndex);
					}
				}
			}
			for (int i = 0; i < BodyCatalog.bodyCount; i++)
			{
				BodyIndex val = (BodyIndex)i;
				CharacterBody bodyPrefabBodyComponent = BodyCatalog.GetBodyPrefabBodyComponent(val);
				if (Object.op_Implicit((Object)(object)bodyPrefabBodyComponent))
				{
					snaps[val] = new Snap
					{
						baseMaxHealth = bodyPrefabBodyComponent.baseMaxHealth,
						levelMaxHealth = bodyPrefabBodyComponent.levelMaxHealth,
						baseRegen = bodyPrefabBodyComponent.baseRegen,
						levelRegen = bodyPrefabBodyComponent.levelRegen,
						baseMaxShield = bodyPrefabBodyComponent.baseMaxShield,
						levelMaxShield = bodyPrefabBodyComponent.levelMaxShield,
						baseMoveSpeed = bodyPrefabBodyComponent.baseMoveSpeed,
						levelMoveSpeed = bodyPrefabBodyComponent.levelMoveSpeed,
						baseAttackSpeed = bodyPrefabBodyComponent.baseAttackSpeed,
						levelAttackSpeed = bodyPrefabBodyComponent.levelAttackSpeed,
						baseDamage = bodyPrefabBodyComponent.baseDamage,
						levelDamage = bodyPrefabBodyComponent.levelDamage,
						baseArmor = bodyPrefabBodyComponent.baseArmor,
						baseCrit = bodyPrefabBodyComponent.baseCrit,
						baseAcceleration = bodyPrefabBodyComponent.baseAcceleration,
						baseJumpPower = bodyPrefabBodyComponent.baseJumpPower,
						levelJumpPower = bodyPrefabBodyComponent.levelJumpPower,
						champion = bodyPrefabBodyComponent.isChampion
					};
				}
			}
			TeamDef teamDef = TeamCatalog.GetTeamDef((TeamIndex)2);
			if (teamDef != null)
			{
				origMonsterCap = teamDef.softCharacterLimit;
			}
			CharacterBody.onBodyStartGlobal += OnBodyStart;
			GlobalEventManager.onCharacterDeathGlobal += OnDeath;
			ready = true;
			ApplyAll();
		}

		private static bool IsEnemy(CharacterBody b)
		{
			//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
			if (!Object.op_Implicit((Object)(object)b) || !Object.op_Implicit((Object)(object)b.teamComponent))
			{
				return false;
			}
			TeamIndex teamIndex = b.teamComponent.teamIndex;
			if ((int)teamIndex != 1)
			{
				return (int)teamIndex != -1;
			}
			return false;
		}

		private static void Set(CharacterBody b, Snap s, bool neutral, Rage rg)
		{
			if (!modEnabled)
			{
				neutral = true;
				rg = null;
			}
			float num = 1f;
			float num2 = 1f;
			float num3 = 1f;
			float num4 = 1f;
			float num5 = 1f;
			float num6 = 1f;
			float num7 = 1f;
			float num8 = 1f;
			float num9 = 0f;
			float num10 = 0f;
			if (!neutral)
			{
				num = health.Value * timeFactor;
				num2 = regen.Value;
				num3 = shield.Value;
				num4 = moveSpeed.Value;
				num5 = attackSpeed.Value;
				num6 = damage.Value * timeFactor;
				num7 = accel.Value;
				num8 = jump.Value;
				num9 = armorAdd.Value;
				num10 = critAdd.Value;
				if (s.champion)
				{
					num *= bossHealth.Value;
					num6 *= bossDamage.Value;
					num4 *= bossSpeed.Value;
					num5 *= bossAttack.Value;
				}
				if (b.isElite)
				{
					num *= eliteHealth.Value;
					num6 *= eliteDamage.Value;
				}
				num *= StageHp();
				num6 *= StageDmg();
				num4 *= StageMul(stMs);
				num5 *= StageMul(stAtk);
			}
			if (rg != null)
			{
				num *= rg.hp;
				num6 *= rg.dmg;
				num4 *= rg.ms;
				num5 *= rg.atk;
			}
			b.baseMaxHealth = s.baseMaxHealth * num;
			b.levelMaxHealth = s.levelMaxHealth * num;
			b.baseRegen = s.baseRegen * num2;
			b.levelRegen = s.levelRegen * num2;
			b.baseMaxShield = s.baseMaxShield * num3;
			b.levelMaxShield = s.levelMaxShield * num3;
			b.baseMoveSpeed = s.baseMoveSpeed * num4;
			b.levelMoveSpeed = s.levelMoveSpeed * num4;
			b.baseAttackSpeed = s.baseAttackSpeed * num5;
			b.levelAttackSpeed = s.levelAttackSpeed * num5;
			b.baseDamage = s.baseDamage * num6;
			b.levelDamage = s.levelDamage * num6;
			b.baseAcceleration = s.baseAcceleration * num7;
			b.baseJumpPower = s.baseJumpPower * num8;
			b.levelJumpPower = s.levelJumpPower * num8;
			b.baseArmor = s.baseArmor + num9;
			b.baseCrit = s.baseCrit + num10;
		}

		private static void ApplyScale(CharacterBody b, bool skip)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (Object.op_Implicit((Object)(object)b.modelLocator) ? b.modelLocator.modelTransform : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				if (!origScale.TryGetValue(b, out var value))
				{
					value = val.localScale;
					origScale[b] = value;
				}
				float num = ((skip || !modEnabled) ? 1f : sizeMult.Value);
				if (modEnabled && rages.TryGetValue(b, out var value2))
				{
					num *= value2.size;
				}
				val.localScale = value * num;
			}
		}

		private static void ApplyBody(CharacterBody body)
		{
			//IL_000e: 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)
			if (Object.op_Implicit((Object)(object)body) && snaps.TryGetValue(body.bodyIndex, out var value))
			{
				bool neutral = survivors.Contains(body.bodyIndex) && !affectSurvivors.Value;
				rages.TryGetValue(body, out var value2);
				Set(body, value, neutral, value2);
				if (IsEnemy(body))
				{
					ApplyScale(body, skip: false);
				}
				body.RecalculateStats();
			}
		}

		private static void ApplyAll()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!ready)
			{
				return;
			}
			foreach (KeyValuePair<BodyIndex, Snap> snap in snaps)
			{
				CharacterBody bodyPrefabBodyComponent = BodyCatalog.GetBodyPrefabBodyComponent(snap.Key);
				if (Object.op_Implicit((Object)(object)bodyPrefabBodyComponent))
				{
					bool neutral = survivors.Contains(snap.Key) && !affectSurvivors.Value;
					Set(bodyPrefabBodyComponent, snap.Value, neutral, null);
				}
			}
			CharacterBody[] array = Object.FindObjectsOfType<CharacterBody>();
			for (int i = 0; i < array.Length; i++)
			{
				ApplyBody(array[i]);
			}
			TeamDef teamDef = TeamCatalog.GetTeamDef((TeamIndex)2);
			if (teamDef != null && origMonsterCap > 0)
			{
				teamDef.softCharacterLimit = Mathf.Max(1, Mathf.RoundToInt((float)origMonsterCap * (modEnabled ? capMult.Value : 1f)));
			}
			ApplyDirectors();
		}

		private static bool IsBossCard(DirectorCard dc)
		{
			if (dc == null || !Object.op_Implicit((Object)(object)dc.spawnCard) || !Object.op_Implicit((Object)(object)dc.spawnCard.prefab))
			{
				return false;
			}
			CharacterMaster component = dc.spawnCard.prefab.GetComponent<CharacterMaster>();
			CharacterBody val = ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.bodyPrefab)) ? component.bodyPrefab.GetComponent<CharacterBody>() : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return val.isChampion;
			}
			return false;
		}

		private static void ApplyBossWeight()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			ClassicStageInfo instance = ClassicStageInfo.instance;
			WeightedSelection<DirectorCard> val = (((Object)(object)instance != (Object)null) ? instance.monsterSelection : null);
			if (val == null)
			{
				return;
			}
			float mult = (modEnabled ? Mathf.Max(0f, bossWeight.Value) : 1f);
			for (int i = 0; i < val.Count; i++)
			{
				ChoiceInfo<DirectorCard> choice = val.GetChoice(i);
				if (IsBossCard(choice.value))
				{
					if (!bossWeights.TryGetValue(choice.value, out var value))
					{
						value = (bossWeights[choice.value] = new Tracked());
					}
					float num = value.Get(choice.weight, (float o) => o * mult);
					if (!Mathf.Approximately(num, choice.weight))
					{
						val.ModifyChoiceWeight(i, num);
					}
				}
			}
		}

		private static void ApplyDirectors()
		{
			try
			{
				ApplyBossWeight();
			}
			catch (Exception ex)
			{
				LogWarn("Boss weight failed: " + ex.Message);
			}
			bool flag = modEnabled;
			float cm = (flag ? (creditMult.Value * StageCredit()) : 1f);
			float sp = (flag ? Mathf.Max(spawnSpeed.Value, 0.01f) : 1f);
			float dist = (flag ? Mathf.Max(distMult.Value, 0.05f) : 1f);
			float ps = (flag ? perSeries.Value : 1f);
			float el = (flag ? Mathf.Max(eliteMult.Value, 0.01f) : 1f);
			CombatDirector[] array = Object.FindObjectsOfType<CombatDirector>();
			foreach (CombatDirector val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					int instanceID = ((Object)val).GetInstanceID();
					if (!dirStates.TryGetValue(instanceID, out var value))
					{
						value = (dirStates[instanceID] = new DirState());
					}
					val.creditMultiplier = value.credit.Get(val.creditMultiplier, (float o) => o * cm);
					val.minSeriesSpawnInterval = value.minS.Get(val.minSeriesSpawnInterval, (float o) => o / sp);
					val.maxSeriesSpawnInterval = value.maxS.Get(val.maxSeriesSpawnInterval, (float o) => o / sp);
					val.minRerollSpawnInterval = value.minR.Get(val.minRerollSpawnInterval, (float o) => o / sp);
					val.maxRerollSpawnInterval = value.maxR.Get(val.maxRerollSpawnInterval, (float o) => o / sp);
					val.maximumNumberToSpawnBeforeSkipping = Mathf.Max(1, Mathf.RoundToInt(value.skip.Get(val.maximumNumberToSpawnBeforeSkipping, (float o) => o * ps)));
					val.eliteBias = value.elite.Get(val.eliteBias, (float o) => o / el);
					val.spawnDistanceMultiplier = value.dist.Get(val.spawnDistanceMultiplier, (float o) => o * dist);
				}
			}
		}

		private static void OnBodyStart(CharacterBody body)
		{
			if (ready && Object.op_Implicit((Object)(object)body) && !((Object)(object)I == (Object)null))
			{
				((MonoBehaviour)I).StartCoroutine(Delayed(body));
			}
		}

		private static IEnumerator Delayed(CharacterBody body)
		{
			yield return (object)new WaitForSeconds(0.25f);
			if (Object.op_Implicit((Object)(object)body) && IsEnemy(body))
			{
				ApplyBody(body);
			}
		}

		private static void OnDeath(DamageReport r)
		{
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || !ready || !modEnabled || r == null)
			{
				return;
			}
			CharacterBody victimBody = r.victimBody;
			if (!Object.op_Implicit((Object)(object)victimBody) || !IsEnemy(victimBody))
			{
				return;
			}
			if (expired.Remove(victimBody))
			{
				rages.Remove(victimBody);
				stats.expired++;
				return;
			}
			stats.kills++;
			rages.TryGetValue(victimBody, out var value);
			bool flag = value != null;
			bool isChampion = victimBody.isChampion;
			float num = goldMult.Value * (flag ? rageGold.Value : 1f) * (isChampion ? bossGold.Value : 1f);
			float num2 = xpMult.Value * (flag ? rageXp.Value : 1f) * (isChampion ? bossXp.Value : 1f);
			if (num > 1.001f || num2 > 1.001f)
			{
				DeathRewards component = ((Component)victimBody).GetComponent<DeathRewards>();
				if (Object.op_Implicit((Object)(object)component))
				{
					float num3 = ((num > 1f) ? ((float)component.goldReward * (num - 1f)) : 0f);
					float num4 = ((num2 > 1f) ? ((float)component.expReward * (num2 - 1f)) : 0f);
					if (goldCap.Value > 0f)
					{
						num3 = Mathf.Min(num3, goldCap.Value);
					}
					if (xpCap.Value > 0f)
					{
						num4 = Mathf.Min(num4, xpCap.Value);
					}
					if (num3 >= 1f && Object.op_Implicit((Object)(object)r.attackerMaster))
					{
						r.attackerMaster.GiveMoney((uint)num3);
						stats.gold += num3;
					}
					if (num4 >= 1f)
					{
						stats.xp += num4;
						if (Object.op_Implicit((Object)(object)r.attackerBody) && Object.op_Implicit((Object)(object)ExperienceManager.instance))
						{
							ExperienceManager.instance.AwardExperience(victimBody.corePosition, r.attackerBody, (ulong)num4);
						}
						else if (Object.op_Implicit((Object)(object)TeamManager.instance))
						{
							TeamManager.instance.GiveTeamExperience((TeamIndex)1, (ulong)num4);
						}
					}
				}
			}
			if (flag)
			{
				stats.ragesKilled++;
				if (Random.value * 100f < rageDropChance.Value)
				{
					for (int i = 0; i < Mathf.RoundToInt(rageDropCount.Value); i++)
					{
						DropOne(victimBody.corePosition, rageW1.Value, rageW2.Value, rageW3.Value);
					}
				}
				rages.Remove(victimBody);
			}
			else if (dropChance.Value > 0f && Random.value * 100f < dropChance.Value)
			{
				DropOne(victimBody.corePosition, dropW1.Value, dropW2.Value, dropW3.Value);
			}
			if (isChampion)
			{
				for (int j = 0; j < Mathf.RoundToInt(bossDrops.Value); j++)
				{
					DropOne(victimBody.corePosition, rageW1.Value, rageW2.Value, rageW3.Value);
				}
			}
		}

		private static void DropOne(Vector3 pos, float w1, float w2, float w3)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Run instance = Run.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			float num = w1 + w2 + w3;
			if (!(num <= 0f))
			{
				float num2 = Random.value * num;
				List<PickupIndex> list = ((num2 < w1) ? instance.availableTier1DropList : ((num2 < w1 + w2) ? instance.availableTier2DropList : instance.availableTier3DropList));
				if (list != null && list.Count != 0)
				{
					PickupIndex val = list[Random.Range(0, list.Count)];
					Vector3 val2 = Vector3.up * 20f + new Vector3(Random.Range(-5f, 5f), 0f, Random.Range(-5f, 5f));
					PickupDropletController.CreatePickupDroplet(val, pos + Vector3.up * 1.5f, val2);
					stats.items++;
				}
			}
		}

		private void Update()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!ready)
			{
				return;
			}
			CheckNet();
			KeyboardShortcut value = toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				modEnabled = !modEnabled;
				ApplyAll();
				Say(modEnabled ? "Mod ENABLED" : "Mod DISABLED");
			}
			if ((Object)(object)Run.instance == (Object)null)
			{
				timeFactor = 1f;
				lastStage = -1;
				return;
			}
			dirTimer += Time.unscaledDeltaTime;
			if (dirTimer >= Mathf.Max(0.1f, checkInterval.Value))
			{
				dirTimer = 0f;
				ApplyDirectors();
				if (debugLog.Value && (logTimer += Mathf.Max(0.1f, checkInterval.Value)) >= 5f)
				{
					logTimer = 0f;
					LogDebug();
				}
				float num = 1f + timeScale.Value / 100f * (Run.instance.GetRunStopwatch() / 60f);
				num = Mathf.Round(num * 20f) / 20f;
				if (!Mathf.Approximately(num, timeFactor))
				{
					timeFactor = num;
					ApplyAll();
				}
				int num2 = StageNumber();
				if (num2 != lastStage)
				{
					lastStage = num2;
					ApplyAll();
				}
				if (!NetworkServer.active)
				{
					CleanupRages();
				}
				CheckElites();
				if (NetworkServer.active && modEnabled)
				{
					CheckRageLifetime();
				}
			}
			if (rageOn.Value && modEnabled && NetworkServer.active)
			{
				rageTimer += Time.deltaTime;
				float num3 = Mathf.Max(1f, rageInterval.Value);
				if (rageTimer >= num3)
				{
					rageTimer = (TryRage() ? 0f : Mathf.Max(0f, num3 - 0.5f));
				}
			}
		}

		private void LogDebug()
		{
			CombatDirector[] array = Object.FindObjectsOfType<CombatDirector>();
			int count = TeamComponent.GetTeamMembers((TeamIndex)2).Count;
			TeamDef teamDef = TeamCatalog.GetTeamDef((TeamIndex)2);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"[MobStatsUI] directors={array.Length} monstersAlive={count} cap={teamDef?.softCharacterLimit ?? (-1)}");
			CombatDirector[] array2 = array;
			foreach (CombatDirector val in array2)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"  {((Object)val).name}: credit={val.monsterCredit:0} mult={val.creditMultiplier} series={val.minSeriesSpawnInterval}-{val.maxSeriesSpawnInterval} skip={val.maximumNumberToSpawnBeforeSkipping} elite={val.eliteBias} dist={val.spawnDistanceMultiplier}");
			}
		}

		private static int StageNumber()
		{
			if (!Object.op_Implicit((Object)(object)Run.instance))
			{
				return 1;
			}
			return Run.instance.stageClearCount + 1;
		}

		private static float StageMul(StageRule r)
		{
			int num = StageNumber();
			int num2 = Mathf.Max(1, Mathf.RoundToInt(r.first.Value));
			if (num < num2)
			{
				return 1f;
			}
			int num3 = 1 + (num - num2) / Mathf.Max(1, Mathf.RoundToInt(r.every.Value));
			int num4 = Mathf.RoundToInt(r.max.Value);
			if (num4 > 0)
			{
				num3 = Mathf.Min(num3, num4);
			}
			return Mathf.Pow(Mathf.Max(0f, r.mult.Value), (float)num3);
		}

		private static float StageHp()
		{
			return StageMul(stHp);
		}

		private static float StageDmg()
		{
			return StageMul(stDmg);
		}

		private static float StageCredit()
		{
			return StageMul(stCredit);
		}

		private static void CheckElites()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			if (!modEnabled)
			{
				return;
			}
			eliteDone.RemoveWhere((CharacterBody b) => !Object.op_Implicit((Object)(object)b));
			CharacterBody[] array = Object.FindObjectsOfType<CharacterBody>();
			foreach (CharacterBody val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && IsEnemy(val) && val.isElite && !eliteDone.Contains(val))
				{
					eliteDone.Add(val);
					HealthComponent healthComponent = val.healthComponent;
					bool num2 = Object.op_Implicit((Object)(object)healthComponent) && healthComponent.fullHealth > 0f && healthComponent.health / healthComponent.fullHealth > 0.99f;
					ApplyBody(val);
					if (num2 && NetworkServer.active && healthComponent.alive)
					{
						healthComponent.HealFraction(1f, default(ProcChainMask));
					}
				}
			}
		}

		private static void CheckRageLifetime()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (rageLifetime.Value <= 0f)
			{
				return;
			}
			float time = Time.time;
			foreach (KeyValuePair<CharacterBody, Rage> item in rages.ToList())
			{
				CharacterBody key = item.Key;
				if (Object.op_Implicit((Object)(object)key) && Object.op_Implicit((Object)(object)key.healthComponent) && key.healthComponent.alive && !(time - item.Value.born < rageLifetime.Value))
				{
					expired.Add(key);
					key.healthComponent.Suicide((GameObject)null, (GameObject)null, default(DamageTypeCombo));
				}
			}
		}

		private static void PrintStats()
		{
			if (statsOn.Value && NetworkServer.active)
			{
				Broadcast("<color=#80d0ff>=== MobStatsUI run summary ===</color>");
				Broadcast("Kills: " + stats.kills + " | Rage mobs: " + stats.ragesSpawned + " spawned, " + stats.ragesKilled + " killed, " + stats.expired + " expired");
				Broadcast("Bonus: " + stats.gold.ToString("0") + " gold, " + stats.xp.ToString("0") + " XP, " + stats.items + " items");
			}
		}

		private static float Rnd(float a, float b)
		{
			return Random.Range(Mathf.Min(a, b), Mathf.Max(a, b));
		}

		private static bool TryRage()
		{
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			foreach (CharacterBody item in rages.Keys.Where((CharacterBody k) => !Object.op_Implicit((Object)(object)k) || !Object.op_Implicit((Object)(object)k.healthComponent) || !k.healthComponent.alive).ToList())
			{
				rages.Remove(item);
			}
			foreach (CharacterBody item2 in origScale.Keys.Where((CharacterBody k) => !Object.op_Implicit((Object)(object)k)).ToList())
			{
				origScale.Remove(item2);
			}
			if (rages.Count >= Mathf.RoundToInt(rageMax.Value))
			{
				return true;
			}
			if (Random.value * 100f >= rageChance.Value)
			{
				return true;
			}
			List<CharacterBody> list = (from b in Object.FindObjectsOfType<CharacterBody>()
				where Object.op_Implicit((Object)(object)b) && IsEnemy(b) && Object.op_Implicit((Object)(object)b.healthComponent) && b.healthComponent.alive && !rages.ContainsKey(b) && snaps.ContainsKey(b.bodyIndex) && !b.isChampion && (!rageElite.Value || b.isElite)
				select b).ToList();
			if (list.Count == 0)
			{
				return false;
			}
			CharacterBody val = list[Random.Range(0, list.Count)];
			Rage rage = new Rage
			{
				size = Rnd(rageSMin.Value, rageSMax.Value),
				ms = Rnd(rageMMin.Value, rageMMax.Value),
				atk = Rnd(rageAMin.Value, rageAMax.Value),
				hp = rageHp.Value,
				dmg = rageDmg.Value,
				born = Time.time
			};
			rages[val] = rage;
			ApplyBody(val);
			val.healthComponent.HealFraction(1f, default(ProcChainMask));
			stats.ragesSpawned++;
			if (rageAnnounce.Value)
			{
				Broadcast("<color=#ff3030>A RAGE MOB has appeared!</color>");
			}
			SendRage(val, rage);
			return true;
		}

		private static void CheckNet()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			if (NetworkServer.active)
			{
				if (!serverHandlers)
				{
					object obj = <>O.<4>__OnHello;
					if (obj == null)
					{
						NetworkMessageDelegate val = OnHello;
						<>O.<4>__OnHello = val;
						obj = (object)val;
					}
					NetworkServer.RegisterHandler((short)29103, (NetworkMessageDelegate)obj);
					serverHandlers = true;
				}
				if (syncedActive)
				{
					RestoreLocal();
				}
				if (cfgDirty && Time.unscaledTime - cfgDirtyAt > 0.75f)
				{
					cfgDirty = false;
					SendConfig(null);
				}
			}
			else
			{
				serverHandlers = false;
			}
			if (!NetworkClient.active)
			{
				registeredClient = null;
				helloPending = false;
				if (syncedActive)
				{
					RestoreLocal();
				}
			}
			else
			{
				if (NetworkClient.allClients.Count == 0)
				{
					return;
				}
				NetworkClient val2 = NetworkClient.allClients[0];
				if (val2 == null)
				{
					return;
				}
				if (val2 != registeredClient)
				{
					object obj2 = <>O.<5>__OnRageMsg;
					if (obj2 == null)
					{
						NetworkMessageDelegate val3 = OnRageMsg;
						<>O.<5>__OnRageMsg = val3;
						obj2 = (object)val3;
					}
					val2.RegisterHandler((short)29101, (NetworkMessageDelegate)obj2);
					object obj3 = <>O.<6>__OnCfgMsg;
					if (obj3 == null)
					{
						NetworkMessageDelegate val4 = OnCfgMsg;
						<>O.<6>__OnCfgMsg = val4;
						obj3 = (object)val4;
					}
					val2.RegisterHandler((short)29102, (NetworkMessageDelegate)obj3);
					registeredClient = val2;
					helloPending = true;
					helloSent = 0;
					helloAt = 0f;
				}
				if (!helloPending || !val2.isConnected)
				{
					return;
				}
				if (helloAt <= 0f)
				{
					helloAt = Time.unscaledTime + 3f;
				}
				else if (Time.unscaledTime >= helloAt)
				{
					val2.Send((short)29103, (MessageBase)(object)new HelloMsg());
					helloSent++;
					if (helloSent >= 2)
					{
						helloPending = false;
					}
					else
					{
						helloAt = Time.unscaledTime + 6f;
					}
				}
			}
		}

		private static void OnHello(NetworkMessage netMsg)
		{
			if (netMsg != null && netMsg.conn != null)
			{
				SendConfig(netMsg.conn);
				SendAllRages(netMsg.conn);
			}
		}

		private static void SendAllRages(NetworkConnection conn)
		{
			foreach (KeyValuePair<CharacterBody, Rage> item in rages.ToList())
			{
				if (Object.op_Implicit((Object)(object)item.Key) && Object.op_Implicit((Object)(object)item.Key.healthComponent) && item.Key.healthComponent.alive)
				{
					SendRage(item.Key, item.Value, conn);
				}
			}
		}

		private static string SerializeSynced()
		{
			IEnumerable<string> values = from e in all
				where !localOnly.Contains(e)
				select Uri.EscapeDataString(e.Definition.Section + "/" + e.Definition.Key) + "=" + Uri.EscapeDataString(e.GetSerializedValue());
			return string.Join("|", values);
		}

		private static void SendConfig(NetworkConnection conn)
		{
			if (!NetworkServer.active)
			{
				return;
			}
			string text = SerializeSynced();
			int num = Mathf.Max(1, (text.Length + 600 - 1) / 600);
			int ver = ++syncVersion;
			for (int i = 0; i < num; i++)
			{
				int num2 = i * 600;
				ConfigChunkMsg configChunkMsg = new ConfigChunkMsg
				{
					ver = ver,
					idx = i,
					total = num,
					part = ((num2 < text.Length) ? text.Substring(num2, Mathf.Min(600, text.Length - num2)) : "")
				};
				if (conn != null)
				{
					conn.Send((short)29102, (MessageBase)(object)configChunkMsg);
				}
				else
				{
					NetworkServer.SendToAll((short)29102, (MessageBase)(object)configChunkMsg);
				}
			}
		}

		private static void OnCfgMsg(NetworkMessage netMsg)
		{
			ConfigChunkMsg configChunkMsg = netMsg.ReadMessage<ConfigChunkMsg>();
			if (NetworkServer.active || !ready)
			{
				return;
			}
			if (configChunkMsg.ver != cfgVer || cfgParts == null || cfgParts.Length != configChunkMsg.total)
			{
				cfgVer = configChunkMsg.ver;
				cfgParts = new string[Mathf.Max(1, configChunkMsg.total)];
				cfgGot = 0;
			}
			if (configChunkMsg.idx >= 0 && configChunkMsg.idx < cfgParts.Length)
			{
				if (cfgParts[configChunkMsg.idx] == null)
				{
					cfgParts[configChunkMsg.idx] = configChunkMsg.part ?? "";
					cfgGot++;
				}
				if (cfgGot >= cfgParts.Length)
				{
					string data = string.Concat(cfgParts);
					cfgParts = null;
					ApplySynced(data);
				}
			}
		}

		private static void ApplySynced(string data)
		{
			if ((Object)(object)I == (Object)null)
			{
				return;
			}
			List<ConfigEntryBase> list = all.Where((ConfigEntryBase e) => !localOnly.Contains(e)).ToList();
			if (!syncedActive)
			{
				localBackup.Clear();
				foreach (ConfigEntryBase item in list)
				{
					localBackup[item] = item.GetSerializedValue();
				}
				((BaseUnityPlugin)I).Config.SaveOnConfigSet = false;
				syncedActive = true;
				Say("Settings are now taken from the host");
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			string[] array = data.Split('|');
			for (int num = 0; num < array.Length; num++)
			{
				string[] array2 = array[num].Split('=');
				if (array2.Length == 2)
				{
					dictionary[Uri.UnescapeDataString(array2[0])] = Uri.UnescapeDataString(array2[1]);
				}
			}
			suppress = true;
			foreach (ConfigEntryBase item2 in list)
			{
				if (dictionary.TryGetValue(item2.Definition.Section + "/" + item2.Definition.Key, out var value))
				{
					try
					{
						item2.SetSerializedValue(value);
					}
					catch
					{
					}
				}
			}
			suppress = false;
			ApplyAll();
		}

		private static void RestoreLocal()
		{
			if (!syncedActive || (Object)(object)I == (Object)null)
			{
				return;
			}
			suppress = true;
			foreach (KeyValuePair<ConfigEntryBase, string> item in localBackup)
			{
				try
				{
					item.Key.SetSerializedValue(item.Value);
				}
				catch
				{
				}
			}
			suppress = false;
			localBackup.Clear();
			syncedActive = false;
			((BaseUnityPlugin)I).Config.SaveOnConfigSet = true;
			try
			{
				((BaseUnityPlugin)I).Config.Save();
			}
			catch
			{
			}
			if (ready)
			{
				ApplyAll();
			}
		}

		private static void SendRage(CharacterBody b, Rage rg, NetworkConnection conn = null)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || !Object.op_Implicit((Object)(object)b))
			{
				return;
			}
			NetworkIdentity component = ((Component)b).GetComponent<NetworkIdentity>();
			if (Object.op_Implicit((Object)(object)component))
			{
				RageNetMsg rageNetMsg = new RageNetMsg
				{
					id = component.netId,
					size = rg.size,
					ms = rg.ms,
					atk = rg.atk,
					hp = rg.hp,
					dmg = rg.dmg
				};
				if (conn != null)
				{
					conn.Send((short)29101, (MessageBase)(object)rageNetMsg);
				}
				else
				{
					NetworkServer.SendToAll((short)29101, (MessageBase)(object)rageNetMsg);
				}
			}
		}

		private static void OnRageMsg(NetworkMessage netMsg)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			RageNetMsg rageNetMsg = netMsg.ReadMessage<RageNetMsg>();
			if (NetworkServer.active || !ready)
			{
				return;
			}
			GameObject val = ClientScene.FindLocalObject(rageNetMsg.id);
			if (Object.op_Implicit((Object)(object)val))
			{
				CharacterBody component = val.GetComponent<CharacterBody>();
				if (Object.op_Implicit((Object)(object)component))
				{
					rages[component] = new Rage
					{
						size = rageNetMsg.size,
						ms = rageNetMsg.ms,
						atk = rageNetMsg.atk,
						hp = rageNetMsg.hp,
						dmg = rageNetMsg.dmg,
						born = Time.time
					};
					ApplyBody(component);
				}
			}
		}

		private static void CleanupRages()
		{
			foreach (CharacterBody item in rages.Keys.Where((CharacterBody k) => !Object.op_Implicit((Object)(object)k) || !Object.op_Implicit((Object)(object)k.healthComponent) || !k.healthComponent.alive).ToList())
			{
				rages.Remove(item);
			}
		}
	}
}