Decompiled source of RogueGolf v0.14.0

plugins/RogueGolf/RogueGolf.Core.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[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 RogueGolf.Core
{
	public static class BalanceMigration
	{
		public static int Apply0101(IDictionary<string, float> values, bool alreadyApplied)
		{
			if (alreadyApplied)
			{
				return 0;
			}
			int changed = 0;
			Replace("TimedLow", 0.6f, 0.35f);
			Replace("TimedHigh", 1.5f, 2f);
			Replace("glass_cannon.Outgoing", 1.5f, 2f);
			Replace("glass_cannon.Incoming", 1.5f, 2f);
			Replace("golf_kart_warfare.TrafficRate", 2f, 3f);
			Replace("friendly_fire_sale.Outgoing", 2f, 2.5f);
			Replace("total_war.Ammo", 8f, 7f);
			return changed;
			void Replace(string key, float before, float after)
			{
				if (values.TryGetValue(key, out var value) && value == before)
				{
					values[key] = after;
					changed++;
				}
			}
		}
	}
	public enum Parameter
	{
		ShotPower,
		Gravity,
		BallMass,
		BallScale,
		Bounce,
		Friction,
		Outgoing,
		Incoming,
		ItemRate,
		Ammo,
		ItemQuality,
		Protection,
		TrafficRate,
		Count
	}
	[Flags]
	public enum Mechanic
	{
		None = 0,
		HotStart = 1,
		RandomLoadout = 2,
		EqualLoadout = 4,
		Pacifist = 8,
		SuddenInventory = 0x10,
		ChaosLoadout = 0x20,
		DeathExplosion = 0x40,
		ExplosiveCollisions = 0x80,
		MusicalChairs = 0x100,
		LeaderTax = 0x200,
		LastPlaceInsurance = 0x400,
		ComebackSeason = 0x800,
		UnstableReality = 0x1000,
		RandomRules = 0x2000,
		GravityRoulette = 0x4000,
		RealityBreakdown = 0x8000,
		TotalWar = 0x10000,
		NuclearGolf = 0x20000,
		LawOfZeus = 0x40000
	}
	public readonly struct Contribution
	{
		public readonly Parameter Parameter;

		public readonly float Factor;

		public Contribution(Parameter parameter, float factor)
		{
			if (parameter < Parameter.ShotPower || parameter >= Parameter.Count || !VoteSettings.Finite(factor) || factor < 0.01f || factor > 120f)
			{
				throw new ArgumentException("Invalid contribution");
			}
			Parameter = parameter;
			Factor = factor;
		}
	}
	public static class CanonicalCatalog
	{
		public static float Minimum(Parameter p)
		{
			return p switch
			{
				Parameter.Friction => 0.01f, 
				Parameter.BallScale => 0.2f, 
				Parameter.Gravity => 0.1f, 
				_ => 0.25f, 
			};
		}

		public static float Maximum(Parameter p)
		{
			return p switch
			{
				Parameter.ShotPower => 3, 
				Parameter.TrafficRate => 3, 
				Parameter.Bounce => 2, 
				Parameter.ItemQuality => 10, 
				Parameter.ItemRate => 12, 
				Parameter.Ammo => 20, 
				Parameter.BallMass => 10, 
				Parameter.BallScale => 4, 
				_ => 4, 
			};
		}

		public static string TemporaryRuleId(int variant)
		{
			return (new string[8] { "power_surge", "dead_arms", "bouncy_ball", "fast_greens", "low_gravity", "small_ball", "thick_skin", "big_ball" })[variant % 8];
		}

		public static bool IsTimed(Mechanic m)
		{
			return (m & (Mechanic.UnstableReality | Mechanic.RandomRules | Mechanic.GravityRoulette | Mechanic.RealityBreakdown)) != 0;
		}

		public static EffectChannel Required(Parameter p)
		{
			switch (p)
			{
			case Parameter.ShotPower:
				return EffectChannel.ShotPower;
			case Parameter.Gravity:
				return EffectChannel.Gravity;
			case Parameter.BallMass:
				return EffectChannel.BallMass;
			case Parameter.BallScale:
				return EffectChannel.Scale;
			case Parameter.Bounce:
				return EffectChannel.Bounce;
			case Parameter.Friction:
				return EffectChannel.Friction;
			case Parameter.Outgoing:
			case Parameter.Incoming:
				return EffectChannel.Knockback;
			case Parameter.ItemRate:
			case Parameter.ItemQuality:
				return EffectChannel.ItemSpawn;
			case Parameter.Ammo:
				return EffectChannel.Inventory;
			case Parameter.Protection:
				return EffectChannel.Protection;
			case Parameter.TrafficRate:
				return EffectChannel.Traffic;
			default:
				throw new ArgumentException("Unknown parameter");
			}
		}

		public static Contribution[] Variation(Mechanic rule, int variant, ModifierSettings settings = null)
		{
			if (!IsTimed(rule))
			{
				return Array.Empty<Contribution>();
			}
			settings = settings ?? new ModifierSettings();
			if (rule == Mechanic.GravityRoulette)
			{
				return new Contribution[1]
				{
					new Contribution(Parameter.Gravity, (new float[4]
					{
						settings.Get("RouletteMoon", 0.2f),
						settings.Get("RouletteLow", 0.6f),
						1f,
						settings.Get("RouletteHigh", 1.5f)
					})[variant % 4])
				};
			}
			RunRandom random = new RunRandom((uint)((variant + 1) * 7919));
			Parameter[] array = new Parameter[6]
			{
				Parameter.Gravity,
				Parameter.Bounce,
				Parameter.Friction,
				Parameter.ShotPower,
				Parameter.Incoming,
				Parameter.BallScale
			};
			if (rule == Mechanic.RealityBreakdown)
			{
				return array.Select((Parameter p) => new Contribution(p, (random.Next(2) == 0) ? settings.Get("TimedLow", 0.35f) : settings.Get("TimedHigh", 2f))).ToArray();
			}
			Parameter parameter = array[variant % array.Length];
			return new Contribution[1]
			{
				new Contribution(parameter, (variant % 2 == 0) ? settings.Get("TimedLow", 0.35f) : settings.Get("TimedHigh", 2f))
			};
		}

		public static ModifierDefinition[] Create(ModifierSettings s)
		{
			List<ModifierDefinition> result = new List<ModifierDefinition>();
			ModifierRarity rarity = ModifierRarity.Common;
			ModifierRarity rarity2 = ModifierRarity.Rare;
			ModifierRarity rarity3 = ModifierRarity.Legendary;
			ModifierRarity rarity4 = ModifierRarity.Boss;
			EffectDuration duration = EffectDuration.OneHole;
			Contribution[] terms = Array.Empty<Contribution>();
			EffectChannel effectChannel = EffectChannel.Inventory;
			EffectChannel effectChannel2 = EffectChannel.Knockback;
			EffectChannel needs = EffectChannel.ShotPower | EffectChannel.Gravity | EffectChannel.Scale | EffectChannel.Bounce | EffectChannel.Friction | effectChannel2;
			Add("power_surge", "Power Surge", rarity, new Contribution[1] { C(Parameter.ShotPower, s.PowerSurge) });
			Add("dead_arms", "Dead Arms", rarity, new Contribution[1] { C(Parameter.ShotPower, 0.8f) });
			Add("heavy_ball", "Heavy Ball", rarity, new Contribution[1] { C(Parameter.BallMass, s.HeavyBall) }, Mechanic.None, EffectDuration.Run, "mass");
			Add("featherweight", "Featherweight", rarity, new Contribution[1] { C(Parameter.BallMass, s.Featherweight) }, Mechanic.None, EffectDuration.Run, "mass");
			Add("bouncy_ball", "Bouncy Ball", rarity, new Contribution[1] { C(Parameter.Bounce, 1.4f) });
			Add("big_ball", "Big Ball", rarity, new Contribution[1] { C(Parameter.BallScale, 1.25f) }, Mechanic.None, EffectDuration.Run, "scale");
			Add("small_ball", "Small Ball", rarity, new Contribution[1] { C(Parameter.BallScale, 0.75f) }, Mechanic.None, EffectDuration.Run, "scale");
			Add("fast_greens", "Fast Greens", rarity, new Contribution[1] { C(Parameter.Friction, 0.6f) }, Mechanic.None, EffectDuration.Run, "friction");
			Add("sticky_greens", "Sticky Greens", rarity, new Contribution[1] { C(Parameter.Friction, 1.6f) }, Mechanic.None, EffectDuration.Run, "friction");
			Add("item_rush", "Item Rush", rarity, new Contribution[1] { C(Parameter.ItemRate, 2f) });
			Add("strong_arms", "Strong Arms", rarity, new Contribution[1] { C(Parameter.Outgoing, 1.25f) });
			Add("thick_skin", "Thick Skin", rarity, new Contribution[1] { C(Parameter.Incoming, 0.8f) });
			Add("glass_jaw", "Glass Jaw", rarity, new Contribution[1] { C(Parameter.Incoming, 1.25f) });
			Add("extra_ammo", "Extra Ammo", rarity, new Contribution[1] { C(Parameter.Ammo, 2f) });
			Add("hot_start", "Hot Start", rarity, terms, Mechanic.HotStart, EffectDuration.Run, "starting_loadout", effectChannel, "Basic item each hole");
			Add("low_gravity", "Low Gravity", rarity2, new Contribution[1] { C(Parameter.Gravity, s.LowGravity) }, Mechanic.None, EffectDuration.Run, "gravity");
			Add("high_gravity", "High Gravity", rarity2, new Contribution[1] { C(Parameter.Gravity, s.HighGravity) }, Mechanic.None, EffectDuration.Run, "gravity");
			Add("tiny_ball", "Tiny Ball", rarity2, new Contribution[1] { C(Parameter.BallScale, 0.5f) }, Mechanic.None, EffectDuration.Run, "scale");
			Add("giant_ball", "Giant Ball", rarity2, new Contribution[1] { C(Parameter.BallScale, 2f) }, Mechanic.None, EffectDuration.Run, "scale");
			Add("high_knockback", "High Knockback", rarity2, new Contribution[1] { C(Parameter.Incoming, 1.75f) });
			Add("sumo_golf", "Sumo Golf", rarity2, new Contribution[3]
			{
				C(Parameter.BallMass, 3f),
				C(Parameter.Outgoing, 1.5f),
				C(Parameter.Incoming, 0.6f)
			});
			Add("pinball", "Pinball", rarity2, new Contribution[2]
			{
				C(Parameter.Bounce, 1.7f),
				C(Parameter.Friction, 0.35f)
			});
			Add("ice_rink", "Ice Rink", rarity2, new Contribution[1] { C(Parameter.Friction, 0.1f) }, Mechanic.None, EffectDuration.Run, "friction");
			Add("supercharged", "Supercharged", rarity2, new Contribution[1] { C(Parameter.ShotPower, 1.5f) });
			Add("weaklings", "Weaklings", rarity2, new Contribution[1] { C(Parameter.ShotPower, 0.5f) });
			Add("arms_race", "Arms Race", rarity2, new Contribution[1] { C(Parameter.ItemQuality, 3f) });
			Add("pacifist_golf", "Pacifist Golf", rarity2, terms, Mechanic.Pacifist, duration, "", effectChannel | effectChannel2, "Offensive item hits blocked");
			Add("random_loadout", "Random Loadout", rarity2, terms, Mechanic.RandomLoadout, EffectDuration.Run, "starting_loadout", effectChannel, "Independent random item each hole");
			Add("equal_opportunity_violence", "Equal Opportunity Violence", rarity2, terms, Mechanic.EqualLoadout, EffectDuration.Run, "starting_loadout", effectChannel, "Same random item for everyone each hole");
			Add("glass_cannon", "Glass Cannon", rarity2, new Contribution[2]
			{
				C(Parameter.Outgoing, 2f),
				C(Parameter.Incoming, 2f)
			});
			Add("juggernauts", "Juggernauts", rarity2, new Contribution[2]
			{
				C(Parameter.Incoming, 0.4f),
				C(Parameter.BallMass, 3f)
			});
			Add("leader_tax", "Leader Tax", rarity2, terms, Mechanic.LeaderTax, EffectDuration.Run, "", EffectChannel.ShotPower | EffectChannel.Ranking, "Leader shot power: " + (s.Get("LeaderTaxFactor", 0.8f) * 100f).ToString("0") + "%");
			Add("last_place_insurance", "Last Place Insurance", rarity2, terms, Mechanic.LastPlaceInsurance, EffectDuration.Run, "", EffectChannel.ShotPower | EffectChannel.Ranking, "Last place shot power: " + (s.Get("LastPlaceFactor", 1.2f) * 100f).ToString("0") + "%");
			Add("no_mercy", "No Mercy", rarity2, new Contribution[1] { C(Parameter.Protection, 0.5f) });
			Add("sudden_inventory", "Sudden Inventory", rarity2, terms, Mechanic.SuddenInventory, EffectDuration.Run, "", effectChannel, "Periodic random item grants");
			Add("moon_golf", "Moon Golf", rarity3, new Contribution[1] { C(Parameter.Gravity, 0.2f) }, Mechanic.None, EffectDuration.Run, "gravity");
			Add("jupiter_golf", "Jupiter Golf", rarity3, new Contribution[1] { C(Parameter.Gravity, 2.5f) }, Mechanic.None, EffectDuration.Run, "gravity");
			Add("knockback_apocalypse", "Knockback Apocalypse", rarity3, new Contribution[1] { C(Parameter.Incoming, 3f) });
			Add("arsenal", "Arsenal", rarity3, new Contribution[2]
			{
				C(Parameter.ItemRate, 4f),
				C(Parameter.ItemQuality, 5f)
			});
			Add("infinite_warfare", "Infinite Warfare", rarity3, new Contribution[1] { C(Parameter.Ammo, 5f) });
			Add("death_explosion", "Death Explosion", rarity3, terms, Mechanic.DeathExplosion, EffectDuration.Run, "", EffectChannel.Explosions, "Explode on elimination");
			Add("explosive_collisions", "Explosive Collisions", rarity3, terms, Mechanic.ExplosiveCollisions, EffectDuration.Run, "", EffectChannel.Explosions, "Fast ball landings and player impacts explode; the landing ball ignores its own blast");
			Add("balloon_animals", "Balloon Animals", rarity3, new Contribution[4]
			{
				C(Parameter.BallScale, 3f),
				C(Parameter.BallMass, 0.3f),
				C(Parameter.Incoming, 1.6f),
				C(Parameter.Bounce, 1.4f)
			});
			Add("cannonballs", "Cannonballs", rarity3, new Contribution[3]
			{
				C(Parameter.BallScale, 0.5f),
				C(Parameter.BallMass, 8f),
				C(Parameter.Incoming, 0.5f)
			});
			Add("chaos_loadout", "Chaos Loadout", rarity3, terms, Mechanic.ChaosLoadout, EffectDuration.Run, "", effectChannel, "Periodic inventory rerolls");
			Add("musical_chairs", "Musical Chairs", rarity3, terms, Mechanic.MusicalChairs, EffectDuration.Run, "", EffectChannel.Teleport, "Periodically swap ball positions");
			Add("rubber_world", "Rubber World", rarity3, new Contribution[1] { C(Parameter.Bounce, 2f) });
			Add("zero_traction", "Zero Traction", rarity3, new Contribution[1] { C(Parameter.Friction, 0.01f) }, Mechanic.None, EffectDuration.Run, "friction");
			Add("maximum_overdrive", "Maximum Overdrive", rarity3, new Contribution[1] { C(Parameter.ShotPower, 2f) });
			Add("golf_kart_warfare", "Golf Kart Warfare", rarity3, new Contribution[1] { C(Parameter.TrafficRate, 3f) }, Mechanic.None, EffectDuration.Run, "", EffectChannel.None, "Compatible traffic courses only");
			Add("friendly_fire_sale", "Friendly Fire Sale", rarity3, new Contribution[1] { C(Parameter.Outgoing, 2.5f) }, Mechanic.None, EffectDuration.Run, "", EffectChannel.None, "Player versus player knockback boosted");
			Add("comeback_season", "Comeback Season", rarity3, terms, Mechanic.ComebackSeason, EffectDuration.Run, "", EffectChannel.ShotPower | EffectChannel.Ranking, "Rank shot power up to " + (s.Get("ComebackMaximum", 1.3f) * 100f).ToString("0") + "%");
			Add("unstable_reality", "Unstable Reality", rarity3, terms, Mechanic.UnstableReality, EffectDuration.Run, "", needs, "One physics property rerolls periodically");
			Add("random_rules", "Random Rules", rarity3, terms, Mechanic.RandomRules, EffectDuration.Run, "", needs, "One removable physics rule rotates periodically");
			Add("law_of_zeus", "LAW OF ZEUS", rarity4, terms, Mechanic.LawOfZeus, duration, "", EffectChannel.WorldEvents, "The gods are displeased");
			Add("gravity_roulette", "Gravity Roulette", rarity4, terms, Mechanic.GravityRoulette, duration, "", EffectChannel.Gravity, "Gravity presets cycle periodically");
			Add("reality_breakdown", "Reality Breakdown", rarity4, terms, Mechanic.RealityBreakdown, duration, "", needs, "Multiple physics properties reroll");
			Add("item_storm", "Item Storm", rarity4, new Contribution[1] { C(Parameter.ItemRate, 10f) }, Mechanic.None, duration);
			Add("total_war", "Total War", rarity4, new Contribution[1] { C(Parameter.Ammo, 7f) }, Mechanic.TotalWar, duration, "", effectChannel, "Powerful loadouts replenished periodically");
			Add("nuclear_golf", "Nuclear Golf", rarity4, terms, Mechanic.NuclearGolf, duration, "", EffectChannel.Explosions, "Large bounded death and impact explosions");
			Add("ballpocalypse", "Ballpocalypse", rarity4, new Contribution[2]
			{
				C(Parameter.BallScale, 4f),
				C(Parameter.BallMass, 3f)
			}, Mechanic.None, duration);
			Add("micro_golf", "Micro Golf", rarity4, new Contribution[1] { C(Parameter.BallScale, 0.2f) }, Mechanic.None, duration);
			Add("orbital_golf", "Orbital Golf", rarity4, new Contribution[2]
			{
				C(Parameter.Gravity, 0.15f),
				C(Parameter.ShotPower, 2.5f)
			}, Mechanic.None, duration);
			Add("pinball_hell", "Pinball Hell", rarity4, new Contribution[3]
			{
				C(Parameter.Bounce, 2f),
				C(Parameter.Friction, 0.05f),
				C(Parameter.Incoming, 3f)
			}, Mechanic.None, duration);
			Add("arsenal_apocalypse", "Arsenal Apocalypse", rarity4, new Contribution[3]
			{
				C(Parameter.ItemRate, 10f),
				C(Parameter.ItemQuality, 8f),
				C(Parameter.Ammo, 8f)
			}, Mechanic.None, duration);
			return result.ToArray();
			void Add(string id, string name, ModifierRarity modifierRarity, Contribution[] source, Mechanic rule = Mechanic.None, EffectDuration effectDuration = EffectDuration.Run, string group = "", EffectChannel effectChannel4 = EffectChannel.None, string detail = "")
			{
				Contribution[] array = source.Select((Contribution c) => C(c.Parameter, s.Get(id + "." + c.Parameter, c.Factor))).ToArray();
				Contribution[] array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					Contribution contribution = array2[num];
					if (contribution.Factor < Minimum(contribution.Parameter) || contribution.Factor > Maximum(contribution.Parameter))
					{
						throw new ArgumentException("Tuning outside channel bounds: " + id);
					}
				}
				EffectChannel effectChannel3 = effectChannel4;
				array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					Contribution contribution2 = array2[num];
					effectChannel3 |= Required(contribution2.Parameter);
				}
				string text = string.Join(", ", array.Select((Contribution c) => (c.Factor * 100f).ToString("0", CultureInfo.InvariantCulture) + "% " + c.Parameter));
				if (detail != "")
				{
					text = text + ((text == "") ? "" : ". ") + detail;
				}
				result.Add(new ModifierDefinition(id, name, modifierRarity, effectChannel3, (array.Length == 1) ? array[0].Factor : 1f, group, effectDuration, modifierRarity switch
				{
					ModifierRarity.Legendary => 2, 
					ModifierRarity.Rare => 3, 
					ModifierRarity.Common => 4, 
					_ => 1, 
				}, array, rule, modifierRarity.ToString() + " | " + text + ". " + ((effectDuration == EffectDuration.OneHole) ? "Next hole only." : "For this run.")));
			}
			static Contribution C(Parameter p, float f)
			{
				return new Contribution(p, f);
			}
		}
	}
	public static class Conditions
	{
		public static string Direction(ModifierDefinition d)
		{
			if (CanonicalCatalog.IsTimed(d.Rule))
			{
				return "~";
			}
			bool flag = d.Contributions.Any((Contribution c) => c.Factor > 1f);
			bool flag2 = d.Contributions.Any((Contribution c) => c.Factor < 1f);
			if (!(flag && flag2))
			{
				if (!flag)
				{
					if (!flag2)
					{
						return "";
					}
					return "−";
				}
				return "+";
			}
			return "±";
		}

		public static KeyValuePair<string, string>[] Rows(ModifierRegistry registry, ModifierSettings settings, IEnumerable<ActiveModifier> effects, int hole, float personalShot = 1f)
		{
			ActiveModifier[] array = effects.Where((ActiveModifier e) => !e.Suspended && e.FirstHole <= hole).ToArray();
			EffectValues effectValues = registry.Compose(array);
			HashSet<Parameter> hashSet = new HashSet<Parameter>();
			Mechanic mechanic = Mechanic.None;
			ActiveModifier[] array2 = array;
			foreach (ActiveModifier activeModifier in array2)
			{
				ModifierDefinition modifierDefinition = registry.Get(activeModifier.Id);
				mechanic |= modifierDefinition.Rule;
				foreach (Contribution item in modifierDefinition.Contributions.Concat((modifierDefinition.Rule == Mechanic.RandomRules) ? registry.Get(CanonicalCatalog.TemporaryRuleId(activeModifier.Variant)).Contributions : CanonicalCatalog.Variation(modifierDefinition.Rule, activeModifier.Variant, settings)))
				{
					hashSet.Add(item.Parameter);
				}
			}
			if (Math.Abs(personalShot - 1f) > 0.0001f)
			{
				hashSet.Add(Parameter.ShotPower);
			}
			List<KeyValuePair<string, string>> rows = new List<KeyValuePair<string, string>>();
			if ((mechanic & Mechanic.Pacifist) != Mechanic.None)
			{
				Add("pacifist", "Offensive items disabled");
			}
			Parameter[] array3 = new Parameter[13]
			{
				Parameter.Gravity,
				Parameter.ShotPower,
				Parameter.BallScale,
				Parameter.BallMass,
				Parameter.Bounce,
				Parameter.Friction,
				Parameter.Outgoing,
				Parameter.Incoming,
				Parameter.Ammo,
				Parameter.ItemRate,
				Parameter.ItemQuality,
				Parameter.Protection,
				Parameter.TrafficRate
			};
			for (int num = 0; num < array3.Length; num++)
			{
				Parameter parameter = array3[num];
				if (hashSet.Contains(parameter))
				{
					float value = ((parameter == Parameter.ShotPower) ? Math.Max(0.25f, Math.Min(3f, effectValues[parameter] * personalShot)) : effectValues[parameter]);
					string text = ((parameter == Parameter.ShotPower) ? "Shot power" : EffectPresentation.ParameterName(parameter));
					if (parameter == Parameter.Outgoing && array.Any((ActiveModifier e) => e.Id == "friendly_fire_sale"))
					{
						text += " (PvP)";
					}
					Add(parameter.ToString(), text + " ×" + EffectPresentation.Number(value));
				}
			}
			if ((mechanic & Mechanic.LawOfZeus) != Mechanic.None)
			{
				Add("zeus", "Thunderstorms across the course");
			}
			if ((mechanic & Mechanic.TotalWar) != Mechanic.None)
			{
				Add("inventory", "Powerful loadouts refill");
			}
			else if ((mechanic & Mechanic.ChaosLoadout) != Mechanic.None)
			{
				Add("inventory", "Inventories reroll");
			}
			else if ((mechanic & Mechanic.SuddenInventory) != Mechanic.None)
			{
				Add("inventory", "Random items granted");
			}
			if ((mechanic & Mechanic.MusicalChairs) != Mechanic.None)
			{
				Add("swap", "Ball positions swap");
			}
			if ((mechanic & Mechanic.Pacifist) == 0)
			{
				if ((mechanic & (Mechanic.DeathExplosion | Mechanic.NuclearGolf)) != Mechanic.None)
				{
					Add("death", "Explode on elimination");
				}
				if ((mechanic & (Mechanic.ExplosiveCollisions | Mechanic.NuclearGolf)) != Mechanic.None)
				{
					Add("impact", "Explosive impacts");
				}
			}
			return rows.ToArray();
			void Add(string id, string value2)
			{
				rows.Add(new KeyValuePair<string, string>(id, value2));
			}
		}
	}
	public enum EffectGlyph
	{
		Bolt,
		Weight,
		Feather,
		Ball,
		Bounce,
		Ice,
		Glue,
		Gift,
		Fist,
		Shield,
		Ammo,
		Orbit,
		Dice,
		Peace,
		Crown,
		Flag,
		Clock,
		Explosion,
		Swap,
		Car,
		World,
		Skull,
		Infinity,
		Storm
	}
	public readonly struct EffectSymbol
	{
		public readonly EffectGlyph Glyph;

		public readonly int Variant;

		public EffectSymbol(EffectGlyph glyph, int variant = 0)
		{
			Glyph = glyph;
			Variant = variant;
		}
	}
	public static class EffectPresentation
	{
		private static readonly Dictionary<string, EffectSymbol> symbols = BuildSymbols();

		private static Dictionary<string, EffectSymbol> BuildSymbols()
		{
			Dictionary<string, EffectSymbol> result = new Dictionary<string, EffectSymbol>();
			Add(EffectGlyph.Bolt, new string[5] { "power_surge", "supercharged", "maximum_overdrive", "dead_arms", "weaklings" });
			Add(EffectGlyph.Weight, new string[3] { "heavy_ball", "sumo_golf", "cannonballs" });
			Add(EffectGlyph.Feather, new string[2] { "featherweight", "balloon_animals" });
			Add(EffectGlyph.Ball, new string[6] { "small_ball", "big_ball", "tiny_ball", "giant_ball", "micro_golf", "ballpocalypse" });
			Add(EffectGlyph.Bounce, new string[4] { "bouncy_ball", "pinball", "rubber_world", "pinball_hell" });
			Add(EffectGlyph.Ice, new string[3] { "fast_greens", "ice_rink", "zero_traction" });
			Add(EffectGlyph.Glue, new string[1] { "sticky_greens" });
			Add(EffectGlyph.Gift, new string[4] { "item_rush", "hot_start", "arsenal", "arsenal_apocalypse" });
			Add(EffectGlyph.Fist, new string[5] { "strong_arms", "high_knockback", "glass_cannon", "knockback_apocalypse", "friendly_fire_sale" });
			Add(EffectGlyph.Shield, new string[5] { "thick_skin", "glass_jaw", "juggernauts", "no_mercy", "last_place_insurance" });
			Add(EffectGlyph.Ammo, new string[3] { "extra_ammo", "arms_race", "total_war" });
			Add(EffectGlyph.Orbit, new string[6] { "low_gravity", "high_gravity", "moon_golf", "jupiter_golf", "orbital_golf", "gravity_roulette" });
			Add(EffectGlyph.Dice, new string[4] { "random_loadout", "equal_opportunity_violence", "chaos_loadout", "random_rules" });
			Add(EffectGlyph.Peace, new string[1] { "pacifist_golf" });
			Add(EffectGlyph.Crown, new string[1] { "leader_tax" });
			Add(EffectGlyph.Flag, new string[1] { "comeback_season" });
			Add(EffectGlyph.Clock, new string[1] { "sudden_inventory" });
			Add(EffectGlyph.Explosion, new string[3] { "death_explosion", "explosive_collisions", "nuclear_golf" });
			Add(EffectGlyph.Swap, new string[1] { "musical_chairs" });
			Add(EffectGlyph.Car, new string[1] { "golf_kart_warfare" });
			Add(EffectGlyph.World, new string[2] { "unstable_reality", "reality_breakdown" });
			Add(EffectGlyph.Infinity, new string[1] { "infinite_warfare" });
			Add(EffectGlyph.Storm, new string[2] { "item_storm", "law_of_zeus" });
			return result;
			void Add(EffectGlyph glyph, string[] ids)
			{
				for (int i = 0; i < ids.Length; i++)
				{
					result.Add(ids[i], new EffectSymbol(glyph, i + 1));
				}
			}
		}

		public static EffectSymbol Symbol(string id)
		{
			if (!symbols.TryGetValue(id, out var value))
			{
				throw new ArgumentException("Missing effect icon: " + id);
			}
			return value;
		}

		public static string Number(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		public static string ParameterName(Parameter p)
		{
			return (new string[13]
			{
				"Shot speed", "Gravity", "Ball mass", "Ball size", "Bounce", "Ground friction", "Knockback dealt", "Knockback received", "Item spawn rate", "Item uses",
				"Powerful-item odds", "Recovery protection", "Traffic rate"
			})[(int)p];
		}

		public static string Factor(Contribution c)
		{
			return ParameterName(c.Parameter) + " ×" + Number(c.Factor);
		}

		public static string Status(ActiveModifier effect, int currentHole)
		{
			string text = ((effect.RemainingHoles < 0) ? "For this run" : ((effect.FirstHole > currentHole) ? "Next hole only" : "This hole only"));
			return (effect.Suspended ? "Suspended · " : ((effect.FirstHole > currentHole) ? "Upcoming · " : "")) + text;
		}

		public static string Details(ModifierDefinition d, ModifierSettings settings, ActiveModifier active = null)
		{
			List<string> list = d.Contributions.Select(Factor).ToList();
			string text = "Every " + Number(settings.Interval) + "s";
			switch (d.Rule)
			{
			case Mechanic.LawOfZeus:
				list.Add("The gods are displeased. Continuous vanilla storms target greens, players, balls, item points and fairway routes. Host-limited to at most seven storms; one hole only.");
				break;
			case Mechanic.HotStart:
				list.Add("Start each hole with a basic item.");
				break;
			case Mechanic.RandomLoadout:
				list.Add("Each player starts with an independently drawn item.");
				break;
			case Mechanic.EqualLoadout:
				list.Add("Everyone starts with the same randomly drawn item.");
				break;
			case Mechanic.Pacifist:
				list.Add("Offensive item use and offensive item hits are blocked.");
				break;
			case Mechanic.SuddenInventory:
				list.Add(text + ", receive a random item if a slot is free.");
				break;
			case Mechanic.ChaosLoadout:
				list.Add(text + ", inventories reroll. Active item use delays the reroll.");
				break;
			case Mechanic.MusicalChairs:
				list.Add(text + ", balls swap positions. Requires at least two balls.");
				break;
			case Mechanic.TotalWar:
				list.Add(text + ", powerful loadouts are replenished.");
				break;
			case Mechanic.LeaderTax:
				list.Add("Leader shot speed ×" + Number(settings.Get("LeaderTaxFactor", 0.8f)) + ". No solo penalty.");
				break;
			case Mechanic.LastPlaceInsurance:
				list.Add("Last-place shot speed ×" + Number(settings.Get("LastPlaceFactor", 1.2f)) + ". Requires multiple players.");
				break;
			case Mechanic.ComebackSeason:
				list.Add("Rank-based shot speed assistance, up to ×" + Number(settings.Get("ComebackMaximum", 1.3f)) + ". Requires multiple players.");
				break;
			case Mechanic.DeathExplosion:
			case Mechanic.ExplosiveCollisions:
			case Mechanic.NuclearGolf:
				list.Add((d.Rule == Mechanic.DeathExplosion) ? "Explode on elimination." : ((d.Rule == Mechanic.ExplosiveCollisions) ? "Fast ball landings and player impacts cause explosions." : "Eliminations, fast ball landings and player impacts cause large explosions."));
				if (d.Rule != Mechanic.DeathExplosion)
				{
					list.Add("The landing ball is immune to its own blast; other targets are affected normally.");
				}
				list.Add("Blast radius " + Number(Math.Min(12f, settings.ExplosionRadius * (float)((d.Rule != Mechanic.NuclearGolf) ? 1 : 2))) + "m · cooldown " + Number(settings.ExplosionCooldown) + "s" + ((d.Rule == Mechanic.DeathExplosion) ? "" : (" · impact threshold " + Number(settings.ImpactThreshold))));
				break;
			case Mechanic.UnstableReality:
			case Mechanic.RandomRules:
			case Mechanic.GravityRoulette:
			case Mechanic.RealityBreakdown:
				list.Add(text + ((d.Rule == Mechanic.GravityRoulette) ? ", gravity cycles." : ((d.Rule == Mechanic.RandomRules) ? ", a temporary rule rotates." : ", physics rerolls.")));
				if (d.Rule == Mechanic.UnstableReality || d.Rule == Mechanic.RealityBreakdown)
				{
					list.Add("Reroll factors: ×" + Number(settings.Get("TimedLow", 0.35f)) + " / ×" + Number(settings.Get("TimedHigh", 2f)) + " (combined limits apply).");
				}
				if (active != null)
				{
					Contribution[] source = ((d.Rule == Mechanic.RandomRules) ? ModifierRegistry.Create(settings).Get(CanonicalCatalog.TemporaryRuleId(active.Variant)).Contributions : CanonicalCatalog.Variation(d.Rule, active.Variant, settings));
					list.Add("Current: " + string.Join(" · ", source.Select(Factor)));
				}
				break;
			}
			if (d.Id == "golf_kart_warfare")
			{
				list.Add("Only active on courses with supported traffic.");
			}
			if (d.Id == "friendly_fire_sale")
			{
				list.Add("Extra outgoing knockback applies to player-versus-player hits only; native team protections remain.");
			}
			return string.Join("\n", list);
		}

		public static bool HasCycle(Mechanic rule)
		{
			if (!CanonicalCatalog.IsTimed(rule) && rule != Mechanic.SuddenInventory && rule != Mechanic.ChaosLoadout && rule != Mechanic.MusicalChairs)
			{
				return rule == Mechanic.TotalWar;
			}
			return true;
		}

		public static string CycleValues(ModifierDefinition definition, ModifierSettings settings, ActiveModifier active)
		{
			if (CanonicalCatalog.IsTimed(definition.Rule))
			{
				Contribution[] source = ((definition.Rule == Mechanic.RandomRules) ? ModifierRegistry.Create(settings).Get(CanonicalCatalog.TemporaryRuleId(active.Variant)).Contributions : CanonicalCatalog.Variation(definition.Rule, active.Variant, settings));
				return string.Join(" · ", source.Select(Factor));
			}
			if (definition.Rule != Mechanic.SuddenInventory)
			{
				if (definition.Rule != Mechanic.ChaosLoadout)
				{
					if (definition.Rule != Mechanic.MusicalChairs)
					{
						return "Powerful loadouts replenish";
					}
					return "Ball positions swap";
				}
				return "Inventory reroll";
			}
			return "Random item when a slot is free";
		}

		public static string Combined(ModifierRegistry registry, IEnumerable<ActiveModifier> active)
		{
			EffectValues values = registry.Compose(active);
			IEnumerable<string> values2 = from Parameter p in Enum.GetValues(typeof(Parameter))
				where p != Parameter.Count && Math.Abs(values[p] - 1f) > 0.0001f
				select Factor(new Contribution(p, values[p]));
			string text = string.Join(" · ", values2);
			if (!(text == ""))
			{
				return text;
			}
			return "Standard physics. Item and special rules are listed below.";
		}
	}
	public readonly struct GravityVector
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public GravityVector(float x, float y, float z)
		{
			if (!Valid(x) || !Valid(y) || !Valid(z))
			{
				throw new ArgumentOutOfRangeException("x");
			}
			X = x;
			Y = y;
			Z = z;
		}

		private static bool Valid(float n)
		{
			if (VoteSettings.Finite(n))
			{
				return Math.Abs(n) <= 1000f;
			}
			return false;
		}

		public GravityVector Scale(float factor)
		{
			return new GravityVector(X * factor, Y * factor, Z * factor);
		}
	}
	public sealed class GravityLease
	{
		private readonly Func<GravityVector> read;

		private readonly Action<GravityVector> write;

		private GravityVector original;

		private string run = "";

		public bool Active => run != "";

		public GravityLease(Func<GravityVector> read, Action<GravityVector> write)
		{
			this.read = read;
			this.write = write;
		}

		public void Apply(string runId, GravityVector hostBaseline, float factor = 0.6f)
		{
			if (string.IsNullOrEmpty(runId))
			{
				throw new ArgumentException("Effect requires a run identity");
			}
			if (!VoteSettings.Finite(factor) || factor < 0.1f || factor > 4f)
			{
				throw new ArgumentOutOfRangeException("factor");
			}
			if (run != runId)
			{
				Restore();
				original = read();
				run = runId;
			}
			write(hostBaseline.Scale(factor));
		}

		public void Restore()
		{
			if (Active)
			{
				write(original);
				run = "";
			}
		}
	}
	public static class LowGravity
	{
		public const string Id = "low_gravity";

		public const float Factor = 0.6f;

		public static VoteOption[] Candidates(bool skip)
		{
			VoteOption voteOption = new VoteOption("low_gravity", "Low Gravity", "60% gravity for balls and players. Lasts this run; does not stack.");
			if (skip)
			{
				return new VoteOption[2]
				{
					voteOption,
					new VoteOption("skip", "Skip", "No new modifier. Uses one shared skip.")
				};
			}
			return new VoteOption[1] { voteOption };
		}
	}
	public sealed class IntermissionGate
	{
		private Action continuation;

		private Func<bool> valid;

		private string run = "";

		private int hole = -1;

		public bool Held => continuation != null;

		public bool WasIntercepted(string runId, int holeIndex)
		{
			if (run == runId)
			{
				return hole == holeIndex;
			}
			return false;
		}

		public bool Hold(string runId, int holeIndex, Action resume, Func<bool> isValid)
		{
			if (Held || WasIntercepted(runId, holeIndex))
			{
				return false;
			}
			if (resume == null || isValid == null)
			{
				throw new ArgumentNullException();
			}
			run = runId;
			hole = holeIndex;
			continuation = resume;
			valid = isValid;
			return true;
		}

		public bool Release()
		{
			if (!Held)
			{
				return false;
			}
			Action action = continuation;
			Func<bool> func = valid;
			continuation = null;
			valid = null;
			if (!func())
			{
				return false;
			}
			action();
			return true;
		}

		public void Cancel()
		{
			continuation = null;
			valid = null;
			run = "";
			hole = -1;
		}
	}
	[Flags]
	public enum EffectChannel
	{
		None = 0,
		ShotPower = 1,
		Gravity = 2,
		BallMass = 4,
		Scale = 8,
		Bounce = 0x10,
		Friction = 0x20,
		Knockback = 0x40,
		ItemSpawn = 0x80,
		Inventory = 0x100,
		Explosions = 0x200,
		Teleport = 0x400,
		Ranking = 0x800,
		Protection = 0x1000,
		Traffic = 0x2000,
		WorldEvents = 0x4000,
		All = 0x7FFF
	}
	public enum ModifierRarity
	{
		Common,
		Rare,
		Legendary,
		Boss
	}
	public enum EffectDuration
	{
		Run,
		OneHole
	}
	public enum StackPolicy
	{
		Unique
	}
	public sealed class ModifierDefinition
	{
		private readonly Contribution[] contributions;

		public string Id { get; }

		public string Name { get; }

		public string Description { get; }

		public ModifierRarity Rarity { get; }

		public EffectDuration Duration { get; }

		public StackPolicy Stacking => StackPolicy.Unique;

		public string ExclusiveGroup { get; }

		public EffectChannel Channel { get; }

		public float Factor { get; }

		public int Weight { get; }

		public Contribution[] Contributions => (Contribution[])contributions.Clone();

		public Mechanic Rule { get; }

		public bool TemporarySafe => Rule == Mechanic.None;

		public ModifierDefinition(string id, string name, ModifierRarity rarity, EffectChannel channel, float factor, string group = "", EffectDuration duration = EffectDuration.Run, int weight = 1, Contribution[] contributions = null, Mechanic rule = Mechanic.None, string description = null)
		{
			if (string.IsNullOrEmpty(id) || (!ValidFactor(factor) && contributions == null) || weight < 1 || weight > 100 || channel == EffectChannel.None || (channel & ~EffectChannel.All) != EffectChannel.None)
			{
				throw new ArgumentException("Invalid modifier definition");
			}
			this.contributions = ((contributions != null) ? ((Contribution[])contributions.Clone()) : new Contribution[1]
			{
				new Contribution(channel switch
				{
					EffectChannel.Gravity => Parameter.Gravity, 
					EffectChannel.ShotPower => Parameter.ShotPower, 
					_ => Parameter.BallMass, 
				}, factor)
			});
			Rule = rule;
			Id = id;
			Name = name;
			Rarity = rarity;
			Channel = channel;
			Factor = factor;
			ExclusiveGroup = group;
			Duration = duration;
			Weight = weight;
			string text = (factor * 100f).ToString("0", CultureInfo.InvariantCulture) + "%";
			Description = description ?? (rarity.ToString() + " | " + text + " " + channel switch
			{
				EffectChannel.Gravity => "global gravity", 
				EffectChannel.ShotPower => "ball shot speed", 
				_ => "ball mass", 
			} + ". " + ((duration == EffectDuration.Run) ? "For this run." : "Next hole only."));
		}

		public static bool ValidFactor(float f)
		{
			if (VoteSettings.Finite(f) && f >= 0.1f)
			{
				return f <= 4f;
			}
			return false;
		}
	}
	public sealed class ModifierSettings
	{
		private readonly Dictionary<string, float> extra;

		public KeyValuePair<string, float>[] Extra => extra.OrderBy<KeyValuePair<string, float>, string>((KeyValuePair<string, float> p) => p.Key, StringComparer.Ordinal).ToArray();

		public float Interval => Get("IntervalSeconds", 20f);

		public float ImpactThreshold => Get("ImpactThreshold", 12f);

		public float ExplosionCooldown => Get("ExplosionCooldown", 2f);

		public float ExplosionRadius => Get("ExplosionRadius", 5f);

		public float PowerSurge { get; }

		public float HeavyBall { get; }

		public float Featherweight { get; }

		public float LowGravity { get; }

		public float HighGravity { get; }

		public float Get(string key, float fallback)
		{
			if (!extra.TryGetValue(key, out var value))
			{
				return fallback;
			}
			return value;
		}

		public ModifierSettings(float power = 1.2f, float heavy = 2f, float feather = 0.5f, float low = 0.6f, float high = 1.5f, IEnumerable<KeyValuePair<string, float>> tuning = null)
		{
			if (!ModifierDefinition.ValidFactor(power) || !ModifierDefinition.ValidFactor(heavy) || !ModifierDefinition.ValidFactor(feather) || !ModifierDefinition.ValidFactor(low) || !ModifierDefinition.ValidFactor(high) || power < 1f || power > 3f || heavy < 1f || feather > 1f || low > 1f || high < 1f)
			{
				throw new ArgumentOutOfRangeException("power");
			}
			extra = ((tuning == null) ? new Dictionary<string, float>() : tuning.ToDictionary<KeyValuePair<string, float>, string, float>((KeyValuePair<string, float> p) => p.Key, (KeyValuePair<string, float> p) => p.Value, StringComparer.Ordinal));
			if (extra.Count > 256 || extra.Any((KeyValuePair<string, float> p) => p.Key.Length > 64 || !VoteSettings.Finite(p.Value) || p.Value < 0.01f || p.Value > 120f))
			{
				throw new ArgumentException("Invalid tuning");
			}
			if (Interval < 5f || Interval > 120f || ImpactThreshold < 5f || ImpactThreshold > 80f || ExplosionCooldown < 0.5f || ExplosionCooldown > 30f || ExplosionRadius < 1f || ExplosionRadius > 12f)
			{
				throw new ArgumentException("Unsafe runtime settings");
			}
			PowerSurge = power;
			HeavyBall = heavy;
			Featherweight = feather;
			LowGravity = low;
			HighGravity = high;
		}
	}
	public sealed class ActiveModifier
	{
		private int lastCompleted = -1;

		public string Id { get; }

		public int FirstHole { get; }

		public int RemainingHoles { get; private set; }

		public bool Suspended { get; set; }

		public int Variant { get; set; }

		public ActiveModifier(string id, int firstHole, int remaining = -1, bool suspended = false)
		{
			Id = id;
			FirstHole = firstHole;
			RemainingHoles = remaining;
			Suspended = suspended;
		}

		public bool CompleteHole(int hole)
		{
			if (hole < FirstHole || hole <= lastCompleted || RemainingHoles < 1)
			{
				return false;
			}
			lastCompleted = hole;
			RemainingHoles--;
			return RemainingHoles == 0;
		}

		public ActiveModifier Copy()
		{
			return new ActiveModifier(Id, FirstHole, RemainingHoles, Suspended)
			{
				lastCompleted = lastCompleted,
				Variant = Variant
			};
		}
	}
	public readonly struct EffectValues
	{
		private readonly float[] values;

		public float this[Parameter channel]
		{
			get
			{
				if (values != null)
				{
					return values[(int)channel];
				}
				return 1f;
			}
		}

		public float ShotPower => this[Parameter.ShotPower];

		public float Gravity => this[Parameter.Gravity];

		public float BallMass => this[Parameter.BallMass];

		public static EffectValues Normal => new EffectValues(1f, 1f, 1f);

		public EffectValues(float shot, float gravity, float mass)
		{
			values = Enumerable.Repeat(1f, 13).ToArray();
			values[0] = shot;
			values[1] = gravity;
			values[2] = mass;
		}

		public EffectValues(float[] source)
		{
			values = (float[])source.Clone();
		}
	}
	public sealed class ModifierRegistry
	{
		private readonly Dictionary<string, ModifierDefinition> definitions;

		private readonly ModifierSettings settings;

		public ModifierDefinition[] Definitions => definitions.Values.OrderBy<ModifierDefinition, string>((ModifierDefinition d) => d.Id, StringComparer.Ordinal).ToArray();

		public ModifierRegistry(IEnumerable<ModifierDefinition> source, ModifierSettings settings = null)
		{
			this.settings = settings ?? new ModifierSettings();
			definitions = source.ToDictionary<ModifierDefinition, string>((ModifierDefinition d) => d.Id, StringComparer.Ordinal);
		}

		public ModifierDefinition Get(string id)
		{
			if (!definitions.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		public static ModifierRegistry Create(ModifierSettings s)
		{
			return new ModifierRegistry(CanonicalCatalog.Create(s), s);
		}

		public EffectValues Compose(IEnumerable<ActiveModifier> effects)
		{
			double[] array = Enumerable.Repeat(1.0, 13).ToArray();
			foreach (ActiveModifier item in effects.Where((ActiveModifier e) => !e.Suspended).OrderBy<ActiveModifier, string>((ActiveModifier e) => e.Id, StringComparer.Ordinal))
			{
				ModifierDefinition modifierDefinition = Get(item.Id) ?? throw new ArgumentException("Unknown effect");
				foreach (Contribution item2 in modifierDefinition.Contributions.Concat((modifierDefinition.Rule == Mechanic.RandomRules) ? Get(CanonicalCatalog.TemporaryRuleId(item.Variant)).Contributions : CanonicalCatalog.Variation(modifierDefinition.Rule, item.Variant, settings)))
				{
					array[(int)item2.Parameter] *= item2.Factor;
				}
			}
			return new EffectValues(array.Select((double v, int i) => Clamp(v, CanonicalCatalog.Minimum((Parameter)i), CanonicalCatalog.Maximum((Parameter)i))).ToArray());
		}

		private static float Clamp(double x, double min, double max)
		{
			return (float)Math.Max(min, Math.Min(max, x));
		}

		public void Validate(ActiveModifier[] effects, string[] available, PersistenceMode persistence = PersistenceMode.StackForRun)
		{
			if (available.Distinct().Count() != available.Length || available.Any((string id) => Get(id) == null) || effects.Select((ActiveModifier e) => e.Id).Distinct().Count() != effects.Length)
			{
				throw new ArgumentException("Unknown or duplicate modifiers");
			}
			HashSet<string> hashSet = new HashSet<string>();
			foreach (ActiveModifier activeModifier in effects)
			{
				ModifierDefinition modifierDefinition = Get(activeModifier.Id);
				if (modifierDefinition == null || !available.Contains(activeModifier.Id) || activeModifier.FirstHole < 0 || activeModifier.FirstHole > 999 || activeModifier.Variant < 0 || activeModifier.Variant > 31 || (!CanonicalCatalog.IsTimed(modifierDefinition.Rule) && activeModifier.Variant != 0) || activeModifier.RemainingHoles != ((modifierDefinition.Duration != EffectDuration.Run || persistence == PersistenceMode.PerHole) ? 1 : (-1)) || (modifierDefinition.ExclusiveGroup != "" && !hashSet.Add(modifierDefinition.ExclusiveGroup)))
				{
					throw new ArgumentException("Invalid active modifier");
				}
			}
		}
	}
	public sealed class ModifierManager
	{
		private readonly HashSet<string> available;

		private readonly List<ActiveModifier> active = new List<ActiveModifier>();

		private int breatherHole = -1;

		public ModifierRegistry Registry { get; }

		public ActiveModifier[] Active => active.Select(delegate(ActiveModifier e)
		{
			ActiveModifier activeModifier = e.Copy();
			if (breatherHole >= 0)
			{
				activeModifier.Suspended = true;
			}
			return activeModifier;
		}).ToArray();

		public string[] Available => available.OrderBy<string, string>((string id) => id, StringComparer.Ordinal).ToArray();

		public EffectValues Values => Registry.Compose(Active);

		public bool HasEligible => Available.Any(CanActivate);

		public Func<ModifierDefinition, bool> ContextAllows { get; set; } = (ModifierDefinition d) => true;

		public ModifierManager(ModifierRegistry registry, IEnumerable<string> enabled, EffectChannel capabilities)
		{
			Registry = registry;
			available = new HashSet<string>(enabled.Where((string id) => registry.Get(id) != null && (registry.Get(id).Channel & capabilities) == registry.Get(id).Channel));
		}

		public bool CanActivate(string id)
		{
			ModifierDefinition d = Registry.Get(id);
			if (d != null && ContextAllows(d) && available.Contains(id))
			{
				return !active.Any((ActiveModifier a) => a.Id == id || (d.ExclusiveGroup != "" && Registry.Get(a.Id).ExclusiveGroup == d.ExclusiveGroup));
			}
			return false;
		}

		public bool Activate(string id, int firstHole, PersistenceMode persistence = PersistenceMode.StackForRun)
		{
			if (firstHole < 0 || firstHole > 999 || !CanActivate(id))
			{
				return false;
			}
			ModifierDefinition modifierDefinition = Registry.Get(id);
			active.Add(new ActiveModifier(id, firstHole, (modifierDefinition.Duration != EffectDuration.Run || persistence == PersistenceMode.PerHole) ? 1 : (-1)));
			return true;
		}

		public bool CompleteHole(int hole)
		{
			bool flag = breatherHole >= 0 && hole >= breatherHole;
			if (flag)
			{
				breatherHole = -1;
			}
			return active.RemoveAll((ActiveModifier e) => e.CompleteHole(hole)) > 0 || flag;
		}

		public void SuspendForHole(int hole)
		{
			if (hole < 0 || hole > 999)
			{
				throw new ArgumentOutOfRangeException("hole");
			}
			breatherHole = hole;
		}

		public bool Remove(string id)
		{
			return active.RemoveAll((ActiveModifier e) => e.Id == id) > 0;
		}

		public bool Suspend(string id, bool value)
		{
			ActiveModifier activeModifier = active.FirstOrDefault((ActiveModifier a) => a.Id == id);
			if (activeModifier == null || activeModifier.Suspended == value)
			{
				return false;
			}
			activeModifier.Suspended = value;
			return true;
		}

		public bool SetVariant(string id, int variant)
		{
			ActiveModifier activeModifier = active.FirstOrDefault((ActiveModifier a) => a.Id == id);
			if (activeModifier == null || variant < 0 || variant > 31 || !CanonicalCatalog.IsTimed(Registry.Get(id).Rule))
			{
				return false;
			}
			activeModifier.Variant = variant;
			return true;
		}

		public VoteOption[] Candidates(int count, bool skip, RunRandom random)
		{
			if (count < 1 || count > 5)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			List<ModifierDefinition> list = Available.Where(CanActivate).Select(Registry.Get).ToList();
			List<VoteOption> list2 = new List<VoteOption>();
			while (list.Count > 0 && list2.Count < count)
			{
				int num = random.Next(list.Sum((ModifierDefinition d) => d.Weight));
				int index = 0;
				while (num >= list[index].Weight)
				{
					num -= list[index++].Weight;
				}
				ModifierDefinition modifierDefinition = list[index];
				list.RemoveAt(index);
				list2.Add(new VoteOption(modifierDefinition.Id, modifierDefinition.Name, modifierDefinition.Description));
			}
			if (list2.Count > 0 && skip)
			{
				list2.Add(new VoteOption("skip", "Skip", "No new modifier. Uses one shared skip."));
			}
			return list2.ToArray();
		}

		public void Clear()
		{
			active.Clear();
			breatherHole = -1;
		}
	}
	public sealed class ScalarLease
	{
		private readonly Func<float> read;

		private readonly Action<float> write;

		private bool owned;

		private float baseline;

		public ScalarLease(Func<float> read, Action<float> write)
		{
			this.read = read;
			this.write = write;
		}

		public void Apply(float factor)
		{
			if (!VoteSettings.Finite(factor) || factor <= 0f || factor > 10f)
			{
				throw new ArgumentOutOfRangeException("factor");
			}
			if (factor == 1f)
			{
				Restore();
				return;
			}
			if (!owned)
			{
				baseline = read();
				owned = true;
			}
			write(baseline * factor);
		}

		public void Restore()
		{
			if (owned)
			{
				write(baseline);
				owned = false;
			}
		}
	}
	public enum SelectionMode
	{
		Vote,
		Random,
		Hybrid
	}
	public enum PersistenceMode
	{
		StackForRun,
		PerHole,
		Mixed
	}
	public enum SkipMode
	{
		SharedRun,
		PerPlayer,
		Unlimited,
		Disabled
	}
	public enum SkipBehavior
	{
		NoNewModifier,
		VanillaHole
	}
	public sealed class ProgressionSettings
	{
		private readonly int[] tiers;

		private readonly int[] starts;

		private readonly Dictionary<string, int> weights;

		public SelectionMode Selection { get; }

		public PersistenceMode Persistence { get; }

		public SkipMode Skips { get; }

		public SkipBehavior SkipEffect { get; }

		public bool BossEnabled { get; }

		public int BossInterval { get; }

		public bool RandomSkips { get; }

		public int RandomSkipWeight { get; }

		public int RandomModifierWeight { get; }

		public int[] TierWeights => (int[])tiers.Clone();

		public int[] BandStarts => (int[])starts.Clone();

		public KeyValuePair<string, int>[] ModifierWeights => weights.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal).ToArray();

		public int Weight(string id)
		{
			if (!weights.TryGetValue(id, out var value))
			{
				return 100;
			}
			return value;
		}

		public ProgressionSettings(SelectionMode selection = SelectionMode.Vote, PersistenceMode persistence = PersistenceMode.StackForRun, SkipMode skips = SkipMode.SharedRun, SkipBehavior skipEffect = SkipBehavior.NoNewModifier, bool bossEnabled = true, int bossInterval = 4, bool randomSkips = false, int randomSkipWeight = 10, int randomModifierWeight = 100, int[] tierWeights = null, int[] bandStarts = null, IEnumerable<KeyValuePair<string, int>> modifierWeights = null)
		{
			if (!Enum.IsDefined(typeof(SelectionMode), selection) || !Enum.IsDefined(typeof(PersistenceMode), persistence) || !Enum.IsDefined(typeof(SkipMode), skips) || !Enum.IsDefined(typeof(SkipBehavior), skipEffect) || bossInterval < 1 || bossInterval > 99 || randomSkipWeight < 0 || randomSkipWeight > 10000 || randomModifierWeight < 1 || randomModifierWeight > 10000)
			{
				throw new ArgumentException("Invalid progression settings");
			}
			tiers = (int[])(tierWeights ?? new int[14]
			{
				75, 25, 0, 55, 35, 10, 35, 45, 20, 20,
				45, 35, 25, 75
			}).Clone();
			starts = (int[])(bandStarts ?? new int[4] { 2, 4, 6, 8 }).Clone();
			weights = ((modifierWeights == null) ? new Dictionary<string, int>() : modifierWeights.ToDictionary<KeyValuePair<string, int>, string, int>((KeyValuePair<string, int> p) => p.Key, (KeyValuePair<string, int> p) => p.Value, StringComparer.Ordinal));
			if (tiers.Length != 14 || tiers.Any((int w) => w < 0 || w > 10000) || starts.Length != 4 || starts[0] != 2 || starts.Any((int h) => h < 2 || h > 999) || !starts.SequenceEqual(from h in starts.Distinct()
				orderby h
				select h) || weights.Count > 128 || weights.Any((KeyValuePair<string, int> p) => string.IsNullOrEmpty(p.Key) || p.Key.Length > 32 || p.Value < 0 || p.Value > 10000))
			{
				throw new ArgumentException("Invalid progression weights or bands");
			}
			Selection = selection;
			Persistence = persistence;
			Skips = skips;
			SkipEffect = skipEffect;
			BossEnabled = bossEnabled;
			BossInterval = bossInterval;
			RandomSkips = randomSkips;
			RandomSkipWeight = randomSkipWeight;
			RandomModifierWeight = randomModifierWeight;
		}

		public bool IsBoss(int selectionNumber)
		{
			if (BossEnabled && selectionNumber > 0)
			{
				return selectionNumber % BossInterval == 0;
			}
			return false;
		}

		public int TierWeight(int upcomingHole, ModifierRarity rarity, bool boss)
		{
			if (boss)
			{
				return rarity switch
				{
					ModifierRarity.Boss => tiers[13], 
					ModifierRarity.Legendary => tiers[12], 
					_ => 0, 
				};
			}
			if (rarity == ModifierRarity.Boss)
			{
				return 0;
			}
			int num = 0;
			for (int i = 1; i < starts.Length; i++)
			{
				if (upcomingHole >= starts[i])
				{
					num = i;
				}
			}
			return tiers[(int)(num * 3 + rarity)];
		}
	}
	public static class ProgressionCandidates
	{
		public static VoteOption[] Generate(ModifierManager manager, ProgressionSettings settings, int upcomingHole, int selectionNumber, int count, RunRandom random)
		{
			if (count < 1 || count > 5 || upcomingHole < 2 || upcomingHole > 1000 || selectionNumber < 1)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			bool boss = settings.IsBoss(selectionNumber);
			List<ModifierDefinition> list = (from d in manager.Available.Where(manager.CanActivate).Select(manager.Registry.Get)
				where settings.Weight(d.Id) > 0 && settings.TierWeight(upcomingHole, d.Rarity, boss) > 0
				select d).OrderBy<ModifierDefinition, string>((ModifierDefinition d) => d.Id, StringComparer.Ordinal).ToList();
			List<VoteOption> list2 = new List<VoteOption>();
			while (list.Count > 0 && list2.Count < count)
			{
				ModifierRarity[] array = (from t in list.Select((ModifierDefinition d) => d.Rarity).Distinct()
					orderby t
					select t).ToArray();
				int num = random.Next(array.Sum((ModifierRarity t) => settings.TierWeight(upcomingHole, t, boss)));
				ModifierRarity tier = array[0];
				ModifierRarity[] array2 = array;
				for (int num2 = 0; num2 < array2.Length; num2++)
				{
					ModifierRarity rarity = (tier = array2[num2]);
					num -= settings.TierWeight(upcomingHole, rarity, boss);
					if (num < 0)
					{
						break;
					}
				}
				ModifierDefinition[] array3 = list.Where((ModifierDefinition d) => d.Rarity == tier).ToArray();
				num = random.Next(array3.Sum((ModifierDefinition d) => settings.Weight(d.Id)));
				ModifierDefinition modifierDefinition = array3[0];
				ModifierDefinition[] array4 = array3;
				foreach (ModifierDefinition modifierDefinition2 in array4)
				{
					modifierDefinition = modifierDefinition2;
					num -= settings.Weight(modifierDefinition2.Id);
					if (num < 0)
					{
						break;
					}
				}
				string text = modifierDefinition.Description;
				if (settings.Persistence == PersistenceMode.PerHole)
				{
					text = text.Replace("For this run.", "Next hole only.");
				}
				list2.Add(new VoteOption(modifierDefinition.Id, modifierDefinition.Name, text));
				list.Remove(modifierDefinition);
			}
			return list2.ToArray();
		}

		public static string Automatic(VoteOption[] candidates, ProgressionSettings settings, bool skipAvailable, RunRandom random)
		{
			if (candidates.Length == 0)
			{
				return "none";
			}
			if (skipAvailable && settings.RandomSkips && settings.RandomSkipWeight > 0 && random.Next(settings.RandomModifierWeight + settings.RandomSkipWeight) < settings.RandomSkipWeight)
			{
				return "skip";
			}
			return candidates[random.Next(candidates.Length)].Id;
		}
	}
	public sealed class SkipLedger
	{
		private readonly SkipMode mode;

		private readonly int initial;

		private readonly Dictionary<ulong, int> personal = new Dictionary<ulong, int>();

		public int Shared { get; private set; }

		public SkipLedger(SkipMode mode, int budget)
		{
			if (budget < 0 || budget > 99)
			{
				throw new ArgumentOutOfRangeException("budget");
			}
			this.mode = mode;
			initial = budget;
			Shared = budget;
		}

		public int Remaining(ulong player)
		{
			if (mode == SkipMode.Disabled)
			{
				return 0;
			}
			if (mode == SkipMode.Unlimited)
			{
				return 99;
			}
			if (mode == SkipMode.SharedRun)
			{
				return Shared;
			}
			if (player == 0L)
			{
				return 0;
			}
			if (!personal.TryGetValue(player, out var value))
			{
				return initial;
			}
			return value;
		}

		public bool Charge(IEnumerable<ulong> payers)
		{
			ulong[] array = payers.Distinct().ToArray();
			if (array.Length == 0 || array.Any((ulong id) => Remaining(id) <= 0))
			{
				return false;
			}
			if (mode == SkipMode.SharedRun)
			{
				Shared--;
			}
			if (mode == SkipMode.PerPlayer)
			{
				ulong[] array2 = array;
				foreach (ulong num2 in array2)
				{
					personal[num2] = Remaining(num2) - 1;
				}
			}
			return true;
		}
	}
	public sealed class RunChoice
	{
		public int Hole { get; }

		public string Id { get; }

		public bool Boss { get; }

		public RunChoice(int hole, string id, bool boss)
		{
			if (hole < 2 || hole > 1000 || string.IsNullOrEmpty(id) || id.Length > 32)
			{
				throw new ArgumentException("Invalid run choice");
			}
			Hole = hole;
			Id = id;
			Boss = boss;
		}
	}
	public enum PacketKind : byte
	{
		Hello = 1,
		Snapshot,
		Vote,
		BreakTie
	}
	public sealed class RunSnapshot
	{
		public string Session = "";

		public string RunId = "";

		public string Winner = "";

		public string Notice = "";

		public long Revision;

		public RunState State;

		public int Hole = -1;

		public int Skips;

		public int VoteId;

		public int Recipient;

		public int Host;

		public uint Seed;

		public bool ModifierMode;

		public ProgressionSettings Progression = new ProgressionSettings();

		public int SelectionNumber;

		public bool BossSelection;

		public bool SummaryComplete;

		public RunChoice[] History = Array.Empty<RunChoice>();

		public Dictionary<int, int> PersonalSkips = new Dictionary<int, int>();

		public Dictionary<ulong, float> PlayerShotFactors = new Dictionary<ulong, float>();

		public GravityVector GravityBaseline;

		public ModifierSettings ModifierSettings = new ModifierSettings();

		public string[] AvailableModifiers = Array.Empty<string>();

		public ActiveModifier[] ActiveModifiers = Array.Empty<ActiveModifier>();

		public double Deadline;

		public double TieDeadline;

		public double RevealUntil;

		public double NextCycleAt;

		public VoteSettings Settings = new VoteSettings();

		public VoteOption[] Options = Array.Empty<VoteOption>();

		public int[] Eligible = Array.Empty<int>();

		public Dictionary<int, string> Ballots = new Dictionary<int, string>();

		public string[] Tied = Array.Empty<string>();
	}
	public sealed class Packet
	{
		public PacketKind Kind;

		public string Session = "";

		public string Nonce = "";

		public string RunId = "";

		public string Option = "";

		public int VoteId;

		public long Sequence;

		public EffectChannel Capabilities;

		public RunSnapshot Snapshot;
	}
	public static class Wire
	{
		public const int Version = 7;

		public const int MaxBytes = 16384;

		public const int MaxPlayers = 64;

		private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

		public static byte[] Encode(Packet packet)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Utf8, leaveOpen: true);
			binaryWriter.Write(7);
			binaryWriter.Write((byte)packet.Kind);
			Text(binaryWriter, packet.Session, 64);
			if (packet.Kind == PacketKind.Hello)
			{
				Text(binaryWriter, packet.Nonce, 64);
				binaryWriter.Write((ushort)packet.Capabilities);
			}
			else if (packet.Kind == PacketKind.Snapshot)
			{
				WriteSnapshot(binaryWriter, packet.Snapshot);
			}
			else
			{
				if (packet.Kind != PacketKind.Vote && packet.Kind != PacketKind.BreakTie)
				{
					throw new InvalidDataException("Unknown packet kind");
				}
				Text(binaryWriter, packet.RunId, 64);
				binaryWriter.Write(packet.VoteId);
				binaryWriter.Write(packet.Sequence);
				Text(binaryWriter, packet.Option, 32);
			}
			binaryWriter.Flush();
			if (memoryStream.Length > 16384)
			{
				throw new InvalidDataException("Oversized packet");
			}
			return memoryStream.ToArray();
		}

		public static Packet Decode(byte[] bytes)
		{
			if (bytes == null || bytes.Length > 16384)
			{
				throw new InvalidDataException("Oversized packet");
			}
			using MemoryStream memoryStream = new MemoryStream(bytes, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream, Utf8, leaveOpen: true);
			if (binaryReader.ReadInt32() != 7)
			{
				throw new InvalidDataException("Protocol mismatch");
			}
			Packet packet = new Packet
			{
				Kind = (PacketKind)binaryReader.ReadByte(),
				Session = Text(binaryReader, 64)
			};
			if (packet.Kind == PacketKind.Hello)
			{
				packet.Nonce = Text(binaryReader, 64);
				packet.Capabilities = (EffectChannel)binaryReader.ReadUInt16();
				if ((packet.Capabilities & ~EffectChannel.All) != EffectChannel.None)
				{
					throw new InvalidDataException("Invalid capabilities");
				}
			}
			else if (packet.Kind == PacketKind.Snapshot)
			{
				packet.Snapshot = ReadSnapshot(binaryReader);
				packet.Snapshot.Session = packet.Session;
			}
			else
			{
				if (packet.Kind != PacketKind.Vote && packet.Kind != PacketKind.BreakTie)
				{
					throw new InvalidDataException("Unknown packet kind");
				}
				packet.RunId = Text(binaryReader, 64);
				packet.VoteId = binaryReader.ReadInt32();
				packet.Sequence = binaryReader.ReadInt64();
				packet.Option = Text(binaryReader, 32);
			}
			if (memoryStream.Position != memoryStream.Length)
			{
				throw new InvalidDataException("Trailing bytes");
			}
			return packet;
		}

		private static void WriteSnapshot(BinaryWriter w, RunSnapshot s)
		{
			Text(w, s.RunId, 64);
			w.Write(s.Revision);
			w.Write((byte)s.State);
			w.Write(s.Hole);
			w.Write(s.Skips);
			w.Write(s.VoteId);
			w.Write(s.Recipient);
			w.Write(s.Host);
			w.Write(s.Seed);
			w.Write(s.ModifierMode);
			WriteProgression(w, s);
			w.Write(s.GravityBaseline.X);
			w.Write(s.GravityBaseline.Y);
			w.Write(s.GravityBaseline.Z);
			w.Write(s.ModifierSettings.PowerSurge);
			w.Write(s.ModifierSettings.HeavyBall);
			w.Write(s.ModifierSettings.Featherweight);
			w.Write(s.ModifierSettings.LowGravity);
			w.Write(s.ModifierSettings.HighGravity);
			Count(w, s.ModifierSettings.Extra.Length, 200);
			KeyValuePair<string, float>[] extra = s.ModifierSettings.Extra;
			for (int i = 0; i < extra.Length; i++)
			{
				KeyValuePair<string, float> keyValuePair = extra[i];
				Text(w, keyValuePair.Key, 64);
				w.Write(keyValuePair.Value);
			}
			Count(w, s.PlayerShotFactors.Count, 64);
			foreach (KeyValuePair<ulong, float> item in s.PlayerShotFactors.OrderBy((KeyValuePair<ulong, float> p) => p.Key))
			{
				w.Write(item.Key);
				w.Write(item.Value);
			}
			Count(w, s.AvailableModifiers.Length, 128);
			string[] availableModifiers = s.AvailableModifiers;
			foreach (string value in availableModifiers)
			{
				Text(w, value, 32);
			}
			Count(w, s.ActiveModifiers.Length, 128);
			ActiveModifier[] activeModifiers = s.ActiveModifiers;
			foreach (ActiveModifier activeModifier in activeModifiers)
			{
				Text(w, activeModifier.Id, 32);
				w.Write(activeModifier.FirstHole);
				w.Write(activeModifier.RemainingHoles);
				w.Write(activeModifier.Suspended);
				w.Write(activeModifier.Variant);
			}
			w.Write(s.Deadline);
			w.Write(s.TieDeadline);
			w.Write(s.RevealUntil);
			w.Write(s.NextCycleAt);
			Text(w, s.Winner, 32);
			Text(w, s.Notice, 256);
			w.Write(s.Settings.CandidateCount);
			w.Write(s.Settings.Duration);
			w.Write(s.Settings.AllowChanges);
			w.Write(s.Settings.EarlyClose);
			w.Write((byte)s.Settings.Tie);
			w.Write(s.Settings.SkipBudget);
			w.Write(s.Settings.RevealSeconds);
			Count(w, s.Options.Length, 6);
			VoteOption[] options = s.Options;
			foreach (VoteOption voteOption in options)
			{
				Text(w, voteOption.Id, 32);
				Text(w, voteOption.Name, 64);
				Text(w, voteOption.Description, 256);
			}
			Count(w, s.Eligible.Length, 64);
			int[] eligible = s.Eligible;
			foreach (int value2 in eligible)
			{
				w.Write(value2);
			}
			Count(w, s.Ballots.Count, 64);
			foreach (KeyValuePair<int, string> item2 in s.Ballots.OrderBy((KeyValuePair<int, string> x) => x.Key))
			{
				w.Write(item2.Key);
				Text(w, item2.Value, 32);
			}
			Count(w, s.Tied.Length, 6);
			availableModifiers = s.Tied;
			foreach (string value3 in availableModifiers)
			{
				Text(w, value3, 32);
			}
		}

		private static RunSnapshot ReadSnapshot(BinaryReader r)
		{
			RunSnapshot s = new RunSnapshot
			{
				RunId = Text(r, 64),
				Revision = r.ReadInt64(),
				State = (RunState)r.ReadByte(),
				Hole = r.ReadInt32(),
				Skips = r.ReadInt32(),
				VoteId = r.ReadInt32(),
				Recipient = r.ReadInt32(),
				Host = r.ReadInt32(),
				Seed = r.ReadUInt32(),
				ModifierMode = r.ReadBoolean()
			};
			ReadProgression(r, s);
			s.GravityBaseline = new GravityVector(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
			float power = r.ReadSingle();
			float heavy = r.ReadSingle();
			float feather = r.ReadSingle();
			float low = r.ReadSingle();
			float high = r.ReadSingle();
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			int num = Count(r, 200);
			for (int i = 0; i < num; i++)
			{
				dictionary.Add(Text(r, 64), r.ReadSingle());
			}
			s.ModifierSettings = new ModifierSettings(power, heavy, feather, low, high, dictionary);
			int num2 = Count(r, 64);
			for (int j = 0; j < num2; j++)
			{
				ulong key = r.ReadUInt64();
				float num3 = r.ReadSingle();
				if (!VoteSettings.Finite(num3) || num3 < 0.5f || num3 > 1.5f)
				{
					throw new InvalidDataException("Invalid rank factor");
				}
				s.PlayerShotFactors.Add(key, num3);
			}
			s.AvailableModifiers = new string[Count(r, 128)];
			for (int k = 0; k < s.AvailableModifiers.Length; k++)
			{
				s.AvailableModifiers[k] = Text(r, 32);
			}
			s.ActiveModifiers = new ActiveModifier[Count(r, 128)];
			for (int l = 0; l < s.ActiveModifiers.Length; l++)
			{
				s.ActiveModifiers[l] = new ActiveModifier(Text(r, 32), r.ReadInt32(), r.ReadInt32(), r.ReadBoolean())
				{
					Variant = r.ReadInt32()
				};
			}
			ModifierRegistry.Create(s.ModifierSettings).Validate(s.ActiveModifiers, s.AvailableModifiers, s.Progression.Persistence);
			s.Deadline = r.ReadDouble();
			s.TieDeadline = r.ReadDouble();
			s.RevealUntil = r.ReadDouble();
			s.NextCycleAt = r.ReadDouble();
			s.Winner = Text(r, 32);
			s.Notice = Text(r, 256);
			if (s.Revision < 0 || !Enum.IsDefined(typeof(RunState), s.State) || s.Hole < -1 || s.Hole > 999 || s.Skips < 0 || s.Skips > 99 || !VoteSettings.Finite(s.Deadline) || !VoteSettings.Finite(s.TieDeadline) || !VoteSettings.Finite(s.RevealUntil) || !VoteSettings.Finite(s.NextCycleAt) || s.NextCycleAt < 0.0)
			{
				throw new InvalidDataException("Invalid snapshot fields");
			}
			if (s.ActiveModifiers.Length != 0 && (!s.ModifierMode || s.RunId == "" || s.State == RunState.Inactive || s.State == RunState.CleaningUp || s.State == RunState.RunComplete))
			{
				throw new InvalidDataException("Effect outside an active modifier run");
			}
			s.Settings = new VoteSettings(r.ReadInt32(), r.ReadDouble(), r.ReadBoolean(), r.ReadBoolean(), (TieRule)r.ReadByte(), r.ReadInt32(), r.ReadDouble());
			s.Options = new VoteOption[Count(r, 6)];
			for (int m = 0; m < s.Options.Length; m++)
			{
				s.Options[m] = new VoteOption(Text(r, 32), Text(r, 64), Text(r, 256));
			}
			s.Eligible = new int[Count(r, 64)];
			for (int n = 0; n < s.Eligible.Length; n++)
			{
				s.Eligible[n] = r.ReadInt32();
			}
			int num4 = Count(r, 64);
			for (int num5 = 0; num5 < num4; num5++)
			{
				int key2 = r.ReadInt32();
				if (s.Ballots.ContainsKey(key2))
				{
					throw new InvalidDataException("Duplicate ballot");
				}
				s.Ballots.Add(key2, Text(r, 32));
			}
			s.Tied = new string[Count(r, 6)];
			for (int num6 = 0; num6 < s.Tied.Length; num6++)
			{
				s.Tied[num6] = Text(r, 32);
			}
			HashSet<string> ids = new HashSet<string>(s.Options.Select((VoteOption o) => o.Id));
			if (ids.Count != s.Options.Length || s.Eligible.Distinct().Count() != s.Eligible.Length || s.Ballots.Any((KeyValuePair<int, string> b) => !s.Eligible.Contains(b.Key) || !ids.Contains(b.Value)) || s.Tied.Any((string t) => !ids.Contains(t)) || (s.Winner != "" && !ids.Contains(s.Winner)))
			{
				throw new InvalidDataException("Inconsistent snapshot");
			}
			return s;
		}

		private static void WriteProgression(BinaryWriter w, RunSnapshot s)
		{
			ProgressionSettings progression = s.Progression;
			w.Write((byte)progression.Selection);
			w.Write((byte)progression.Persistence);
			w.Write((byte)progression.Skips);
			w.Write((byte)progression.SkipEffect);
			w.Write(progression.BossEnabled);
			w.Write(progression.BossInterval);
			w.Write(progression.RandomSkips);
			w.Write(progression.RandomSkipWeight);
			w.Write(progression.RandomModifierWeight);
			int[] tierWeights = progression.TierWeights;
			foreach (int value in tierWeights)
			{
				w.Write(value);
			}
			tierWeights = progression.BandStarts;
			foreach (int value2 in tierWeights)
			{
				w.Write(value2);
			}
			Count(w, progression.ModifierWeights.Length, 128);
			KeyValuePair<string, int>[] modifierWeights = progression.ModifierWeights;
			for (int i = 0; i < modifierWeights.Length; i++)
			{
				KeyValuePair<string, int> keyValuePair = modifierWeights[i];
				Text(w, keyValuePair.Key, 32);
				w.Write(keyValuePair.Value);
			}
			w.Write(s.SelectionNumber);
			w.Write(s.BossSelection);
			w.Write(s.SummaryComplete);
			Count(w, s.History.Length, 64);
			RunChoice[] history = s.History;
			foreach (RunChoice runChoice in history)
			{
				w.Write(runChoice.Hole);
				Text(w, runChoice.Id, 32);
				w.Write(runChoice.Boss);
			}
			Count(w, s.PersonalSkips.Count, 64);
			foreach (KeyValuePair<int, int> item in s.PersonalSkips.OrderBy((KeyValuePair<int, int> pv) => pv.Key))
			{
				w.Write(item.Key);
				w.Write(item.Value);
			}
		}

		private static void ReadProgression(BinaryReader r, RunSnapshot s)
		{
			SelectionMode selection = (SelectionMode)r.ReadByte();
			PersistenceMode persistence = (PersistenceMode)r.ReadByte();
			SkipMode skips = (SkipMode)r.ReadByte();
			SkipBehavior skipEffect = (SkipBehavior)r.ReadByte();
			bool bossEnabled = r.ReadBoolean();
			int bossInterval = r.ReadInt32();
			bool randomSkips = r.ReadBoolean();
			int randomSkipWeight = r.ReadInt32();
			int randomModifierWeight = r.ReadInt32();
			int[] array = new int[14];
			for (int i = 0; i < 14; i++)
			{
				array[i] = r.ReadInt32();
			}
			int[] array2 = new int[4];
			for (int j = 0; j < 4; j++)
			{
				array2[j] = r.ReadInt32();
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			int num = Count(r, 128);
			for (int k = 0; k < num; k++)
			{
				dictionary.Add(Text(r, 32), r.ReadInt32());
			}
			s.Progression = new ProgressionSettings(selection, persistence, skips, skipEffect, bossEnabled, bossInterval, randomSkips, randomSkipWeight, randomModifierWeight, array, array2, dictionary);
			s.SelectionNumber = r.ReadInt32();
			s.BossSelection = r.ReadBoolean();
			s.SummaryComplete = r.ReadBoolean();
			ModifierRegistry registry = ModifierRegistry.Create(new ModifierSettings());
			if (s.SelectionNumber < 0 || s.SelectionNumber > 999 || dictionary.Keys.Any((string id) => registry.Get(id) == null))
			{
				throw new InvalidDataException("Invalid progression state");
			}
			s.History = new RunChoice[Count(r, 64)];
			for (int num2 = 0; num2 < s.History.Length; num2++)
			{
				RunChoice runChoice = new RunChoice(r.ReadInt32(), Text(r, 32), r.ReadBoolean());
				if ((runChoice.Id != "skip" && runChoice.Id != "none" && registry.Get(runChoice.Id) == null) || (num2 > 0 && s.History[num2 - 1].Hole >= runChoice.Hole))
				{
					throw new InvalidDataException("Invalid run history");
				}
				s.History[num2] = runChoice;
			}
			if (s.History.Length > s.SelectionNumber)
			{
				throw new InvalidDataException("History exceeds selections");
			}
			num = Count(r, 64);
			for (int num3 = 0; num3 < num; num3++)
			{
				int key = r.ReadInt32();
				int num4 = r.ReadInt32();
				if (num4 < 0 || num4 > 99)
				{
					throw new InvalidDataException("Invalid personal skip budget");
				}
				s.PersonalSkips.Add(key, num4);
			}
		}

		private static void Text(BinaryWriter w, string value, int max)
		{
			byte[] bytes = Utf8.GetBytes(value ?? "");
			if (bytes.Length > max)
			{
				throw new InvalidDataException("String too long");
			}
			w.Write((ushort)bytes.Length);
			w.Write(bytes);
		}

		private static string Text(BinaryReader r, int max)
		{
			int num = r.ReadUInt16();
			if (num > max)
			{
				throw new InvalidDataException("String too long");
			}
			byte[] array = r.ReadBytes(num);
			if (array.Length != num)
			{
				throw new EndOfStreamException();
			}
			return Utf8.GetString(array);
		}

		private static void Count(BinaryWriter w, int count, int max)
		{
			if (count < 0 || count > max)
			{
				throw new InvalidDataException("Collection too large");
			}
			w.Write((byte)count);
		}

		private static int Count(BinaryReader r, int max)
		{
			byte num = r.ReadByte();
			if (num > max)
			{
				throw new InvalidDataException("Collection too large");
			}
			return num;
		}
	}
	public sealed class Replica
	{
		private string session = "";

		public RunSnapshot Current { get; private set; }

		public void Reset(string expectedSession)
		{
			session = expectedSession;
			Current = null;
		}

		public bool Accept(RunSnapshot snapshot)
		{
			if (snapshot.Session != session || (Current != null && snapshot.Revision <= Current.Revision))
			{
				return false;
			}
			Current = snapshot;
			return true;
		}
	}
	public static class RankEffects
	{
		public static Dictionary<ulong, float> Calculate(IEnumerable<ulong> nativeOrder, Mechanic rules, ModifierSettings settings)
		{
			ulong[] array = nativeOrder.ToArray();
			if (array.Length > 64 || array.Distinct().Count() != array.Length)
			{
				throw new ArgumentException("Invalid leaderboard");
			}
			Dictionary<ulong, float> dictionary = new Dictionary<ulong, float>();
			if (array.Length < 2)
			{
				return dictionary;
			}
			for (int i = 0; i < array.Length; i++)
			{
				float num = 1f;
				if (i == 0 && (rules & Mechanic.LeaderTax) != Mechanic.None)
				{
					num *= settings.Get("LeaderTaxFactor", 0.8f);
				}
				if (i == array.Length - 1 && (rules & Mechanic.LastPlaceInsurance) != Mechanic.None)
				{
					num *= settings.Get("LastPlaceFactor", 1.2f);
				}
				if ((rules & Mechanic.ComebackSeason) != Mechanic.None)
				{
					num *= 1f + (settings.Get("ComebackMaximum", 1.3f) - 1f) * (float)i / (float)(array.Length - 1);
				}
				dictionary[array[i]] = Math.Max(0.5f, Math.Min(1.5f, num));
			}
			return dictionary;
		}
	}
	public enum RunState
	{
		Inactive,
		InitializingRun,
		StartingHole,
		PlayingHole,
		HoleEnding,
		PreparingVote,
		Voting,
		ResolvingVote,
		ApplyingModifier,
		RunComplete,
		CleaningUp
	}
	public sealed class RunController
	{
		public RunState State { get; private set; }

		public string RunId { get; private set; } = "";

		public long Revision { get; private set; }

		public int HoleIndex { get; private set; } = -1;

		public bool Active => State != RunState.Inactive;

		public event Action<string> Changed;

		public event Action<string> Warning;

		public bool Begin(string id)
		{
			if (Active)
			{
				return false;
			}
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("Run identity is required.", "id");
			}
			RunId = id;
			HoleIndex = -1;
			Move(RunState.InitializingRun, "RunStarted");
			return true;
		}

		public bool HoleLoaded(int index)
		{
			if (!Active || index < 0 || index <= HoleIndex)
			{
				return false;
			}
			if (State != RunState.InitializingRun && State != RunState.StartingHole && State != RunState.HoleEnding)
			{
				return Invalid("HoleLoaded");
			}
			HoleIndex = index;
			Move(RunState.StartingHole, "HoleLoaded");
			return true;
		}

		public bool HoleStarted(int index)
		{
			if (!Active || index != HoleIndex || State == RunState.PlayingHole)
			{
				return false;
			}
			if (State != RunState.StartingHole)
			{
				return Invalid("HoleStarted");
			}
			Move(RunState.PlayingHole, "HoleStarted");
			return true;
		}

		public bool HoleEnded(int index)
		{
			if (!Active || index != HoleIndex || State == RunState.HoleEnding)
			{
				return false;
			}
			if (State != RunState.PlayingHole && State != RunState.StartingHole)
			{
				return false;
			}
			Move(RunState.HoleEnding, "HoleEnded");
			return true;
		}

		public bool Transition(RunState expected, RunState next, string reason)
		{
			if (State != expected || !Allowed(expected, next))
			{
				return Invalid(reason);
			}
			Move(next, reason);
			return true;
		}

		public void Complete()
		{
			if (Active)
			{
				Move(RunState.RunComplete, "RunCompleted");
				Stop("CourseComplete");
			}
		}

		public void Stop(string reason)
		{
			if (Active)
			{
				Move(RunState.CleaningUp, "Cleanup:" + reason);
				HoleIndex = -1;
				Move(RunState.Inactive, "RunStopped:" + reason);
			}
		}

		private bool Invalid(string signal)
		{
			this.Warning?.Invoke(signal + " rejected in " + State);
			return false;
		}

		private static bool Allowed(RunState from, RunState to)
		{
			switch (from)
			{
			case RunState.InitializingRun:
				return to == RunState.StartingHole;
			case RunState.StartingHole:
				return to == RunState.PlayingHole;
			case RunState.PlayingHole:
				return to == RunState.HoleEnding;
			case RunState.HoleEnding:
				if (to != RunState.PreparingVote)
				{
					return to == RunState.StartingHole;
				}
				return true;
			case RunState.PreparingVote:
				if (to != RunState.Voting)
				{
					return to == RunState.StartingHole;
				}
				return true;
			case RunState.Voting:
				if (to != RunState.ResolvingVote)
				{
					return to == RunState.StartingHole;
				}
				return true;
			case RunState.ResolvingVote:
				if (to != RunState.ApplyingModifier)
				{
					return to == RunState.StartingHole;
				}
				return true;
			case RunState.ApplyingModifier:
				return to == RunState.StartingHole;
			default:
				return false;
			}
		}

		private void Move(RunState state, string reason)
		{
			State = state;
			Revision++;
			this.Changed?.Invoke(reason);
		}
	}
	public static class SecureVoteRandom
	{
		public static int Next(int count)
		{
			if (count <= 0)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			ulong num = 4294967296L - 4294967296uL % (ulong)(uint)count;
			byte[] array = new byte[4];
			using RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
			uint num2;
			do
			{
				randomNumberGenerator.GetBytes(array);
				num2 = BitConverter.ToUInt32(array, 0);
			}
			while (num2 >= num);
			return (int)(num2 % (uint)count);
		}
	}
	public static class TestStack
	{
		public static ModifierManager Build(ModifierRegistry registry, IEnumerable<string> ids, int hole, PersistenceMode persistence, EffectChannel capabilities, Func<ModifierDefinition, bool> context)
		{
			string[] array = ids.ToArray();
			if (array.Length > 64 || array.Distinct().Count() != array.Length)
			{
				throw new ArgumentException("Duplicate or oversized test stack");
			}
			ModifierManager modifierManager = new ModifierManager(registry, registry.Definitions.Select((ModifierDefinition d) => d.Id), capabilities)
			{
				ContextAllows = context
			};
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (!modifierManager.Activate(text, hole, persistence))
				{
					throw new ArgumentException("Unsupported or incompatible modifier: " + text);
				}
			}
			return modifierManager;
		}
	}
	public sealed class VoteCoordinator
	{
		private readonly RunRandom random;

		private readonly bool manageSkips;

		private int nextId;

		private int appliedId = -1;

		public VoteSettings Settings { get; }

		public uint Seed { get; }

		public int Skips { get; private set; }

		public VoteSession Vote { get; private set; }

		public double RevealUntil { get; private set; }

		public Func<int, int> RandomChoice { get; set; }

		public VoteCoordinator(VoteSettings settings, uint seed, bool manageSkips = true)
		{
			this.manageSkips = manageSkips;
			Settings = settings;
			Seed = seed;
			random = new RunRandom(seed);
			Skips = settings.SkipBudget;
		}

		public void Open(IEnumerable<int> players, double now, VoteOption[] options = null)
		{
			if (Vote != null && Vote.Winner == "")
			{
				throw new InvalidOperationException("A vote is already open");
			}
			Vote = new VoteSession(++nextId, options ?? DummyCandidates.Generate(Settings.CandidateCount, Skips > 0, random), players, now, Settings, random);
			if (RandomChoice != null)
			{
				Vote.RandomChoice = RandomChoice;
			}
			RevealUntil = 0.0;
		}

		public bool Tick(double now)
		{
			if (Vote == null)
			{
				return false;
			}
			bool result = Vote.Tick(now);
			if (Vote.Winner != "" && appliedId != Vote.Id)
			{
				appliedId = Vote.Id;
				if (manageSkips && Vote.Winner == "skip")
				{
					if (Skips <= 0)
					{
						throw new InvalidOperationException("Skip budget exhausted");
					}
					Skips--;
				}
				RevealUntil = now + Settings.RevealSeconds;
				result = true;
			}
			return result;
		}

		public void ClearVote()
		{
			Vote = null;
			RevealUntil = 0.0;
		}
	}
	public sealed class IntentGuard
	{
		private readonly Dictionary<int, long> sequence = new Dictionary<int, long>();

		private readonly Dictionary<int, double> lastTime = new Dictionary<int, double>();

		public bool Accept(int player, long next, double now)
		{
			if (next <= 0 || !VoteSettings.Finite(now) || (sequence.TryGetValue(player, out var value) && next <= value) || (lastTime.TryGetValue(player, out var value2) && now - value2 < 0.1))
			{
				return false;
			}
			sequence[player] = next;
			lastTime[player] = now;
			return true;
		}

		public void Remove(int player)
		{
			sequence.Remove(player);
			lastTime.Remove(player);
		}

		public void Clear()
		{
			sequence.Clear();
			lastTime.Clear();
		}
	}
	public enum TieRule
	{
		Random,
		HostBreaksTie,
		RandomExceptSkip,
		FirstToReachCount
	}
	public sealed class VoteSettings
	{
		public int CandidateCount { get; }

		public double Duration { get; }

		public bool AllowChanges { get; }

		public bool EarlyClose { get; }

		public TieRule Tie { get; }

		public int SkipBudget { get; }

		public double RevealSeconds { get; }

		public VoteSettings(int candidates = 3, double duration = 15.0, bool allowChanges = true, bool earlyClose = true, TieRule tie = TieRule.Random, int skips = 2, double reveal = 3.0)
		{
			if (candidates < 2 || candidates > 5 || !Finite(duration) || duration < 1.0 || duration > 120.0 || !Enum.IsDefined(typeof(TieRule), tie) || skips < 0 || skips > 99 || !Finite(reveal) || reveal < 1.0 || reveal > 15.0)
			{
				throw new ArgumentOutOfRangeException("candidates", "Invalid voting settings");
			}
			CandidateCount = candidates;
			Duration = duration;
			AllowChanges = allowChanges;
			EarlyClose = earlyClose;
			Tie = tie;
			SkipBudget = skips;
			RevealSeconds = reveal;
		}

		public static bool Finite(double value)
		{
			if (!double.IsNaN(value))
			{
				return !double.IsInfinity(value);
			}
			return false;
		}
	}
	public sealed class RunRandom
	{
		public uint State { get; private set; }

		public RunRandom(uint seed)
		{
			State = ((seed == 0) ? 2654435769u : seed);
		}

		public int Next(int count)
		{
			if (count <= 0)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			uint state = State;
			state ^= state << 13;
			state ^= state >> 17;
			state = (State = state ^ (state << 5));
			return (int)((long)state * (long)(uint)count >>> 32);
		}
	}
	public sealed class VoteOption
	{
		public string Id { get; }

		public string Name { get; }

		public string Description { get; }

		public bool IsSkip => Id == "skip";

		public VoteOption(string id, string name, string description)
		{
			Id = id;
			Name = name;
			Description = description;
		}
	}
	public static class DummyCandidates
	{
		public static VoteOption[] Generate(int count, bool skip, RunRandom random)
		{
			List<string> list = new List<string> { "Ruby", "Emerald", "Sapphire", "Amber", "Amethyst" };
			List<VoteOption> list2 = new List<VoteOption>();
			for (int i = 0; i < count; i++)
			{
				int index = random.Next(list.Count);
				string text = list[index];
				list.RemoveAt(index);
				list2.Add(new VoteOption(text.ToLowerInvariant(), text, "Prototype choice — no gameplay effect."));
			}
			if (skip)
			{
				list2.Add(new VoteOption("skip", "Skip", "Add no new choice. Uses one shared skip."));
			}
			return list2.ToArray();
		}
	}
	public sealed class VoteSession
	{
		private readonly VoteSettings settings;

		private readonly RunRandom random;

		private readonly HashSet<int> eligible;

		private readonly Dictionary<int, string> ballots = new Dictionary<int, string>();

		private readonly Dictionary<string, long> firstCount = new Dictionary<string, long>();

		private long order;

		public Func<int, int> RandomChoice { get; set; }

		public int Id { get; }

		public VoteOption[] Options { get; }

		public double Deadline { get; }

		public double TieDeadline { get; private set; }

		public string Winner { get; private set; } = "";

		public string[] Tied { get; private set; } = Array.Empty<string>();

		public bool Closed { get; private set; }

		public bool AwaitingHost
		{
			get
			{
				if (Closed && Winner == "")
				{
					return Tied.Length != 0;
				}
				return false;
			}
		}

		public IReadOnlyDictionary<int, string> Ballots => ballots;

		public int[] Eligible => eligible.OrderBy((int x) => x).ToArray();

		public Func<int, bool> MaySkip { get; set; } = (int player) => true;

		public VoteSession(int id, VoteOption[] options, IEnumerable<int> voters, double now, VoteSettings rules, RunRandom rng)
		{
			if (!VoteSettings.Finite(now) || options.Length < 1 || options.Length > 6 || options.Select((VoteOption o) => o.Id).Distinct().Count() != options.Length)
			{
				throw new ArgumentException("Invalid vote");
			}
			Id = id;
			Options = options.ToArray();
			eligible = new HashSet<int>(voters);
			settings = rules;
			random = rng;
			RandomChoice = random.Next;
			Deadline = now + rules.Duration;
		}

		public bool ResolveAutomatic(string option)
		{
			if (Closed || !Options.Any((VoteOption o) => o.Id == option))
			{
				return false;
			}
			Closed = true;
			Winner = option;
			return true;
		}

		public bool Cast(int player, string option, double now)
		{
			if ((option == "skip" && !MaySkip(player)) || !VoteSettings.Finite(now) || Closed || now >= Deadline || !eligible.Contains(player) || !Options.Any((VoteOption o) => o.Id == option))
			{
				return false;
			}
			if (ballots.TryGetValue(player, out var value) && (!settings.AllowChanges || value == option))
			{
				return false;
			}
			ballots[player] = option;
			order++;
			int num = ballots.Values.Count((string x) => x == option);
			string key = option + ":" + num;
			if (!firstCount.ContainsKey(key))
			{
				firstCount.Add(key, order);
			}
			return true;
		}

		public bool Disconnect(int player)
		{
			bool num = eligible.Remove(player);
			ballots.Remove(player);
			if (num && AwaitingHost)
			{
				Closed = false;
				Tied = Array.Empty<string>();
				TieDeadline = 0.0;
			}
			return num;
		}

		public bool Tick(double now)
		{
			if (!VoteSettings.Finite(now))
			{
				return false;
			}
			if (AwaitingHost && now >= TieDeadline)
			{
				Winner = Tied[RandomChoice(Tied.Length)];
				return true;
			}
			if (Closed || (now < Deadline && (!settings.EarlyClose || ballots.Count != eligible.Count)))
			{
				return false;
			}
			Closed = true;
			int max = Options.Max((VoteOption o) => ballots.Values.Count((string v) => v == o.Id));
			List<string> list = (from o in Options
				where ballots.Values.Count((string v) => v == o.Id) == max && (max > 0 || !o.IsSkip)
				select o.Id).ToList();
			if (settings.Tie == TieRule.RandomExceptSkip && list.Count > 1)
			{
				list.Remove("skip");
			}
			if (list.Count == 1)
			{
				Winner = list[0];
			}
			else if (settings.Tie == TieRule.HostBreaksTie && max > 0)
			{
				Tied = list.ToArray();
				TieDeadline = now + 5.0;
			}
			else if (settings.Tie == TieRule.FirstToReachCount && max > 0)
			{
				Winner = list.OrderBy((string id) => firstCount[id + ":" + max]).First();
			}
			else
			{
				Winner = list[RandomChoice(list.Count)];
			}
			return true;
		}

		public bool HostChoose(string option, double now)
		{
			if (!AwaitingHost || now >= TieDeadline || !Tied.Contains(option))
			{
				return false;
			}
			Winner = option;
			return true;
		}
	}
	public interface IWorldHazard
	{
		bool Alive { get; }

		void Observe();

		void Stop();
	}
	public interface IActiveWorldEvent
	{
		string Id { get; }

		void Start(WorldEventController controller, double now);
	}
	public sealed class WorldEventController
	{
		private sealed class Job
		{
			public string Id;

			public double Next;

			public double Min;

			public double Max;

			public Action Action;
		}

		private sealed class Owned
		{
			public string Source;

			public string Type;

			public IWorldHazard Hazard;
		}

		private readonly List<Job> jobs = new List<Job>();

		private readonly List<Owned> owned = new List<Owned>();

		private readonly Queue<double> requests = new Queue<double>();

		private readonly int perType;

		private readonly int perSource;

		private readonly int perSecond;

		private double now;

		private bool stopped;

		public RunRandom Random { get; }

		public int ActiveCount => owned.Count;

		public int ScheduledCount => jobs.Count;

		public int Spawned { get; private set; }

		public int Rejected { get; private set; }

		public int MaxActive { get; }

		public WorldEventController(uint seed, int maxActive = 7, int perType = 7, int perSource = 7, int perSecond = 4)
		{
			if (maxActive < 1 || maxActive > 32 || perType < 1 || perSource < 1 || perSecond < 1 || perSecond > 32)
			{
				throw new ArgumentException("Invalid world spawn budget");
			}
			Random = new RunRandom(seed);
			MaxActive = maxActive;
			this.perType = perType;
			this.perSource = perSource;
			this.perSecond = perSecond;
		}

		public double Between(double min, double max)
		{
			return min + (max - min) * (double)Random.Next(1000000) / 1000000.0;
		}

		public void ScheduleRepeating(string source, double min, double max, Action action, double time)
		{
			if (stopped || jobs.Count >= 8 || jobs.Any((Job j) => j.Id == source) || string.IsNullOrEmpty(source) || action == null || double.IsNaN(min) || double.IsNaN(max) || double.IsInfinity(min) || double.IsInfinity(max) || min < 0.1 || max < min || max > 120.0)
			{
				throw new ArgumentException("Invalid event schedule");
			}
			jobs.Add(new Job
			{
				Id = source,
				Min = min,
				Max = max,
				Next = time + Between(min, max),
				Action = action
			});
		}

		public void Tick(double time, bool playable)
		{
			if (stopped)
			{
				return;
			}
			if (!playable)
			{
				Stop();
				return;
			}
			now = time;
			foreach (Owned item in owned)
			{
				item.Hazard.Observe();
			}
			owned.RemoveAll((Owned o) => !o.Hazard.Alive);
			while (requests.Count > 0 && now - requests.Peek() >= 1.0)
			{
				requests.Dequeue();
			}
			Job[] array = jobs.ToArray();
			foreach (Job job in array)
			{
				if (stopped)
				{
					break;
				}
				if (!(now < job.Next))
				{
					job.Next = now + Between(job.Min, job.Max);
					try
					{
						job.Action();
					}
					catch
					{
						Stop();
						throw;
					}
				}
			}
		}

		public bool TrySpawn(string source, string type, Func<IWorldHazard> spawn, int externalActive = 0)
		{
			if (stopped || owned.Count + Math.Max(0, externalActive) >= MaxActive || owned.Count((Owned o) => o.Source == source) >= perSource || owned.Count((Owned o) => o.Type == type) >= perType || requests.Count >= perSecond)
			{
				Rejected++;
				return false;
			}
			requests.Enqueue(now);
			IWorldHazard worldHazard = spawn();
			if (worldHazard == null)
			{
				return false;
			}
			owned.Add(new Owned
			{
				Source = source,
				Type = type,
				Hazard = worldHazard
			});
			Spawned++;
			return true;
		}

		public void Stop()
		{
			if (stopped)
			{
				return;
			}
			stopped = true;
			jobs.Clear();
			requests.Clear();
			Exception ex = null;
			Owned[] array = this.owned.ToArray();
			foreach (Owned owned in array)
			{
				try
				{
					owned.Hazard.Stop();
				}
				catch (Exception ex2)
				{
					ex = ex2;
				}
			}
			this.owned.Clear();
			if (ex == null)
			{
				return;
			}
			throw new InvalidOperationException("World hazard cleanup failed", ex);
		}
	}
	public static class WorldSpawnPatterns
	{
		public static int ChooseCategory(RunRandom random, int[] weights, bool[] available, int previous)
		{
			if (weights.Length != available.Length || weights.Length > 32 || weights.Any((int w) => w < 0 || w > 1000))
			{
				throw new ArgumentException("Invalid targeting weights");
			}
			int[] array = (from i in Enumerable.Range(0, weights.Length)
				where available[i] && weights[i] > 0
				select i).ToArray();
			if (array.Length == 0)
			{
				return -1;
			}
			int[] array2 = array.Where((int i) => i != previous).ToArray();
			if (array2.Length == 0)
			{
				array2 = array;
			}
			int num = random.Next(array2.Sum((int i) => weights[i]));
			int[] array3 = array2;
			foreach (int num3 in array3)
			{
				num -= weights[num3];
				if (num < 0)
				{
					return num3;
				}
			}
			return array2[^1];
		}

		public static int Burst(WorldEventController controller, int min, int max, Func<bool> spawn)
		{
			if (min < 1 || max < min || max > 8)
			{
				throw new ArgumentException("Invalid burst");
			}
			int num = min + controller.Random.Next(max - min + 1);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (spawn())
				{
					num2++;
				}
			}
			return num2;
		}
	}
	public sealed class LawOfZeusEvent : IActiveWorldEvent
	{
		private readonly double minInterval;

		private readonly double maxInterval;

		private readonly int minBurst;

		private readonly int maxBurst;

		private readonly Func<bool> spawn;

		private readonly Action<int, int> report;

		public string Id => "law_of_zeus";

		public LawOfZeusEvent(Func<bool> spawn, Action<int, int> report, double minInterval = 0.8, double maxInterval = 1.5, int minBurst = 1, int maxBurst = 2)
		{
			this.spawn = spawn;
			this.report = report;
			this.minInterval = minInterval;
			this.maxInterval = maxInterval;
			this.minBurst = minBurst;
			this.maxBurst = maxBurst;
		}

		public void Start(WorldEventController controller, double now)
		{
			int burst = 0;
			controller.ScheduleRepeating(Id, minInterval, maxInterval, delegate
			{
				int arg = WorldSpawnPatterns.Burst(controller, minBurst, maxBurst, spawn);
				report?.Invoke(++burst, arg);
			}, now);
		}
	}
}

plugins/RogueGolf/RogueGolf.Plugin.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using RogueGolf.Core;
using Steamworks;
using Steamworks.Data;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[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 RogueGolf.Plugin
{
	[BepInPlugin("technomancer.roguegolf", "RogueGolf", "0.14.0")]
	public sealed class RogueGolfPlugin : BaseUnityPlugin
	{
		private sealed class SettingRow
		{
			internal string Name;

			internal string Value;

			internal Action Previous;

			internal Action Next;
		}

		private sealed class ContactState
		{
			internal readonly HashSet<int> Balls;

			internal readonly float Bounce;

			internal readonly float Friction;

			internal volatile string Sample;

			internal ContactState(IEnumerable<int> balls, float bounce, float friction)
			{
				Balls = new HashSet<int>(balls);
				Bounce = bounce;
				Friction = friction;
			}
		}

		private sealed class BallShapeLease
		{
			private readonly GolfBall ball;

			private readonly float radius;

			private readonly Dictionary<Transform, Vector3> visuals = new Dictionary<Transform, Vector3>();

			internal float NativeRadius => radius;

			internal BallShapeLease(GolfBall value)
			{
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				ball = value;
				radius = ball.Collider.radius;
				MeshRenderer[] componentsInChildren = ((Component)ball).GetComponentsInChildren<MeshRenderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Transform val = ((Component)componentsInChildren[i]).transform;
					while ((Object)(object)val.parent != (Object)(object)((Component)ball).transform && (Object)(object)val.parent != (Object)null)
					{
						val = val.parent;
					}
					if ((Object)(object)val != (Object)(object)((Component)ball).transform && !visuals.ContainsKey(val))
					{
						visuals[val] = val.localScale;
					}
				}
			}

			internal void Apply(float factor)
			{
				//IL_0055: 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)
				if ((Object)(object)ball == (Object)null)
				{
					return;
				}
				ball.Collider.radius = radius * factor;
				foreach (KeyValuePair<Transform, Vector3> visual in visuals)
				{
					if ((Object)(object)visual.Key != (Object)null)
					{
						visual.Key.localScale = visual.Value * factor;
					}
				}
			}
		}

		private struct HitContext
		{
			internal PlayerInfo Attacker;

			internal bool GolfSwing;
		}

		private sealed class UiAction
		{
			internal RectTransform Rect;

			internal Action Run;

			internal ScorecardShape Shape;

			internal Color Normal;
		}

		private WorldEventController worldEvents;

		private WorldSpawnLocations eventLocations;

		private uint eventSeed;

		private string eventRun = "";

		private int eventHole = -1;

		private bool eventFaulted;

		private ConfigEntry<int> stormCap;

		private ConfigEntry<int> burstMin;

		private ConfigEntry<int> burstMax;

		private ConfigEntry<float> burstIntervalMin;

		private ConfigEntry<float> burstIntervalMax;

		private ConfigEntry<bool> eventVerbose;

		private ConfigEntry<int>[] eventWeights;

		private TextMeshProUGUI cycleLabel;

		private CycleRing cycleRing;

		private readonly Dictionary<string, string> conditionValues = new Dictionary<string, string>();

		private readonly Dictionary<string, float> conditionChanged = new Dictionary<string, float>();

		private readonly List<KeyValuePair<string, TextMeshProUGUI>> conditionLabels = new List<KeyValuePair<string, TextMeshProUGUI>>();

		private string conditionRun = "";

		private GravityLease gravity;

		private GravityVector gravityBaseline;

		private ModifierSettings modifierSettings = new ModifierSettings(1.2f, 2f, 0.5f, 0.6f, 1.5f, (IEnumerable<KeyValuePair<string, float>>)null);

		private ModifierManager modifiers;

		private RunRandom modifierRandom;

		private EffectValues effects = EffectValues.Normal;

		private readonly Dictionary<Rigidbody, ScalarLease> massLeases = new Dictionary<Rigidbody, ScalarLease>();

		private readonly Dictionary<string, ConfigEntry<bool>> modifierEnabled = new Dictionary<string, ConfigEntry<bool>>();

		private readonly Dictionary<string, ConfigEntry<float>> tuning = new Dictionary<string, ConfigEntry<float>>();

		private ConfigEntry<float> powerFactor;

		private ConfigEntry<float> heavyFactor;

		private ConfigEntry<float> featherFactor;

		private ConfigEntry<float> lowFactor;

		private ConfigEntry<float> highFactor;

		private ConfigEntry<bool> traceGravity;

		private MethodInfo swingSpeedMethod;

		private string ballSample = "";

		private string playerSample = "";

		private int peerSceneRefreshFrame = -1;

		private ConfigEntry<Key> testingKey;

		private bool testingOpen;

		private bool testingUiReady;

		private int testingPage;

		private int consoleSuppressFrame = -1;

		private string testingDetail = "";

		private string testingMessage = "";

		private string testingRangeId = "";

		private readonly HashSet<string> testingDraft = new HashSet<string>();

		private readonly IntermissionGate gate = new IntermissionGate();

		private ConfigEntry<PrototypeStage> stage;

		private ConfigEntry<float> holdSeconds;

		private PrototypeStage runStage;

		private MethodInfo nextMethod;

		private bool releasing;

		private double releaseAt;

		private ConfigEntry<bool> labCommands;

		private double nextLabRead;

		private GolfBall labBlastNeighbor;

		private bool dampingTraced;

		private Collider[] scoringTriggers = Array.Empty<Collider>();

		private bool cupTraced;

		private readonly Dictionary<Collider, float> cupSurfaceHeights = new Dictionary<Collider, float>();

		private readonly Dictionary<GolfBall, BallShapeLease> shapes = new Dictionary<GolfBall, BallShapeLease>();

		private static volatile ContactState contacts = new ContactState(Array.Empty<int>(), 1f, 1f);

		[ThreadStatic]
		private static HitContext hitContext;

		private ConfigEntry<SelectionMode> selectionMode;

		private ConfigEntry<PersistenceMode> persistenceMode;

		private ConfigEntry<SkipMode> skipMode;

		private ConfigEntry<SkipBehavior> skipBehavior;

		private ConfigEntry<bool> bossEnabled;

		private ConfigEntry<bool> randomSkips;

		private ConfigEntry<int> bossInterval;

		private ConfigEntry<int> randomSkipWeight;

		private ConfigEntry<int> randomModifierWeight;

		private readonly List<ConfigEntry<int>> tierWeights = new List<ConfigEntry<int>>();

		private readonly List<ConfigEntry<int>> bandStarts = new List<ConfigEntry<int>>();

		private readonly Dictionary<string, ConfigEntry<int>> modifierWeights = new Dictionary<string, ConfigEntry<int>>();

		private ProgressionSettings progression = new ProgressionSettings((SelectionMode)0, (PersistenceMode)0, (SkipMode)0, (SkipBehavior)0, true, 4, false, 10, 100, (int[])null, (int[])null, (IEnumerable<KeyValuePair<string, int>>)null);

		private SkipLedger skipLedger;

		private readonly Dictionary<int, ulong> votePlayers = new Dictionary<int, ulong>();

		private readonly List<RunChoice> runHistory = new List<RunChoice>();

		private RunRandom selectionRandom;

		private int selectionNumber;

		private bool bossSelection;

		private bool summaryComplete;

		private uint progressionSeed;

		private string dismissedSummary = "";

		private int summaryPage;

		public const string Id = "technomancer.roguegolf";

		public const string Version = "0.14.0";

		internal static RogueGolfPlugin Instance;

		internal readonly RunController Run = new RunController();

		private Harmony harmony;

		private ConfigEntry<bool> enabledMode;

		private bool subscribed;

		private bool quitting;

		private string detailId = "";

		private readonly ThunderstormAdapter thunderAdapter = new ThunderstormAdapter();

		private ThunderstormAdapter.Lease thunderProof;

		private string thunderProofRun = "";

		private int thunderProofHole = -1;

		private int thunderProofStrikes;

		private double thunderProofNext;

		private NetworkBridge bridge;

		private VoteCoordinator voting;

		private ConfigEntry<int> candidateCount;

		private ConfigEntry<int> skipBudget;

		private ConfigEntry<int> seed;

		private ConfigEntry<float> voteSeconds;

		private ConfigEntry<float> revealSeconds;

		private ConfigEntry<bool> voteChanges;

		private ConfigEntry<bool> earlyClose;

		private ConfigEntry<TieRule> tieRule;

		private long snapshotRevision;

		private string notice = "";

		private double noticeUntil;

		private int lastWinnerVote;

		private bool networkFault;

		private static readonly Color Paper = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)251, (byte)224, byte.MaxValue));

		private static readonly Color Ink = Color32.op_Implicit(new Color32((byte)48, (byte)58, (byte)37, byte.MaxValue));

		private static readonly Color Green = Color32.op_Implicit(new Color32((byte)112, (byte)181, (byte)65, byte.MaxValue));

		private static readonly Color Gold = Color32.op_Implicit(new Color32((byte)249, (byte)198, (byte)67, byte.MaxValue));

		private static readonly Color Muted = Color32.op_Implicit(new Color32((byte)107, (byte)116, (byte)88, byte.MaxValue));

		private GameObject uiObject;

		private RectTransform uiRoot;

		private RectTransform uiContent;

		private TMP_FontAsset uiFont;

		private TMP_FontAsset uiTitleFont;

		private string uiKey = "";

		private int selected;

		private int effectPage;

		private int settingsPage;

		private int pauseTab;

		private int weightTab;

		private bool cursorOwned;

		private bool previousVisible;

		private bool uiFocus;

		private bool pauseMinimized;

		private bool setupOpen;

		private CursorLockMode previousCursor;

		private ModifierSettings hudSettings;

		private ModifierRegistry hudRegistry;

		private readonly List<UiAction> uiActions = new List<UiAction>();

		private ActiveModifier[] remoteActive = Array.Empty<ActiveModifier>();

		private Dictionary<ulong, float> playerShotFactors = new Dictionary<ulong, float>();

		private RunRandom worldRandom;

		private double nextWorldTick;

		private double nextPlayerScan;

		private double nextExplosion;

		private int worldHole = -1;

		private int explosionsThisHole;

		private readonly HashSet<uint> grantedPlayers = new HashSet<uint>();

		private readonly Dictionary<PlayerGolfer, Vector3> deathPositions = new Dictionary<PlayerGolfer, Vector3>();

		private readonly HashSet<string> verifiedTrafficScenes = new HashSet<string>();

		private ItemType sharedLoadout;

		private MethodInfo removeItem;

		private MethodInfo explodeRocket;

		private bool creatingExplosion;

		private bool pendingAmmoTrim;

		private Hittable explosionSourceBall;

		private Mechanic mechanics;

		private readonly HashSet<string> currentIds = new HashSet<string>();

		internal EffectChannel Capabilities { get; private set; }

		private string GravitySampleKey
		{
			get
			{
				//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)
				string text = CourseManager.CurrentHoleCourseIndex.ToString();
				Vector3 val = Physics.gravity;
				return text + ":" + ((Vector3)(ref val)).ToString("F4");
			}
		}

		private bool InDrivingRange => SingletonBehaviour<DrivingRangeManager>.HasInstance;

		private bool RangeTesting => testingRangeId != "";

		private bool EffectsPlaying
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Invalid comparison between Unknown and I4
				if ((int)Run.State != 3)
				{
					return RangeTesting;
				}
				return true;
			}
		}

		private string EffectRunId
		{
			get
			{
				if (!RangeTesting)
				{
					return Run.RunId;
				}
				return testingRangeId;
			}
		}

		private int TestHole
		{
			get
			{
				if (!RangeTesting)
				{
					return Run.HoleIndex;
				}
				return 0;
			}
		}

		private bool ViewingRangeTest
		{
			get
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				if (View != null && (int)View.State == 3)
				{
					return View.RunId.StartsWith("range-test-", StringComparison.Ordinal);
				}
				return false;
			}
		}

		private bool MatchSetupVisible
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Invalid comparison between Unknown and I4
				if ((InputManager.CurrentModeMask & 0x100) != 0)
				{
					if ((int)InputManager.CurrentMode != 256)
					{
						if (setupOpen)
						{
							return uiFocus;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		private RunSnapshot View
		{
			get
			{
				NetworkBridge networkBridge = bridge;
				if (networkBridge == null)
				{
					return null;
				}
				return networkBridge.Replica.Current;
			}
		}

		private bool OverlayVisible
		{
			get
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Invalid comparison between Unknown and I4
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Invalid comparison between Unknown and I4
				if (View != null)
				{
					if ((int)View.State != 6 && (int)View.State != 7 && (int)View.State != 8)
					{
						return (int)View.State == 5;
					}
					return true;
				}
				return false;
			}
		}

		private bool Paused
		{
			get
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Invalid comparison between Unknown and I4
				if (!MatchSetupVisible && (InputManager.CurrentModeMask & 0x20) != 0)
				{
					if ((int)InputManager.CurrentMode != 32)
					{
						return uiFocus;
					}
					return true;
				}
				return false;
			}
		}

		private bool NativeMenuFocused
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Invalid comparison between Unknown and I4
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Invalid comparison between Unknown and I4
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Invalid comparison between Unknown and I4
				if ((int)InputManager.CurrentMode != 32 && (int)InputManager.CurrentMode != 256 && (int)InputManager.CurrentMode != 1024)
				{
					return (int)InputManager.CurrentMode == 512;
				}
				return true;
			}
		}

		private bool InputAvailable
		{
			get
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Invalid comparison between Unknown and I4
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Invalid comparison between Unknown and I4
				if (Application.isFocused && !NativeMenuFocused && (int)InputManager.CurrentMode != 64 && (int)InputManager.CurrentMode != 2048)
				{
					return (int)InputManager.CurrentMode != 128;
				}
				return false;
			}
		}

		private bool SummaryVisible
		{
			get
			{
				if (View != null && View.SummaryComplete && dismissedSummary != View.RunId)
				{
					return !NativeMenuFocused;
				}
				return false;
			}
		}

		private ModifierRegistry UiRegistry
		{
			get
			{
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				if (hudSettings != View?.ModifierSettings || hudRegistry == null)
				{
					hudSettings = (ModifierSettings)(((object)View?.ModifierSettings) ?? ((object)new ModifierSettings(1.2f, 2f, 0.5f, 0.6f, 1.5f, (IEnumerable<KeyValuePair<string, float>>)null)));
					hudRegistry = ModifierRegistry.Create(hudSettings);
				}
				return hudRegistry;
			}
		}

		private IEnumerable<ActiveModifier> CurrentEffects
		{
			get
			{
				ModifierManager obj = modifiers;
				return ((obj != null) ? obj.Active : null) ?? remoteActive;
			}
		}

		private void ConfigureActiveEvents()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			stormCap = ((BaseUnityPlugin)this).Config.Bind<int>("LawOfZeus", "MaxActiveStorms", 7, new ConfigDescription("Maximum RogueGolf storms, including fading clouds. Existing vanilla item storms consume free capacity. Captured when the event starts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 7), Array.Empty<object>()));
			burstMin = ((BaseUnityPlugin)this).Config.Bind<int>("LawOfZeus", "MinBurstSize", 1, new ConfigDescription("Whole storms per burst, not individual bolts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
			burstMax = ((BaseUnityPlugin)this).Config.Bind<int>("LawOfZeus", "MaxBurstSize", 2, new ConfigDescription("Maximum whole storms per burst.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
			burstIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("LawOfZeus", "MinBurstInterval", 0.8f, new ConfigDescription("Scaled seconds between bursts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.4f, 10f), Array.Empty<object>()));
			burstIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("LawOfZeus", "MaxBurstInterval", 1.5f, new ConfigDescription("Maximum scaled seconds between bursts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.4f, 10f), Array.Empty<object>()));
			eventVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("LawOfZeus", "VerboseSpawns", false, "Log each chosen storm center; normal logs summarize bursts only.");
			string[] source = new string[5] { "GreenWeight", "PlayerWeight", "BallWeight", "ItemPointWeight", "FairwayWeight" };
			int[] defaults = new int[5] { 25, 25, 15, 20, 15 };
			eventWeights = source.Select((string n, int i) => ((BaseUnityPlugin)this).Config.Bind<int>("LawOfZeus", n, defaults[i], new ConfigDescription("Relative area targeting weight. Zero excludes this category. Unavailable landmarks are omitted.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()))).ToArray();
			Channel((EffectChannel)16384, delegate
			{
				if (AccessTools.Method(typeof(ThunderstormManager), "ServerCreateThunderstorm", new Type[3]
				{
					typeof(Vector3),
					typeof(PlayerInventory),
					typeof(ItemUseId)
				}, (Type[])null) == null)
				{
					throw new MissingMethodException("Vanilla thunderstorm factory");
				}
			});
		}

		private void TickActiveEvents()
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || !enabledMode.Value || !EffectsPlaying || modifiers == null || !modifiers.Active.Any((ActiveModifier e) => e.Id == "law_of_zeus" && !e.Suspended && e.FirstHole <= TestHole) || bridge == null || !bridge.CanStart(out var _))
			{
				StopActiveEvents();
			}
			else
			{
				if (eventFaulted)
				{
					return;
				}
				try
				{
					if (worldEvents != null && (eventRun != EffectRunId || eventHole != TestHole))
					{
						StopActiveEvents();
					}
					if (worldEvents == null)
					{
						StopThunderProof();
						eventRun = EffectRunId;
						eventHole = TestHole;
						eventLocations = new WorldSpawnLocations();
						int cap = stormCap.Value;
						int[] weights = eventWeights.Select((ConfigEntry<int> w) => w.Value).ToArray();
						if (weights.Sum() == 0)
						{
							throw new InvalidOperationException("At least one Zeus targeting weight must be positive.");
						}
						worldEvents = new WorldEventController(eventSeed ^ (uint)((TestHole + 1) * 7919), cap, cap, cap, 4);
						new LawOfZeusEvent((Func<bool>)(() => SpawnZeusStorm(weights)), (Action<int, int>)delegate(int burst, int count)
						{
							if (count > 0)
							{
								((BaseUnityPlugin)this).Logger.LogInfo((object)("[EVENT] LawOfZeus burst=" + burst + " count=" + count + " active=" + worldEvents.ActiveCount + "/" + cap));
							}
						}, (double)burstIntervalMin.Value, (double)Math.Max(burstIntervalMin.Value, burstIntervalMax.Value), burstMin.Value, Math.Max(burstMin.Value, burstMax.Value)).Start(worldEvents, Time.timeAsDouble);
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[EVENT] LawOfZeus started cap=" + cap + " " + eventLocations.Summary));
					}
					worldEvents.Tick(Time.timeAsDouble, true);
				}
				catch (Exception ex)
				{
					try
					{
						StopActiveEvents();
					}
					finally
					{
						eventFaulted = true;
					}
					((BaseUnityPlugin)this).Logger.LogError((object)("[EVENT] Disabled for this hole after failure: " + ex));
				}
			}
		}

		private bool SpawnZeusStorm(int[] weights)
		{
			Vector3[] activeCenters = ThunderstormAdapter.ActiveCenters;
			if (activeCenters.Length >= worldEvents.MaxActive)
			{
				return false;
			}
			if (!eventLocations.TryScatter(worldEvents.Random, weights, Players(), activeCenters, out var point, out var category))
			{
				return false;
			}
			bool num = worldEvents.TrySpawn("law_of_zeus", "thunderstorm", (Func<IWorldHazard>)(() => (IWorldHazard)(object)thunderAdapter.Spawn(point)), Math.Max(0, activeCenters.Length - worldEvents.ActiveCount));
			if (num && eventVerbose.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[EVENT] Thunderstorm target=" + category + " position=" + ((object)Unsafe.As<Vector3, Vector3>(ref point)/*cast due to .constrained prefix*/).ToString()));
			}
			return num;
		}

		private void StopActiveEvents()
		{
			WorldEventController val = worldEvents;
			worldEvents = null;
			eventLocations = null;
			eventRun = "";
			eventHole = -1;
			if (val != null)
			{
				val.Stop();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[EVENT] Stopped; schedules=0 owned=0 spawned=" + val.Spawned + " budgetRejected=" + val.Rejected));
			}
		}

		private void DrawCycles()
		{
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			RunSnapshot s = View;
			if (conditionRun != s.RunId)
			{
				conditionValues.Clear();
				conditionChanged.Clear();
				conditionRun = s.RunId;
			}
			float value = 1f;
			if ((Object)(object)GameManager.LocalPlayerId != (Object)null)
			{
				s.PlayerShotFactors.TryGetValue(GameManager.LocalPlayerId.Guid, out value);
			}
			if (value <= 0f)
			{
				value = 1f;
			}
			KeyValuePair<string, string>[] array = Conditions.Rows(UiRegistry, s.ModifierSettings, (IEnumerable<ActiveModifier>)s.ActiveModifiers, s.Hole, value);
			bool flag = s.ActiveModifiers.Any((ActiveModifier e) => !e.Suspended && e.FirstHole <= s.Hole && EffectPresentation.HasCycle(UiRegistry.Get(e.Id).Rule));
			if (array.Length == 0 && !flag)
			{
				return;
			}
			KeyValuePair<string, string>[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				KeyValuePair<string, string> keyValuePair = array2[num];
				if (!conditionValues.TryGetValue(keyValuePair.Key, out var value2) || value2 != keyValuePair.Value)
				{
					conditionChanged[keyValuePair.Key] = Time.unscaledTime;
				}
				conditionValues[keyValuePair.Key] = keyValuePair.Value;
			}
			string[] array3 = conditionValues.Keys.Except(array.Select((KeyValuePair<string, string> r) => r.Key)).ToArray();
			foreach (string key in array3)
			{
				conditionValues.Remove(key);
			}
			int num2 = Math.Min(10, array.Length);
			Transform transform = ((Component)Shape((Transform)(object)uiContent, 20f, 145f, 282f, 45 + num2 * 22 + ((array.Length > num2) ? 22 : 0), Color32.op_Implicit(new Color32((byte)24, (byte)39, (byte)30, (byte)185)), 10f)).transform;
			Label(transform, 12f, 9f, 217f, 24f, "CONDITIONS", 13f, Color32.op_Implicit(new Color32((byte)215, (byte)225, (byte)199, byte.MaxValue)), bold: true);
			if (flag)
			{
				RectTransform val = Rect(transform, 239f, 7f, 32f, 32f);
				cycleRing = ((Component)val).gameObject.AddComponent<CycleRing>();
				((Graphic)cycleRing).raycastTarget = false;
				cycleLabel = Label(transform, 240f, 12f, 30f, 21f, "", 12f, Color.white, bold: true);
				((TMP_Text)cycleLabel).alignment = (TextAlignmentOptions)514;
			}
			for (int num3 = 0; num3 < num2; num3++)
			{
				conditionLabels.Add(new KeyValuePair<string, TextMeshProUGUI>(array[num3].Key, Label(transform, 12f, 40 + num3 * 22, 258f, 22f, array[num3].Value, 16f, Color.white)));
			}
			if (array.Length > num2)
			{
				Label(transform, 12f, 40 + num2 * 22, 258f, 22f, "+" + (array.Length - num2) + " conditions · Esc for details", 13f, Color32.op_Implicit(new Color32((byte)215, (byte)225, (byte)199, byte.MaxValue)));
			}
		}

		private void UpdateCycleDisplay()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<string, TextMeshProUGUI> conditionLabel in conditionLabels)
			{
				if ((Object)(object)conditionLabel.Value != (Object)null)
				{
					float value;
					float num = (conditionChanged.TryGetValue(conditionLabel.Key, out value) ? (Time.unscaledTime - value) : 10f);
					((Graphic)conditionLabel.Value).color = Color.Lerp(Color32.op_Implicit(new Color32(byte.MaxValue, (byte)220, (byte)105, byte.MaxValue)), Color.white, Mathf.Clamp01(num / 1.1f));
				}
			}
			if (!((Object)(object)cycleLabel == (Object)null) && !((Object)(object)cycleRing == (Object)null) && View != null)
			{
				double num2 = Math.Max(0.0, View.NextCycleAt - NetworkTime.time);
				((TMP_Text)cycleLabel).text = ((View.NextCycleAt <= 0.0) ? "–" : Math.Ceiling(num2).ToString());
				cycleRing.Progress = Mathf.Clamp01((float)(num2 / (double)View.ModifierSettings.Interval));
			}
		}

		private void PeerSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			QueuePeerSceneRefresh();
		}

		private void QueuePeerSceneRefresh()
		{
			if (!NetworkServer.active && NetworkClient.active)
			{
				peerSceneRefreshFrame = Time.frameCount + 1;
			}
		}

		private void RefreshPeerScene()
		{
			if (peerSceneRefreshFrame < 0 || Time.frameCount < peerSceneRefreshFrame)
			{
				return;
			}
			peerSceneRefreshFrame = -1;
			if (!NetworkServer.active && NetworkClient.isConnected)
			{
				NetworkBridge networkBridge = bridge;
				if (((networkBridge != null) ? networkBridge.Replica.Current : null) != null)
				{
					ReceiveEffects(bridge.Replica.Current);
					NetLog("Rebound peer effects after scene load");
				}
			}
		}

		private static GravityVector ReadGravity()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			return new GravityVector(Physics.gravity.x, Physics.gravity.y, Physics.gravity.z);
		}

		private ConfigEntry<float> Factor(string name, float value, float min, float max)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<float>("Modifiers", name, value, new ConfigDescription("Multiplier captured from host config at run start.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		private unsafe void ConfigureModifiers()
		{
			//IL_0956: Unknown result type (might be due to invalid IL or missing references)
			//IL_095b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_08ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_08fb: Expected O, but got Unknown
			//IL_08fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0904: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Expected O, but got Unknown
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Expected O, but got Unknown
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Expected O, but got Unknown
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ca: Expected O, but got Unknown
			//IL_0516: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Expected O, but got Unknown
			//IL_055e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0568: Expected O, but got Unknown
			//IL_05a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b0: Expected O, but got Unknown
			//IL_05ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f8: Expected O, but got Unknown
			//IL_073f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0745: Unknown result type (might be due to invalid IL or missing references)
			//IL_076f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0784: Unknown result type (might be due to invalid IL or missing references)
			//IL_0791: Expected O, but got Unknown
			//IL_0791: Expected O, but got Unknown
			//IL_07aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b7: Expected O, but got Unknown
			//IL_0865: Unknown result type (might be due to invalid IL or missing references)
			//IL_0872: Expected O, but got Unknown
			//IL_0875: Unknown result type (might be due to invalid IL or missing references)
			//IL_087b: Unknown result type (might be due to invalid IL or missing references)
			gravity = new GravityLease((Func<GravityVector>)ReadGravity, (Action<GravityVector>)delegate(GravityVector v)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				Physics.gravity = new Vector3(v.X, v.Y, v.Z);
			});
			traceGravity = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "TraceGravity", false, "Read-only native physics and shot-speed samples for local verification.");
			powerFactor = Factor("PowerSurge", 1.2f, 1f, 3f);
			heavyFactor = Factor("HeavyBall", 2f, 1f, 4f);
			featherFactor = Factor("Featherweight", 0.5f, 0.25f, 1f);
			lowFactor = Factor("LowGravity", 0.6f, 0.1f, 1f);
			highFactor = Factor("HighGravity", 1.5f, 1f, 4f);
			ModifierDefinition[] definitions = ModifierRegistry.Create(new ModifierSettings(1.2f, 2f, 0.5f, 0.6f, 1.5f, (IEnumerable<KeyValuePair<string, float>>)null)).Definitions;
			foreach (ModifierDefinition val in definitions)
			{
				modifierEnabled[val.Id] = ((BaseUnityPlugin)this).Config.Bind<bool>("Modifiers.Enabled", val.Id, true, "Offer " + val.Name + " when supported and compatible.");
				if (!new string[5] { "power_surge", "heavy_ball", "featherweight", "low_gravity", "high_gravity" }.Contains(val.Id))
				{
					Contribution[] contributions = val.Contributions;
					foreach (Contribution val2 in contributions)
					{
						Dictionary<string, ConfigEntry<float>> dictionary = tuning;
						string id = val.Id;
						Parameter parameter = val2.Parameter;
						string key = id + "." + ((object)(*(Parameter*)(&parameter))/*cast due to .constrained prefix*/).ToString();
						ConfigFile config = ((BaseUnityPlugin)this).Config;
						string id2 = val.Id;
						parameter = val2.Parameter;
						dictionary[key] = config.Bind<float>("Tuning", id2 + "." + ((object)(*(Parameter*)(&parameter))/*cast due to .constrained prefix*/).ToString(), val2.Factor, new ConfigDescription("Per-run channel multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(CanonicalCatalog.Minimum(val2.Parameter), CanonicalCatalog.Maximum(val2.Parameter)), Array.Empty<object>()));
					}
				}
			}
			ConfigEntry<bool> val3 = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "InfiniteWarfareFiveUses", false, "Tracks the one-time 0.10.0 ammo balance migration.");
			if (!val3.Value)
			{
				if (tuning["infinite_warfare.Ammo"].Value == 10f)
				{
					tuning["infinite_warfare.Ammo"].Value = 5f;
				}
				val3.Value = true;
				((BaseUnityPlugin)this).Config.Save();
			}
			tuning["LeaderTaxFactor"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "LeaderTaxFactor", 0.8f, new ConfigDescription("Leader shot factor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			tuning["LastPlaceFactor"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "LastPlaceFactor", 1.2f, new ConfigDescription("Last place shot factor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.5f), Array.Empty<object>()));
			tuning["ComebackMaximum"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "ComebackMaximum", 1.3f, new ConfigDescription("Maximum rank assistance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.5f), Array.Empty<object>()));
			var array = new[]
			{
				new
				{
					Key = "TimedLow",
					Value = 0.35f,
					Min = 0.1f,
					Max = 1f
				},
				new
				{
					Key = "TimedHigh",
					Value = 2f,
					Min = 1f,
					Max = 3f
				},
				new
				{
					Key = "RouletteMoon",
					Value = 0.2f,
					Min = 0.1f,
					Max = 1f
				},
				new
				{
					Key = "RouletteLow",
					Value = 0.6f,
					Min = 0.1f,
					Max = 1f
				},
				new
				{
					Key = "RouletteHigh",
					Value = 1.5f,
					Min = 1f,
					Max = 4f
				}
			};
			foreach (var anon in array)
			{
				tuning[anon.Key] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", anon.Key, anon.Value, new ConfigDescription("Timed physics preset factor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(anon.Min, anon.Max), Array.Empty<object>()));
			}
			tuning["IntervalSeconds"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "IntervalSeconds", 20f, new ConfigDescription("Host interval for timed effects.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 120f), Array.Empty<object>()));
			tuning["ImpactThreshold"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "ImpactThreshold", 12f, new ConfigDescription("Minimum impact speed for explosions.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>()));
			tuning["ExplosionCooldown"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "ExplosionCooldown", 2f, new ConfigDescription("Global explosion cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), Array.Empty<object>()));
			tuning["ExplosionRadius"] = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "ExplosionRadius", 5f, new ConfigDescription("Native explosion radius, Nuclear Golf doubles it up to 12.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 12f), Array.Empty<object>()));
			ConfigEntry<bool> val4 = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "Balance0101", false, "Tracks the one-time 0.10.1 balance-default migration; custom values are preserved.");
			if (!val4.Value)
			{
				Dictionary<string, float> dictionary2 = tuning.ToDictionary((KeyValuePair<string, ConfigEntry<float>> p) => p.Key, (KeyValuePair<string, ConfigEntry<float>> p) => p.Value.Value);
				int num3 = BalanceMigration.Apply0101((IDictionary<string, float>)dictionary2, false);
				foreach (KeyValuePair<string, float> item in dictionary2)
				{
					tuning[item.Key].Value = item.Value;
				}
				val4.Value = true;
				((BaseUnityPlugin)this).Config.Save();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[CONFIG] 0.10.1 balance migration updated " + num3 + " legacy defaults"));
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(GolfBall), "ApplyGravity", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(PlayerMovement), "GetBaseGravitySpeedDelta", Type.EmptyTypes, (Type[])null);
			if (methodInfo != null && methodInfo2 != null)
			{
				Capabilities = (EffectChannel)(Capabilities | 2);
				if (traceGravity.Value)
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(RogueGolfPlugin), "BeforeBallGravity", (Type[])null), new HarmonyMethod(typeof(RogueGolfPlugin), "AfterBallGravity", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(RogueGolfPlugin), "AfterPlayerGravity", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[MODIFIER] Gravity channel unavailable: missing native consumer hooks");
			}
			try
			{
				swingSpeedMethod = AccessTools.DeclaredMethod(typeof(Hittable), "GetSwingHitSpeed", new Type[3]
				{
					typeof(bool),
					typeof(SwingType),
					typeof(float)
				}, (Type[])null);
				if (swingSpeedMethod == null || swingSpeedMethod.ReturnType != typeof(float))
				{
					throw new MissingMethodException("GetSwingHitSpeed");
				}
				harmony.Patch((MethodBase)swingSpeedMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(RogueGolfPlugin), "ScaleShotSpeed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Capabilities = (EffectChannel)(Capabilities | 1);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[MODIFIER] Shot power unavailable: " + ex.Message));
			}
			try
			{
				MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(GolfBall), "Awake", Type.EmptyTypes, (Type[])null);
				if (methodInfo3 == null)
				{
					throw new MissingMethodException("GolfBall.Awake");
				}
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(RogueGolfPlugin), "BallCreated", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Capabilities = (EffectChannel)(Capabilities | 4);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[MODIFIER] Mass unavailable: " + ex2.Message));
			}
			ConfigurePhysicsChannels();
			ConfigureWorldChannels();
			ConfigureActiveEvents();
			ConfigureLab();
			ConfigureTestingMenu();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[MODIFIER] Supported channels=" + ((object)Capabilities/*cast due to .constrained prefix*/).ToString()));
		}

		private void BeginModifiers(uint seedValue)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Expected O, but got Unknown
			RestoreEffects();
			gravityBaseline = ReadGravity();
			modifierSettings = new ModifierSettings(powerFactor.Value, heavyFactor.Value, featherFactor.Value, lowFactor.Value, highFactor.Value, tuning.Select((KeyValuePair<string, ConfigEntry<float>> p) => new KeyValuePair<string, float>(p.Key, p.Value.Value)));
			modifiers = ((runStage != PrototypeStage.Modifiers) ? ((ModifierManager)null) : new ModifierManager(ModifierRegistry.Create(modifierSettings), from p in modifierEnabled
				where p.Value.Value
				select p.Key, Capabilities));
			if (modifiers != null)
			{
				modifiers.ContextAllows = AllowsContext;
			}
			eventSeed = seedValue ^ 0x5E05;
			worldRandom = new RunRandom(seedValue ^ 0x91317);
			modifierRandom = new RunRandom(seedValue ^ 0xA511E9B3u);
			if (modifiers != null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[MODIFIER] Available=" + string.Join(",", modifiers.Available)));
			}
		}

		private void ActivateModifier(string id)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if (modifiers == null || !modifiers.Activate(id, Run.HoleIndex + 1, progression.Persistence))
			{
				throw new InvalidOperationException("Winner is no longer eligible: " + id);
			}
			if (CanonicalCatalog.IsTimed(modifiers.Registry.Get(id).Rule))
			{
				modifiers.SetVariant(id, worldRandom.Next(32));
			}
			ApplyEffects(Run.RunId, gravityBaseline, modifiers.Values);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[MODIFIER] Activated " + id + " active=" + string.Join(",", modifiers.Active.Select((ActiveModifier e) => e.Id))));
		}

		private void ExpireModifiers(int hole)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (modifiers == null)
			{
				return;
			}
			ActiveModifier[] active = modifiers.Active;
			if (modifiers.CompleteHole(hole))
			{
				ApplyEffects(Run.RunId, gravityBaseline, modifiers.Values);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[MODIFIER] Expired=" + string.Join(",", active.Select((ActiveModifier e) => e.Id).Except(modifiers.Active.Select((ActiveModifier e) => e.Id))) + " hole=" + hole));
			}
		}

		private void ApplyEffects(string runId, GravityVector baseline, EffectValues values)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			BindScoringTriggers();
			dampingTraced = false;
			float num = ((EffectValues)(ref effects))[(Parameter)9];
			effects = values;
			RefreshMechanics();
			if (((EffectValues)(ref values))[(Parameter)9] < num)
			{
				TrimAmmo();
			}
			if (((EffectValues)(ref values)).Gravity == 1f)
			{
				gravity.Restore();
			}
			else
			{
				gravity.Apply(runId, baseline, ((EffectValues)(ref values)).Gravity);
			}
			Rigidbody[] array = massLeases.Keys.Where((Rigidbody b) => (Object)(object)b == (Object)null).ToArray();
			foreach (Rigidbody key in array)
			{
				massLeases.Remove(key);
			}
			GolfBall[] array2 = Object.FindObjectsByType<GolfBall>((FindObjectsInactive)0, (FindObjectsSortMode)0);
			foreach (GolfBall ball in array2)
			{
				ApplyBallMass(ball);
			}
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			string[] obj = new string[8]
			{
				"[MODIFIER] Effective shot=",
				((EffectValues)(ref values)).ShotPower.ToString(),
				" gravity=",
				((EffectValues)(ref values)).Gravity.ToString(),
				" mass=",
				((EffectValues)(ref values)).BallMass.ToString(),
				" vector=",
				null
			};
			Vector3 val = Physics.gravity;
			obj[7] = ((Vector3)(ref val)).ToString("F4");
			logger.LogInfo((object)string.Concat(obj));
		}

		private void ApplyBallMass(GolfBall ball)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			Rigidbody[] array = massLeases.Keys.Where((Rigidbody b) => (Object)(object)b == (Object)null).ToArray();
			foreach (Rigidbody key in array)
			{
				massLeases.Remove(key);
			}
			if ((Object)(object)ball == (Object)null || (Object)(object)ball.Rigidbody == (Object)null)
			{
				return;
			}
			BindShape(ball);
			Rigidbody body = ball.Rigidbody;
			if (!massLeases.TryGetValue(body, out var value))
			{
				if (((EffectValues)(ref effects)).BallMass == 1f)
				{
					return;
				}
				value = new ScalarLease((Func<float>)(() => body.mass), (Action<float>)delegate(float v)
				{
					if ((Object)(object)body != (Object)null)
					{
						body.mass = v;
					}
				});
				massLeases.Add(body, value);
			}
			value.Apply(((EffectValues)(ref effects)).BallMass);
			if (traceGravity.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[PHYSICS] Mass hole=" + CourseManager.CurrentHoleCourseIndex + " factor=" + ((EffectValues)(ref effects)).BallMass + " actual=" + body.mass));
			}
		}

		private static void BallCreated(GolfBall __instance)
		{
			Instance?.ApplyBallMass(__instance);
		}

		private static void ScaleShotSpeed(Hittable __instance, ref float __result)
		{
			RogueGolfPlugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && ((Behaviour)instance).enabled && !((Object)(object)__instance.AsEntity == (Object)null) && __instance.AsEntity.IsGolfBall)
			{
				float num = __result;
				float num2 = __result;
				float shotPower = ((EffectValues)(ref instance.effects)).ShotPower;
				PlayerGolfer owner = __instance.AsEntity.AsGolfBall.Owner;
				__result = num2 * Mathf.Clamp(shotPower * instance.PlayerShotFactor((owner != null) ? owner.PlayerInfo : null), 0.25f, 3f);
				if (instance.traceGravity.Value)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)("[PHYSICS] Shot native=" + num.ToString("F6") + " factor=" + ((EffectValues)(ref instance.effects)).ShotPower + " actual=" + __result.ToString("F6")));
				}
			}
		}

		private void ProbePhysics()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (!traceGravity.Value)
			{
				return;
			}
			GolfBall[] array = Object.FindObjectsByType<GolfBall>((FindObjectsInactive)0, (FindObjectsSortMode)0);
			foreach (GolfBall val in array)
			{
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				string[] obj = new string[6]
				{
					"[PHYSICS] Probe hole=",
					CourseManager.CurrentHoleCourseIndex.ToString(),
					" mass=",
					val.Rigidbody.mass.ToString(),
					" gravity=",
					null
				};
				Vector3 val2 = Physics.gravity;
				obj[5] = ((Vector3)(ref val2)).ToString("F4");
				logger.LogInfo((object)string.Concat(obj));
				if ((Capabilities & 1) != 0)
				{
					swingSpeedMethod.Invoke(val.AsEntity.AsHittable, new object[3]
					{
						false,
						(object)(SwingType)0,
						0.5f
					});
				}
			}
		}

		internal void ResetPeerEffects()
		{
			RestoreEffects();
		}

		private void RestoreEffects()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			peerSceneRefreshFrame = -1;
			GravityLease obj = gravity;
			if (obj != null)
			{
				obj.Restore();
			}
			bool num = ((EffectValues)(ref effects))[(Parameter)9] > 1f;
			effects = EffectValues.Normal;
			if (num)
			{
				TrimAmmo();
			}
			foreach (KeyValuePair<Rigidbody, ScalarLease> massLease in massLeases)
			{
				if ((Object)(object)massLease.Key != (Object)null)
				{
					massLease.Value.Restore();
					ConfigEntry<bool> obj2 = traceGravity;
					if (obj2 != null && obj2.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[PHYSICS] Mass restored=" + massLease.Key.mass));
					}
				}
			}
			massLeases.Clear();
			ModifierManager obj3 = modifiers;
			if (obj3 != null)
			{
				obj3.Clear();
			}
			RestoreShapes();
			ResetWorldEffects();
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			Vector3 val = Physics.gravity;
			logger.LogInfo((object)("[MODIFIER] Restored channels; gravity=" + ((Vector3)(ref val)).ToString("F4")));
		}

		private void ReceiveEffects(RunSnapshot s)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active)
			{
				remoteActive = s.ActiveModifiers;
				modifierSettings = s.ModifierSettings;
				playerShotFactors = new Dictionary<ulong, float>(s.PlayerShotFactors);
				if (!((Behaviour)this).enabled || s.ActiveModifiers.Length == 0)
				{
					RestoreEffects();
				}
				else
				{
					ApplyEffects(s.RunId, s.GravityBaseline, ModifierRegistry.Create(s.ModifierSettings).Compose((IEnumerable<ActiveModifier>)s.ActiveModifiers));
				}
			}
		}

		private static void BeforeBallGravity(Rigidbody ___rigidbody, out Vector3 __state)
		{
			//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)
			__state = ___rigidbody.linearVelocity;
		}

		private static void AfterBallGravity(Rigidbody ___rigidbody, Vector3 __state)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			RogueGolfPlugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && !(instance.ballSample == instance.GravitySampleKey))
			{
				instance.ballSample = instance.GravitySampleKey;
				ManualLogSource logger = ((BaseUnityPlugin)instance).Logger;
				string[] obj = new string[8]
				{
					"[PHYSICS] Ball hole=",
					CourseManager.CurrentHoleCourseIndex.ToString(),
					" gravity=",
					null,
					null,
					null,
					null,
					null
				};
				Vector3 val = Physics.gravity;
				obj[3] = ((Vector3)(ref val)).ToString("F4");
				obj[4] = " delta=";
				val = ___rigidbody.linearVelocity - __state;
				obj[5] = ((Vector3)(ref val)).magnitude.ToString("F6");
				obj[6] = " expected=";
				val = Physics.gravity;
				obj[7] = (((Vector3)(ref val)).magnitude * Time.fixedDeltaTime).ToString("F6");
				logger.LogInfo((object)string.Concat(obj));
			}
		}

		private static void AfterPlayerGravity(float __result, float ___gravityFactor)
		{
			//IL_002f: 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)
			RogueGolfPlugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && !(instance.playerSample == instance.GravitySampleKey))
			{
				instance.playerSample = instance.GravitySampleKey;
				float num = Physics.gravity.y * GameManager.PlayerMovementSettings.BaseGravityFactor * ___gravityFactor * Time.fixedDeltaTime;
				ManualLogSource logger = ((BaseUnityPlugin)instance).Logger;
				string[] obj = new string[8]
				{
					"[PHYSICS] Player hole=",
					CourseManager.CurrentHoleCourseIndex.ToString(),
					" gravity=",
					null,
					null,
					null,
					null,
					null
				};
				Vector3 val = Physics.gravity;
				obj[3] = ((Vector3)(ref val)).ToString("F4");
				obj[4] = " delta=";
				obj[5] = __result.ToString("F6");
				obj[6] = " expected=";
				obj[7] = num.ToString("F6");
				logger.LogInfo((object)string.Concat(obj));
			}
		}

		private void OpenRunSetup()
		{
			if (NetworkServer.active && MatchSetupVisible)
			{
				SetUiFocus(value: true);
				if (uiFocus)
				{
					setupOpen = true;
					pauseTab = 1;
					settingsPage = 0;
					selected = 0;
					uiKey = "";
				}
			}
		}

		private void CloseRunSetup()
		{
			setupOpen = false;
			pauseTab = 0;
			SetUiFocus(value: false);
			selected = 0;
			uiKey = "";
		}

		private void DrawRunSetup()
		{
			//IL_002f: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			Shape((Transform)(object)uiContent, 0f, 0f, 1280f, 720f, new Color(0f, 0f, 0f, 0.65f), 0f, block: true);
			RectTransform val = Panel((Transform)(object)uiContent, 390f, 60f, 500f, 600f, Paper);
			Label((Transform)(object)val, 20f, 17f, 310f, 45f, "RogueGolf setup", 30f, null, bold: true);
			Button((Transform)(object)val, 360f, 24f, 120f, 34f, "Back", CloseRunSetup, Gold);
			Label((Transform)(object)val, 20f, 64f, 460f, 26f, "Private chaos runs · ties resolved randomly", 16f, Muted);
			Button((Transform)(object)val, 20f, 97f, 225f, 36f, "Run settings", delegate
			{
				pauseTab = 1;
				settingsPage = 0;
			}, (pauseTab == 1) ? Gold : Green);
			Button((Transform)(object)val, 255f, 97f, 225f, 36f, "Weights", delegate
			{
				pauseTab = 2;
				settingsPage = 0;
			}, (pauseTab == 2) ? Gold : Green);
			DrawHostSettings(val);
			Label((Transform)(object)val, 20f, 561f, 460f, 32f, "Arrows / D-pad: select · Enter / A: use · Esc / B: back", 14f, Muted);
		}

		private SettingRow EnumRow<T>(string name, ConfigEntry<T> entry) where T : struct
		{
			T[] choices = (T[])Enum.GetValues(typeof(T));
			return new SettingRow
			{
				Name = name,
				Value = SplitWords(entry.Value.ToString()),
				Previous = delegate
				{
					Step(-1);
				},
				Next = delegate
				{
					Step(1);
				}
			};
			void Step(int direction)
			{
				int num = Array.IndexOf(choices, entry.Value);
				entry.Value = choices[(num + direction + choices.Length) % choices.Length];
				SaveUiConfig();
			}
		}

		private SettingRow IntRow(string name, ConfigEntry<int> entry, int min, int max, int step = 1)
		{
			return new SettingRow
			{
				Name = name,
				Value = entry.Value.ToString(),
				Previous = delegate
				{
					Step(-1);
				},
				Next = delegate
				{
					Step(1);
				}
			};
			void Step(int d)
			{
				entry.Value = Math.Max(min, Math.Min(max, entry.Value + d * step));
				SaveUiConfig();
			}
		}

		private SettingRow BoolRow(string name, ConfigEntry<bool> entry)
		{
			return new SettingRow
			{
				Name = name,
				Value = (entry.Value ? "On" : "Off"),
				Previous = Toggle,
				Next = Toggle
			};
			void Toggle()
			{
				entry.Value = !entry.Value;
				SaveUiConfig();
			}
		}

		private SettingRow SecondsRow(string name, ConfigEntry<float> entry, int max)
		{
			return new SettingRow
			{
				Name = name,
				Value = EffectPresentation.Number(entry.Value) + "s",
				Previous = delegate
				{
					Step(-1);
				},
				Next = delegate
				{
					Step(1);
				}
			};
			void Step(int d)
			{
				entry.Value = Math.Max(1f, Math.Min(max, entry.Value + (float)d));
				SaveUiConfig();
			}
		}

		private static string SplitWords(string text)
		{
			return Regex.Replace(text, "([a-z])([A-Z])", "$1 $2");
		}

		private void SaveUiConfig()
		{
			((BaseUnityPlugin)this).Config.Save();
			uiKey = "";
		}

		private void DrawHostSettings(RectTransform panel)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_04de: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_050e: Unknown result type (might be due to invalid IL or missing references)
			//IL_058e: Unknown result type (might be due to invalid IL or missing references)
			//IL_07da: Unknown result type (might be due to invalid IL or missing references)
			//IL_07df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0855: Unknown result type (might be due to invalid IL or missing references)
			//IL_089a: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d5: Unknown result type (might be due to invalid IL or missing references)
			bool active = NetworkServer.active;
			Label((Transform)(object)panel, 20f, 146f, 460f, 44f, active ? "Next-course settings. Disabling also stops the current run." : "Current host settings. Only the host can edit the next course.", 16f, Muted);
			List<SettingRow> list = new List<SettingRow>();
			if (pauseTab == 1)
			{
				if (active)
				{
					list.Add(BoolRow("RogueGolf enabled", enabledMode));
					list.Add(EnumRow<SelectionMode>("Selection", selectionMode));
					list.Add(EnumRow<PersistenceMode>("Persistence", persistenceMode));
					list.Add(IntRow("Candidate cards", candidateCount, 2, 5));
					list.Add(EnumRow<SkipMode>("Skip budget", skipMode));
					list.Add(IntRow("Starting skips", skipBudget, 0, 99));
					list.Add(EnumRow<SkipBehavior>("Skip effect", skipBehavior));
					list.Add(BoolRow("Boss selections", bossEnabled));
					list.Add(IntRow("Boss interval (selections)", bossInterval, 1, 99));
					list.Add(BoolRow("Automatic skips", randomSkips));
					list.Add(IntRow("Automatic skip weight", randomSkipWeight, 0, 10000, 10));
					list.Add(IntRow("Automatic modifier weight", randomModifierWeight, 1, 10000, 10));
					list.Add(SecondsRow("Voting time", voteSeconds, 120));
					list.Add(SecondsRow("Reveal time", revealSeconds, 15));
					list.Add(BoolRow("Allow vote changes", voteChanges));
					list.Add(BoolRow("Close when all vote", earlyClose));
					list.Add(IntRow("Run seed (0 = random)", seed, 0, 999999999));
					for (int i = 0; i < bandStarts.Count; i++)
					{
						int index = i;
						ConfigEntry<int> entry = bandStarts[i];
						list.Add(new SettingRow
						{
							Name = (new string[3] { "Early-mid starts at hole", "Mid-late starts at hole", "Late starts at hole" })[i],
							Value = entry.Value.ToString(),
							Previous = delegate
							{
								Change(-1);
							},
							Next = delegate
							{
								Change(1);
							}
						});
						void Change(int d)
						{
							int val2 = ((index == 0) ? 3 : (bandStarts[index - 1].Value + 1));
							int val3 = ((index == 2) ? 999 : (bandStarts[index + 1].Value - 1));
							entry.Value = Math.Max(val2, Math.Min(val3, entry.Value + d));
							SaveUiConfig();
						}
					}
				}
				else if (View != null)
				{
					ProgressionSettings val = View.Progression;
					(string, string)[] array = new(string, string)[7]
					{
						("Selection", ((object)val.Selection/*cast due to .constrained prefix*/).ToString()),
						("Persistence", ((object)val.Persistence/*cast due to .constrained prefix*/).ToString()),
						("Skip budget", ((object)val.Skips/*cast due to .constrained prefix*/).ToString()),
						("Skip effect", ((object)val.SkipEffect/*cast due to .constrained prefix*/).ToString()),
						("Boss interval", val.BossEnabled ? val.BossInterval.ToString() : "Off"),
						("Seed", View.Seed.ToString()),
						("Vote time", View.Settings.Duration + "s")
					};
					for (int num = 0; num < array.Length; num++)
					{
						(string, string) tuple = array[num];
						list.Add(new SettingRow
						{
							Name = tuple.Item1,
							Value = SplitWords(tuple.Item2)
						});
					}
				}
			}
			else
			{
				Button((Transform)(object)panel, 20f, 190f, 148f, 32f, "Rarity weights", delegate
				{
					weightTab = 0;
					settingsPage = 0;
				}, (weightTab == 0) ? Gold : Green);
				Button((Transform)(object)panel, 176f, 190f, 165f, 32f, "Modifier weights", delegate
				{
					weightTab = 1;
					settingsPage = 0;
				}, (weightTab == 1) ? Gold : Green);
				Label((Transform)(object)panel, 355f, 196f, 125f, 25f, "0 excludes", 14f, Muted);
				if (weightTab == 0)
				{
					string[] array2 = new string[4] { "Early", "Early-mid", "Mid-late", "Late" };
					for (int num2 = 0; num2 < 14; num2++)
					{
						string name = ((num2 < 12) ? (array2[num2 / 3] + " / " + ((object)(ModifierRarity)(num2 % 3)/*cast due to .constrained prefix*/).ToString()) : ("Boss selection / " + ((num2 == 12) ? "Legendary" : "Boss")));
						object obj;
						if (!active)
						{
							obj = new SettingRow
							{
								Name = name
							};
							object obj2 = obj;
							RunSnapshot view = View;
							((SettingRow)obj2).Value = ((view != null) ? view.Progression.TierWeights[num2] : 0).ToString();
						}
						else
						{
							obj = IntRow(name, tierWeights[num2], 0, 10000, 5);
						}
						list.Add((SettingRow)obj);
					}
				}
				else
				{
					foreach (ModifierDefinition item in from d in UiRegistry.Definitions
						orderby d.Rarity, d.Name
						select d)
					{
						object obj3;
						if (!active)
						{
							obj3 = new SettingRow
							{
								Name = item.Name
							};
							object obj4 = obj3;
							RunSnapshot view2 = View;
							((SettingRow)obj4).Value = ((view2 != null) ? view2.Progression.Weight(item.Id) : 100).ToString();
						}
						else
						{
							obj3 = IntRow(item.Name + (modifierEnabled[item.Id].Value ? "" : " (disabled)"), modifierWeights[item.Id], 0, 10000, 25);
						}
						list.Add((SettingRow)obj3);
					}
				}
			}
			int num3 = ((pauseTab == 1) ? 6 : 5);
			int num4 = Math.Max(1, (list.Count + num3 - 1) / num3);
			settingsPage = Math.Max(0, Math.Min(settingsPage, num4 - 1));
			for (int num5 = 0; num5 < num3 && settingsPage * num3 + num5 < list.Count; num5++)
			{
				SettingRow row = list[settingsPage * num3 + num5];
				float num6 = ((pauseTab == 1) ? 197 : 236) + num5 * 49;
				Shape((Transform)(object)panel, 20f, num6, 460f, 44f, Color32.op_Implicit(new Color32((byte)244, (byte)239, (byte)210, byte.MaxValue)), 10f);
				Label((Transform)(object)panel, 30f, num6 + 5f, 220f, 36f, row.Name, 17f, null, bold: true);
				((TMP_Text)Label((Transform)(object)panel, 294f, num6 + 6f, 140f, 34f, row.Value, 16f, Muted)).alignment = (TextAlignmentOptions)514;
				Button((Transform)(object)panel, 256f, num6 + 6f, 34f, 32f, "−", delegate
				{
					if (NetworkServer.active)
					{
						row.Previous?.Invoke();
					}
				}, Gold, active);
				Button((Transform)(object)panel, 438f, num6 + 6f, 34f, 32f, "+", delegate
				{
					if (NetworkServer.active)
					{
						row.Next?.Invoke();
					}
				}, Gold, active);
			}
			Pager(panel, settingsPage, num4, delegate
			{
				settingsPage--;
			}, delegate
			{
				settingsPage++;
			});
		}

		private void ConfigureTestingMenu()
		{
			testingKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Diagnostics", "TestingMenuKey", (Key)99, "Host testing menu key, only while the native console is open with Console Commands enabled in a private driving range or playing hole.");
			try
			{
				NativeHook(typeof(DevConsoleGui), "OnGUI", "AllowConsoleGui");
				testingUiReady = true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[TEST] Testing menu unavailable: " + ex.Message));
			}
		}

		private static bool AllowConsoleGui()
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				if (!Instance.testingOpen)
				{
					return Time.frameCount != Instance.consoleSuppressFrame;
				}
				return false;
			}
			return true;
		}

		private bool CanUseTestingMenu(out string reason)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			reason = "";
			if (!testingUiReady || !((Behaviour)this).enabled || !enabledMode.Value || !NetworkServer.active)
			{
				reason = "Only the active RogueGolf host can use the testing menu.";
				return false;
			}
			if (!DevConsoleGui.Active || !MatchSetupRules.IsCheatsEnabled())
			{
				reason = "Enable Console Commands and open the console first.";
				return false;
			}
			if (bridge == null || !bridge.CanStart(out reason))
			{
				return false;
			}
			if (InDrivingRange && stage.Value == PrototypeStage.Modifiers && !Run.Active)
			{
				return true;
			}
			if (runStage != PrototypeStage.Modifiers || (int)Run.State != 3 || modifiers == null)
			{
				reason = "Start a RogueGolf course and enter a playing hole first.";
				return false;
			}
			return true;
		}

		private void BeginRangeTesting()
		{
			if (InDrivingRange && !RangeTesting)
			{
				uint seedValue = (uint)DateTime.UtcNow.Ticks;
				runStage = PrototypeStage.Modifiers;
				BeginProgression(seedValue);
				BeginModifiers(seedValue);
				testingRangeId = "range-test-" + Guid.NewGuid().ToString("N");
				StartHoleEffects(0);
				Publish();
			}
		}

		private void EndRangeTesting()
		{
			if (RangeTesting)
			{
				ClearRunResources();
				Publish();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"[TEST] Driving-range effects cleared");
			}
		}

		private void UpdateRangeTesting()
		{
			if (RangeTesting && (!NetworkServer.active || !InDrivingRange || Run.Active || !enabledMode.Value || !MatchSetupRules.IsCheatsEnabled() || !bridge.CanStart(out var _)))
			{
				EndRangeTesting();
			}
		}

		private void CloseTestingMenu()
		{
			testingOpen = false;
			consoleSuppressFrame = Time.frameCount;
			uiKey = "";
			selected = 0;
		}

		private void OpenTestingMenu()
		{
			if (!CanUseTestingMenu(out var reason))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[TEST] " + reason));
				return;
			}
			try
			{
				BeginRangeTesting();
			}
			catch (Exception ex)
			{
				EndRangeTesting();
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[TEST] " + ex.Message));
				return;
			}
			SetUiFocus(value: false);
			setupOpen = false;
			testingOpen = true;
			testingDraft.Clear();
			ActiveModifier[] active = modifiers.Active;
			foreach (ActiveModifier val in active)
			{
				testingDraft.Add(val.Id);
			}
			testingPage = 0;
			testingDetail = "";
			testingMessage = "Choose modifiers, then Apply stack. Changes affect everyone.";
			selected = 0;
			uiKey = "";
		}

		private bool UpdateTestingInput()
		{
			//IL_002c: 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_0054: 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)
			if (testingOpen && !CanUseTestingMenu(out var _))
			{
				CloseTestingMenu();
			}
			if (!Application.isFocused)
			{
				return testingOpen;
			}
			if ((InputManager.CurrentModeMask & 0xC80) != 0)
			{
				return testingOpen;
			}
			Keyboard current = Keyboard.current;
			if (testingKey != null && (int)testingKey.Value != 0 && current != null && ((ButtonControl)current[testingKey.Value]).wasPressedThisFrame && DevConsoleGui.Active)
			{
				if (testingOpen)
				{
					CloseTestingMenu();
				}
				else
				{
					OpenTestingMenu();
				}
				return true;
			}
			if (!testingOpen)
			{
				return false;
			}
			Gamepad current2 = Gamepad.current;
			if ((current != null && ((ButtonControl)current.escapeKey).wasPressedThisFrame) || (current2 != null && current2.buttonEast.wasPressedThisFrame))
			{
				CloseTestingMenu();
				return true;
			}
			Mouse mouse = Mouse.current;
			if (mouse != null && mouse.leftButton.wasPressedThisFrame)
			{
				UiAction uiAction = uiActions.LastOrDefault((UiAction a) => RectTransformUtility.RectangleContainsScreenPoint(a.Rect, ((InputControl<Vector2>)(object)((Pointer)mouse).position).ReadValue()));
				if (uiAction != null)
				{
					uiAction.Run();
					uiKey = "";
					return true;
				}
			}
			if ((current != null && ((ButtonControl)current.rightArrowKey).wasPressedThisFrame) || (current != null && ((ButtonControl)current.tabKey).wasPressedThisFrame) || (current2 != null && current2.dpad.down.wasPressedThisFrame))
			{
				selected++;
			}
			if ((current != null && ((ButtonControl)current.leftArrowKey).wasPressedThisFrame) || (current2 != null && current2.dpad.up.wasPressedThisFrame))
			{
				selected--;
			}
			if (uiActions.Count > 0)
			{
				selected = (selected % uiActions.Count + uiActions.Count) % uiActions.Count;
				if ((current != null && ((ButtonControl)current.enterKey).wasPressedThisFrame) || (current2 != null && current2.buttonSouth.wasPressedThisFrame))
				{
					uiActions[selected].Run();
					uiKey = "";
				}
			}
			return true;
		}

		private string TestUnavailable(ModifierDefinition d)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if ((EffectChannel)(d.Channel & Capabilities) != d.Channel)
			{
				return "Required game hooks unavailable";
			}
			if (!AllowsContext(d))
			{
				return "Requires a course with native traffic";
			}
			return "";
		}

		private void TestAction(Action action, string success)
		{
			if (!CanUseTestingMenu(out var reason))
			{
				testingMessage = reason;
				CloseTestingMenu();
				return;
			}
			try
			{
				action();
				testingMessage = success;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[TEST] " + success));
				Publish();
			}
			catch (Exception ex)
			{
				testingMessage = ex.Message;
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[TEST] " + ex.Message));
			}
			uiKey = "";
		}

		private void ApplyTestingStack(IEnumerable<string> ids)
		{
			string[] copy = ids.ToArray();
			TestAction(delegate
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				ModifierManager val = TestStack.Build(ModifierRegistry.Create(modifierSettings), (IEnumerable<string>)copy, TestHole, progression.Persistence, Capabilities, (Func<ModifierDefinition, bool>)AllowsContext);
				RestoreEffects();
				modifiers = val;
				ApplyEffects(EffectRunId, gravityBaseline, modifiers.Values);
				StartHoleEffects(TestHole);
				TickWorldEffects();
				testingDraft.Clear();
				string[] array = copy;
				foreach (string item in array)
				{
					testingDraft.Add(item);
				}
			}, "Applied test stack (" + copy.Length + "). Durations, cycles and starting items restarted.");
		}

		private void DrawTestingMenu()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0607: Unknown result type (might be due to invalid IL or missing references)
			//IL_063e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0675: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_071a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0751: Unknown result type (might be due to invalid IL or missing references)
			//IL_0782: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			Shape((Transform)(object)uiContent, 0f, 0f, 1280f, 720f, new Color(0f, 0f, 0f, 0.8f), 0f, block: true);
			RectTransform parent = Panel((Transform)(object)uiContent, 100f, 40f, 1080f, 640f, Paper);
			Label((Transform)(object)parent, 24f, 16f, 800f, 45f, "RogueGolf · Host testing", 32f, null, bold: true);
			Button((Transform)(object)parent, 888f, 23f, 168f, 34f, "Back to console", CloseTestingMenu, Gold);
			Label((Transform)(object)parent, 24f, 67f, 1020f, 40f, (RangeTesting ? "Driving range" : "Private course") + " · applies to all players · test selections bypass offer weights and enable switches", 16f, Muted);
			ModifierDefinition[] array = (from d in ModifierRegistry.Create(modifierSettings).Definitions
				orderby d.Rarity, d.Name
				select d).ToArray();
			int num = (array.Length + 7) / 8;
			testingPage = Math.Max(0, Math.Min(testingPage, num - 1));
			for (int num2 = 0; num2 < 8 && testingPage * 8 + num2 < array.Length; num2++)
			{
				ModifierDefinition val = array[testingPage * 8 + num2];
				float num3 = 115 + num2 * 49;
				string id = val.Id;
				bool chosen = testingDraft.Contains(id);
				string text = TestUnavailable(val);
				Shape((Transform)(object)parent, 20f, num3, 508f, 45f, Color32.op_Implicit(new Color32((byte)244, (byte)239, (byte)210, byte.MaxValue)), 8f);
				Icon((Transform)(object)parent, 28f, num3 + 6f, 32f, val);
				Label((Transform)(object)parent, 72f, num3 + 3f, 265f, 25f, val.Name, 18f, Ink, bold: true);
				Label((Transform)(object)parent, 72f, num3 + 26f, 265f, 18f, (text == "") ? ((object)val.Rarity/*cast due to .constrained prefix*/).ToString() : text, 12f, Muted);
				Button((Transform)(object)parent, 340f, num3 + 7f, 82f, 31f, "Info", delegate
				{
					testingDetail = id;
				}, Gold);
				Button((Transform)(object)parent, 430f, num3 + 7f, 88f, 31f, chosen ? "Remove" : "Add", delegate
				{
					if (chosen)
					{
						testingDraft.Remove(id);
					}
					else
					{
						testingDraft.Add(id);
					}
				}, chosen ? Gold : Green, chosen || text == "");
			}
			Button((Transform)(object)parent, 24f, 517f, 112f, 32f, "Previous", delegate
			{
				testingPage--;
				selected = 0;
			}, null, testingPage > 0);
			Label((Transform)(object)parent, 154f, 522f, 225f, 26f, "Page " + (testingPage + 1) + " / " + num + " · " + testingDraft.Count + " selected", 16f, Muted);
			Button((Transform)(object)parent, 406f, 517f, 112f, 32f, "Next", delegate
			{
				testingPage++;
				selected = 0;
			}, null, testingPage < num - 1);
			ModifierDefinition detail = ((IEnumerable<ModifierDefinition>)array).FirstOrDefault((Func<ModifierDefinition, bool>)((ModifierDefinition d) => d.Id == testingDetail));
			ModifierDefinition obj = detail;
			Label((Transform)(object)parent, 553f, 112f, 490f, 45f, ((obj != null) ? obj.Name : null) ?? "Choose an effect", 27f, null, bold: true);
			Label((Transform)(object)parent, 553f, 165f, 480f, 210f, (detail == null) ? "Add/remove effects to build a draft stack. Apply stack replaces the current effects.\n\nConflicting groups are rejected before any change. Unsupported effects stay listed with a reason." : EffectPresentation.Details(detail, modifierSettings, ((IEnumerable<ActiveModifier>)modifiers.Active).FirstOrDefault((Func<ActiveModifier, bool>)((ActiveModifier e) => e.Id == detail.Id))), 18f);
			if (detail != null)
			{
				Button((Transform)(object)parent, 553f, 389f, 240f, 35f, "Apply this effect only", delegate
				{
					ApplyTestingStack(new string[1] { detail.Id });
				}, Gold, TestUnavailable(detail) == "");
			}
			Button((Transform)(object)parent, 553f, 439f, 232f, 38f, "Apply stack", delegate
			{
				ApplyTestingStack(testingDraft);
			}, Green);
			Button((Transform)(object)parent, 803f, 439f, 232f, 38f, "Clear all effects", delegate
			{
				ApplyTestingStack(Array.Empty<string>());
			}, Gold);
			Button((Transform)(object)parent, 553f, 491f, 232f, 35f, "Trigger cycle", delegate
			{
				TestAction(delegate
				{
					nextWorldTick = 0.0;
					TickWorldEffects();
				}, "Triggered host cycle");
			}, Gold);
			Button((Transform)(object)parent, 803f, 491f, 232f, 35f, "Reload active stack", delegate
			{
				testingDraft.Clear();
				ActiveModifier[] active = modifiers.Active;
				foreach (ActiveModifier val2 in active)
				{
					testingDraft.Add(val2.Id);
				}
				testingMessage = "Draft copied from active effects.";
			}, Green);
			Button((Transform)(object)parent, 553f, 535f, 152f, 28f, "Thunder nearby", delegate
			{
				TestAction(delegate
				{
					SpawnThunderProof(here: false);
				}, "One vanilla storm spawned nearby. Its lightning can hit players.");
			}, Gold);
			Button((Transform)(object)parent, 718f, 535f, 152f, 28f, "Thunder here", delegate
			{
				TestAction(delegate
				{
					SpawnThunderProof(here: true);
				}, "One vanilla storm centered here. Its lightning can hit players.");
			}, Gold);
			Button((Transform)(object)parent, 883f, 535f, 152f, 28f, "Stop thunder", delegate
			{
				TestAction(StopThunderProof, "Test storm cancelled.");
			}, Green);
			Label((Transform)(object)parent, 24f, 570f, 1020f, 55f, testingMessage, 17f, Ink);
		}

		private void ConfigureIntermission()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			stage = ((BaseUnityPlugin)this).Config.Bind<PrototypeStage>("General", "Stage", PrototypeStage.Modifiers, "Modifiers (canonical catalogue), Voting (dummy cards), Intermission, or Lifecycle.");
			holdSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Intermission", "DurationSeconds", 5f, new ConfigDescription("Phase 2 automatic hold duration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			nextMethod = AccessTools.DeclaredMethod(typeof(CourseManager), "ServerStartNextMatch", new Type[1] { typeof(bool) }, (Type[])null);
		}

		private bool InterceptNext(CourseManager manager, bool skipInventories)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Invalid comparison between Unknown and I4
			if (releasing || !NetworkServer.active || !Run.Active || runStage == PrototypeStage.Lifecycle || Run.HoleIndex < 0)
			{
				return true;
			}
			if (gate.WasIntercepted(Run.RunId, Run.HoleIndex))
			{
				return false;
			}
			if ((int)Run.State != 4)
			{
				return true;
			}
			if (!bridge.CanStart(out notice))
			{
				ShowNotice(notice);
				ClearRunResources();
				Run.Stop("PeerNotCompatible");
				return true;
			}
			if (runStage == PrototypeStage.Modifiers && modifiers == null)
			{
				return true;
			}
			string id = Run.RunId;
			int hole = Run.HoleIndex;
			gate.Hold(id, hole, (Action)delegate
			{
				releasing = true;
				try
				{
					nextMethod.Invoke(manager, new object[1] { skipInventories });
				}
				finally
				{
					releasing = false;
				}
			}, (Func<bool>)(() => (Object)(object)manager != (Object)null && NetworkServer.active && Run.RunId == id && Run.HoleIndex == hole && Run.Active));
			releaseAt = NetworkTime.time + (double)holdSeconds.Value;
			NextMatchCountdown.Hide();
			VoteCoordinator obj = voting;
			if (obj != null)
			{
				obj.ClearVote();
			}
			Run.Transition((RunState)4, (RunState)5, "IntermissionHeld");
			if (runStage == PrototypeStage.Voting || runStage == PrototypeStage.Modifiers)
			{
				OpenVote();
			}
			return false;
		}

		private void ReleaseIntermission()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (gate.Held && NetworkServer.active)
			{
				Run.Transition(Run.State, (RunState)2, "IntermissionReleased");
				if (!gate.Release())
				{
					ClearRunResources();
					Run.Stop("InvalidTransitionTarget");
				}
			}
		}

		private void Update()
		{
			UpdateRangeTesting();
			bridge?.Update();
			UpdateUIInput();
			TickWorldEffects();
			TickThunderProof();
			TickActiveEvents();
			TickLab();
			if (NetworkServer.active && Run.Active && !enabledMode.Value)
			{
				ReleaseIntermission();
				ClearRunResources();
				Run.Stop("Disabled");
			}
			if (runStage == PrototypeStage.Voting || runStage == PrototypeStage.Modifiers)
			{
				TickVote();
			}
			else if (gate.Held && NetworkTime.time >= releaseAt)
			{
				ReleaseIntermission();
			}
		}

		private void ConfigureLab()
		{
			labCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LabCommands", false, "Solo private host only: process RogueGolf.lab.txt in this profile config directory. Testing only.");
		}

		private void TickLab()
		{
			//IL_05db: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a7d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a86: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Invalid comparison between Unknown and I4
			//IL_0107: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Expected O, but got Unknown
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_0534: Unknown result type (might be due to invalid IL or missing references)
			//IL_0536: Unknown result type (might be due to invalid IL or missing references)
			//IL_0540: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_0578: Unknown result type (might be due to invalid IL or missing references)
			//IL_057a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0584: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_0780: Unknown result type (might be due to invalid IL or missing references)
			//IL_0785: Unknown result type (might be due to invalid IL or missing references)
			//IL_078a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0851: Unknown result type (might be due to invalid IL or missing references)
			//IL_0856: Unknown result type (might be due to invalid IL or missing references)
			//IL_085a: Unknown result type (might be due to invalid IL or missing references)
			//IL_085f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0863: Unknown result type (might be due to invalid IL or missing references)
			//IL_0868: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0add: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0af1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b09: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b1a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b24: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b35: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0905: Unknown result type (might be due to invalid IL or missing references)
			//IL_0916: Unknown result type (might be due to invalid IL or missing references)
			//IL_0920: Unknown result type (might be due to invalid IL or missing references)
			//IL_093b: Unknown result type (might be due to invalid IL or missing references)
			//IL_097c: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<bool> obj = labCommands;
			if (obj == null || !obj.Value || NetworkTime.time < nextLabRead)
			{
				return;
			}
			nextLabRead = NetworkTime.time + 0.25;
			string path = Path.Combine(Paths.ConfigPath, "RogueGolf.lab.txt");
			if (!File.Exists(path))
			{
				return;
			}
			string text = File.ReadAllText(path).Trim();
			File.Delete(path);
			try
			{
				if (!NetworkServer.active || NetworkServer.connections.Count != 1 || bridge == null || !bridge.CanStart(out var _) || (int)Run.State != 3)
				{
					throw new InvalidOperationException("Lab requires a playing hole in a solo private host run");
				}
				if (text.Length > 256)
				{
					throw new ArgumentException("Lab request too long");
				}
				string[] array = text.Split(' ');
				string text2 = array[0];
				if (text2 == "apply" && array.Length == 2)
				{
					ModifierRegistry val = ModifierRegistry.Create(modifierSettings);
					ModifierDefinition val2 = val.Get(array[1]);
					if (val2 == null || (EffectChannel)(val2.Channel & Capabilities) != val2.Channel)
					{
						throw new ArgumentException("Unsupported lab modifier");
					}
					if (val2.Id == "golf_kart_warfare" && Object.FindObjectsByType<TrafficSpawner>((FindObjectsInactive)0, (FindObjectsSortMode)0).Length == 0)
					{
						throw new ArgumentException("No native traffic in current scene");
					}
					RestoreEffects();
					modifiers = new ModifierManager(val, val.Definitions.Select((ModifierDefinition d) => d.Id), Capabilities);
					modifiers.ContextAllows = AllowsContext;
					if (!modifiers.Activate(val2.Id, Run.HoleIndex, (PersistenceMode)0))
					{
						throw new InvalidOperationException("Lab activation rejected");
					}
					ApplyEffects(Run.RunId, gravityBaseline, modifiers.Values);
					StartHoleEffects(Run.HoleIndex);
					Publish();
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[LAB] Applied " + val2.Id));
				}
				else if (text2 == "stack" && array.Length > 1 && array.Length <= 13)
				{
					ModifierRegistry obj2 = ModifierRegistry.Create(modifierSettings);
					ModifierManager val3 = new ModifierManager(obj2, obj2.Definitions.Select((ModifierDefinition d) => d.Id), Capabilities);
					val3.ContextAllows = AllowsContext;
					foreach (string item in array.Skip(1))
					{
						if (!val3.Activate(item, Run.HoleIndex, (PersistenceMode)0))
						{
							throw new ArgumentException("Incompatible lab stack: " + item);
						}
					}
					RestoreEffects();
					modifiers = val3;
					ApplyEffects(Run.RunId, gravityBaseline, modifiers.Values);
					StartHoleEffects(Run.HoleIndex);
					Publish();
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[LAB] Applied stack " + string.Join(",", array.Skip(1))));
				}
				else
				{
					switch (text2)
					{
					case "clear":
						RestoreEffects();
						Publish();
						break;
					case "empty":
					{
						PlayerInfo[] array2 = Players();
						foreach (PlayerInfo val6 in array2)
						{
							for (int num3 = 0; num3 < val6.Inventory.slots.Count; num3++)
							{
								if ((int)val6.Inventory.slots[num3].itemType != 0)
								{
									removeItem.Invoke(val6.Inventory, new object[3] { num3, false, false });
								}
							}
						}
						break;
					}
					case "tick":
						nextWorldTick = 0.0;
						TickWorldEffects();
						break;
					case "cup":
					{
						GolfBall ownBall4 = Players().First().AsGolfer.OwnBall;
						ownBall4.AsEntity.InformWillTeleport();
						ownBall4.Rigidbody.position = ((Component)GolfHoleManager.MainHole).transform.position + Vector3.up * 2f;
						((Component)ownBall4).transform.position = ownBall4.Rigidbody.position;
						ownBall4.Rigidbody.linearVelocity = Vector3.zero;
						ownBall4.AsEntity.InformTeleported();
						break;
					}
					case "swap_probe":
					{
						PlayerGolfer asGolfer = Players().First().AsGolfer;
						GolfBall ownBall = asGolfer.OwnBall;
						Vector3 position = ownBall.Rigidbody.position;
						GolfBall val4 = asGolfer.ServerSpawnBall(position + Vector3.right * 2f, Quaternion.identity);
						try
						{
							RotateBalls((GolfBall[])(object)new GolfBall[2] { ownBall, val4 });
							if (Vector3.Distance(ownBall.Rigidbody.position, position + Vector3.right * 2f) > 0.01f || Vector3.Distance(val4.Rigidbody.position, position) > 0.01f)
							{
								throw new InvalidOperationException("Native swap failed");
							}
							((BaseUnityPlugin)this).Logger.LogInfo((object)"[LAB] Two native ball swap passed");
						}
						finally
						{
							ownBall.Rigidbody.position = position;
							((Component)ownBall).transform.position = position;
							asGolfer.NetworkownBall = ownBall;
							NetworkServer.Destroy(((Component)val4).gameObject);
						}
						break;
					}
					case "equip_pistol":
					{
						PlayerInventory inventory = Players().First().Inventory;
						Grant(inventory, (ItemType)2, replaceIfFull: true);
						inventory.TrySelectItemSlot(0, false);
						break;
					}
					case "use_item":
					{
						bool flag2 = default(bool);
						bool flag = Players().First().Inventory.TryUseItem(false, ref flag2);
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[LAB] Native item use=" + flag + " ateInput=" + flag2));
						break;
					}
					case "thunder":
						SpawnThunderProof(here: false);
						break;
					case "thunder_here":
						SpawnThunderProof(here: true);
						break;
					case "thunder_stop":
						StopThunderProof();
						break;
					case "thunder_status":
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[THUNDER] proof=" + (thunderProof != null) + " storms=" + Object.FindObjectsByType<Thunderstorm>((FindObjectsInactive)0, (FindObjectsSortMode)0).Length + " strikes=" + Object.FindObjectsByType<ThunderstormLightningStrike>((FindObjectsInactive)0, (FindObjectsSortMode)0).Length));
						break;
					case "explode":
						TryExplosion(((Component)Players().First().AsGolfer.OwnBall).transform.position + Vector3.up);
						break;
					case "spawn_blast_neighbor":
					{
						PlayerGolfer asGolfer3 = Players().First().AsGolfer;
						GolfBall ownBall5 = asGolfer3.OwnBall;
						if ((Object)(object)labBlastNeighbor != (Object)null)
						{
							NetworkServer.Destroy(((Component)labBlastNeighbor).gameObject);
						}
						labBlastNeighbor = asGolfer3.ServerSpawnBall(ownBall5.Rigidbody.position + Vector3.right, Quaternion.identity);
						asGolfer3.NetworkownBall = ownBall5;
						break;
					}
					case "source_blast_probe":
					{
						PlayerGolfer asGolfer2 = Players().First().AsGolfer;
						GolfBall ownBall3 = asGolfer2.OwnBall;
						Rigidbody rigidbody2 = ownBall3.Rigidbody;
						Vector3 position2 = rigidbody2.position;
						Vector3 linearVelocity = rigidbody2.linearVelocity;
						Vector3 angularVelocity = rigidbody2.angularVelocity;
						GolfBall val5 = labBlastNeighbor;
						if ((Object)(object)val5 == (Object)null)
						{
							throw new InvalidOperationException("Run spawn_blast_neighbor first, then let native initialization finish");
						}
						int sourceHits = 0;
						float neighborImpulse = 0f;
						Action<PlayerInventory, ItemType, ItemUseId, Vector3, float, Vector3, bool, bool, ProtectiveState> action = delegate
						{
							sourceHits++;
						};
						Action<PlayerInventory, ItemType, ItemUseId, Vector3, float, Vector3, bool, bool, ProtectiveState> action2 = delegate(PlayerInventory a, ItemType b, ItemUseId c, Vector3 d, float e, Vector3 f, bool g, bool h, ProtectiveState i)
						{
							neighborImpulse += ((Vector3)(ref f)).magnitude;
						};
						ownBall3.AsEntity.AsHittable.WillApplyItemHitPhysics += action;
						val5.AsEntity.AsHittable.WillApplyItemHitPhysics += action2;
						try
						{
							val5.Rigidbody.position = position2 + Vector3.right;
							((Component)val5).transform.position = val5.Rigidbody.position;
							val5.Rigidbody.linearVelocity = Vector3.right * 5f;
							rigidbody2.linearVelocity = new Vector3(1f, 2f, 3f);
							MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(GolfBall), "UpdateIsStationary", (Type[])null, (Type[])null);
							methodInfo.Invoke(ownBall3, null);
							methodInfo.Invoke(val5, null);
							Physics.SyncTransforms();
							int num = explosionsThisHole;
							TryExplosion(position2, ownBall3.AsEntity.AsHittable);
							if (explosionsThisHole != num + 1 || sourceHits != 0 || neighborImpulse < 0.01f)
							{
								throw new InvalidOperationException("Blast