Decompiled source of ShrineOfChance v0.5.1

plugins/MuckShrineOfChance.dll

Decompiled 16 hours ago
using System;
using System.Collections;
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.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckShrineOfChance")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds Risk of Rain 2's Shrine of Chance to Muck. Pay coins for a chance at a powerup.")]
[assembly: AssemblyFileVersion("0.5.1.0")]
[assembly: AssemblyInformationalVersion("0.5.1+5caa96df932c509d2f99e20681f4a02fa2d68991")]
[assembly: AssemblyProduct("Shrine of Chance")]
[assembly: AssemblyTitle("MuckShrineOfChance")]
[assembly: InternalsVisibleTo("MuckShrineOfChance.Tests")]
[assembly: AssemblyVersion("0.5.1.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckShrineOfChance
{
	internal sealed class AnnounceGate
	{
		private bool pending;

		public void Paid()
		{
			pending = true;
		}

		public string Committed(ShrineOutcome outcome, ShrineOdds odds)
		{
			if (!pending)
			{
				return null;
			}
			pending = false;
			return ShrineText.Message(outcome, odds) + " " + ShrineText.OddsLine(odds);
		}

		public void Skipped()
		{
			pending = false;
		}
	}
	internal static class BossAlert
	{
		public static void Broadcast(string playerName)
		{
			string text = BossAlertText.Line(playerName);
			ServerSend.SendChatMessage(-1, "", text);
			Present(text);
		}

		public static void Present(string message)
		{
			if (BossAlertText.IsAlert(message) && BossAlertGate.Accept(message, Time.time) && (Object)(object)ShrineNotices.Instance != (Object)null)
			{
				ShrineNotices.Instance.ShowBanner(message);
			}
		}
	}
	internal static class BossAlertGate
	{
		private static string last;

		private static float lastTime = -1000f;

		public static bool Accept(string message, float now)
		{
			if (message == last && now - lastTime < NoticeTimeline.BossBanner.Hold + NoticeTimeline.BossBanner.Fade)
			{
				return false;
			}
			last = message;
			lastTime = now;
			return true;
		}

		public static void Reset()
		{
			last = null;
			lastTime = -1000f;
		}
	}
	internal static class BossAlertText
	{
		public const string Tail = " rolled a BOSS at the Shrine of Chance!";

		public static string Line(string playerName)
		{
			if (string.IsNullOrEmpty(playerName))
			{
				playerName = "Someone";
			}
			return playerName + " rolled a BOSS at the Shrine of Chance!";
		}

		public static bool IsAlert(string message)
		{
			if (message != null && message.EndsWith(" rolled a BOSS at the Shrine of Chance!"))
			{
				return message.Length > " rolled a BOSS at the Shrine of Chance!".Length;
			}
			return false;
		}

		public static bool IsSystemAlert(int fromUser, string fromUsername, string message)
		{
			if (fromUser != -1 || !string.IsNullOrEmpty(fromUsername))
			{
				return false;
			}
			return IsAlert(message);
		}
	}
	[HarmonyPatch(typeof(ChatBox), "AppendMessage")]
	internal static class BossChatPatch
	{
		private static void Postfix(int fromUser, string message, string fromUsername)
		{
			if (BossAlertText.IsSystemAlert(fromUser, fromUsername, message))
			{
				BossAlert.Present(message);
			}
		}
	}
	public class ChanceShrine : MonoBehaviour, Interactable, SharedObject
	{
		private static readonly Color Stone = new Color(0.45f, 0.45f, 0.45f);

		private static readonly Color Gold = new Color(1f, 0.8f, 0.2f);

		private static readonly Color SpentOrb = Color.gray;

		private int id;

		private ShrineExchange exchange;

		private float pendingUntil;

		private Renderer orb;

		private Material pillarMaterial;

		private Material orbMaterial;

		private SlotMachineView view;

		private readonly AnnounceGate announce = new AnnounceGate();

		public static void Build(int id, Vector3 ground)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ShrineOfChance");
			val.transform.position = ground;
			val.layer = 15;
			BoxCollider obj = val.AddComponent<BoxCollider>();
			((Collider)obj).isTrigger = true;
			obj.size = new Vector3(3f, 4f, 3f);
			obj.center = new Vector3(0f, 2f, 0f);
			ChanceShrine chanceShrine = val.AddComponent<ChanceShrine>();
			chanceShrine.SetId(id);
			MakePart(val.transform, (PrimitiveType)2, new Vector3(0f, 1.2f, 0f), new Vector3(1.6f, 1.2f, 1.6f), Stone, out chanceShrine.pillarMaterial);
			chanceShrine.orb = MakePart(val.transform, (PrimitiveType)0, new Vector3(0f, 3f, 0f), new Vector3(0.9f, 0.9f, 0.9f), Gold, out chanceShrine.orbMaterial);
			chanceShrine.view = val.AddComponent<SlotMachineView>();
			ResourceManager.Instance.AddObject(id, val);
		}

		public void Interact()
		{
			if ((Object)(object)view != (Object)null && view.Spinning)
			{
				Say("The reels are still spinning.");
				return;
			}
			if (Time.time >= pendingUntil)
			{
				exchange.CancelOffer();
			}
			else if (exchange.Offering)
			{
				return;
			}
			if (exchange.Shown.Spent)
			{
				return;
			}
			int useIndex = exchange.Shown.UseIndex;
			int num = PriceAt(useIndex);
			int money = InventoryUI.Instance.GetMoney();
			if (money < num)
			{
				Say($"Not enough coins (${num}).");
				return;
			}
			int seed = GameManager.GetSeed();
			if (exchange.TryOffer(money, num, RollAt(seed, useIndex), useIndex))
			{
				pendingUntil = Time.time + 3f;
				ClientSend.Interact(id);
			}
		}

		public void LocalExecute()
		{
			Charge charge = exchange.TakeCharge();
			if (charge.Pay)
			{
				InventoryUI.Instance.UseMoney(charge.Price);
				announce.Paid();
			}
		}

		public void ServerExecute(int fromClient = -1)
		{
			//IL_00e9: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			if (!LocalClient.serverOwner || exchange.HostSpent)
			{
				return;
			}
			ShrineSync.Hold(null);
			int seed = GameManager.GetSeed();
			int hostUseIndex = exchange.HostUseIndex;
			ShrineOdds odds = OddsFor(seed, hostUseIndex);
			HostStep step = exchange.HostResolve(ShrineRoll.Outcome(seed, id, hostUseIndex, odds));
			if (!step.Refused)
			{
				ShrineSync.Hold(ShrineVerdict.Of(id, step.UseIndex, step.Outcome, odds));
				if (ShrinePlugin.Cfg.VerboseLogging.Value)
				{
					ShrinePlugin.Log.LogInfo((object)$"Shrine {id} use {step.UseIndex} accepted {step.Outcome} for client {fromClient}.");
				}
				Vector3 position = ((Component)this).transform.position;
				ShrinePlugin.Instance.PayLater(id, seed, step, position, position + Vector3.up * 2.5f, fromClient);
			}
		}

		public void AllExecute()
		{
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			if (exchange.Shown.Spent)
			{
				pendingUntil = 0f;
				announce.Skipped();
				return;
			}
			int useIndex = exchange.Shown.UseIndex;
			int seed = GameManager.GetSeed();
			ShrineOdds odds = OddsFor(seed, useIndex);
			ShrineOutcome shrineOutcome = ShrineRoll.Outcome(seed, id, useIndex, odds);
			if (ShrineSync.TakeIncoming(id, out var verdict))
			{
				ShrineOutcome shrineOutcome2 = shrineOutcome;
				shrineOutcome = verdict.Resolve(ref odds);
				if (shrineOutcome != shrineOutcome2 || verdict.UseIndex != useIndex)
				{
					ShrinePlugin.Log.LogWarning((object)$"Shrine {id}: the host rolled {ShrineText.Label(shrineOutcome, odds)} on use {verdict.UseIndex}, this game rolled {ShrineText.Label(shrineOutcome2, odds)} on use {useIndex}. Showing the host's roll. Compare the Shrine of Chance config code in each player's log.");
				}
			}
			if (ShrinePlugin.Cfg.VerboseLogging.Value)
			{
				ShrinePlugin.Log.LogInfo((object)$"Shrine {id} use {useIndex} rolled {shrineOutcome}.");
			}
			ReelSymbol[] final = ReelSymbols.For(shrineOutcome, ShrineRoll.ReelRoll(seed, id, useIndex, 0), ShrineRoll.ReelRoll(seed, id, useIndex, 1), ShrineRoll.ReelRoll(seed, id, useIndex, 2));
			exchange.Commit(shrineOutcome);
			pendingUntil = 0f;
			if (ShrineOutcomes.ActionOf(shrineOutcome) == ShrineAction.SpawnNuke)
			{
				((MonoBehaviour)ShrinePlugin.Instance).StartCoroutine(NukePickup.SpawnAfter(2.6200001f, NukeIds.For(id, useIndex), ((Component)this).transform.position + NukePickup.ShrineOffset));
			}
			if ((Object)(object)view != (Object)null)
			{
				view.Play(final, odds, OnSettled);
			}
			string text = announce.Committed(shrineOutcome, odds);
			if (text != null)
			{
				if ((Object)(object)ShrineNotices.Instance != (Object)null)
				{
					ShrineNotices.Instance.ShowOdds(odds);
				}
				((MonoBehaviour)this).StartCoroutine(SayAfter(2.6200001f, text));
			}
		}

		public void RemoveObject()
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		public string GetName()
		{
			bool spent = exchange.Shown.Spent && ((Object)(object)view == (Object)null || !view.Spinning);
			return ShrineText.Name(PriceAt(exchange.Shown.UseIndex), spent);
		}

		public bool IsStarted()
		{
			return exchange.Shown.Spent;
		}

		public void SetId(int id)
		{
			this.id = id;
		}

		public int GetId()
		{
			return id;
		}

		private void Awake()
		{
			exchange = new ShrineExchange(ShrinePlugin.Cfg.MaxSuccesses.Value);
		}

		private void OnDestroy()
		{
			if ((Object)(object)pillarMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)pillarMaterial);
			}
			if ((Object)(object)orbMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)orbMaterial);
			}
		}

		private int PriceAt(int useIndex)
		{
			float dayMultiplier = 1f;
			if (ShrinePlugin.Cfg.ScaleWithChestPrice.Value)
			{
				dayMultiplier = GameManager.instance.ChestPriceMultiplier();
			}
			return ShrinePricing.Price(useIndex, dayMultiplier, ShrinePlugin.Cfg.BasePrice.Value, ShrinePlugin.Cfg.PriceGrowth.Value);
		}

		private void OnSettled()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (exchange.Shown.Spent && (Object)(object)orbMaterial != (Object)null)
			{
				orbMaterial.color = SpentOrb;
			}
		}

		private IEnumerator SayAfter(float seconds, string message)
		{
			yield return (object)new WaitForSeconds(seconds);
			Say(message);
		}

		private ShrineOutcome RollAt(int seed, int useIndex)
		{
			return ShrineRoll.Outcome(seed, id, useIndex, OddsFor(seed, useIndex));
		}

		private ShrineOdds OddsFor(int seed, int useIndex)
		{
			if (ShrinePlugin.Cfg.ForceOutcome.Value != ForcedOutcome.Off)
			{
				return ShrineOdds.Forced(ShrineConfig.AsOutcome(ShrinePlugin.Cfg.ForceOutcome.Value));
			}
			ShrineOdds shrineOdds;
			if (ShrinePlugin.Cfg.RandomizeOdds.Value)
			{
				ShrinePlugin.Cfg.ChanceBounds(out var min, out var max);
				shrineOdds = OddsSampler.Draw(seed, id, useIndex, min, max);
			}
			else
			{
				shrineOdds = ShrinePlugin.Cfg.Odds();
			}
			return ShrineOutcomeRegistry.Apply(shrineOdds.WithNuke(ShrinePlugin.Cfg.NukeShare()));
		}

		private static void Say(string message)
		{
			ChatBox instance = ChatBox.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.AppendMessage(-1, message, "");
			}
		}

		private static Renderer MakePart(Transform parent, PrimitiveType type, Vector3 localPosition, Vector3 scale, Color color, out Material material)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive(type);
			obj.layer = 10;
			obj.transform.SetParent(parent, false);
			obj.transform.localPosition = localPosition;
			obj.transform.localScale = scale;
			Renderer component = obj.GetComponent<Renderer>();
			material = component.material;
			material.color = color;
			return component;
		}
	}
	internal static class Hash01
	{
		public static ulong SplitMix64(ulong x)
		{
			long num = (long)x + -7046029254386353131L;
			long num2 = (num ^ (num >>> 30)) * -4658895280553007687L;
			long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L;
			return (ulong)(num3 ^ (num3 >>> 31));
		}

		public static double Of(int a, int b)
		{
			return ToUnit(SplitMix64(Key(a, b)));
		}

		public static double Of(int a, int b, int c)
		{
			return ToUnit(SplitMix64(SplitMix64(Key(a, b)) ^ (uint)c));
		}

		private static ulong Key(int a, int b)
		{
			return ((ulong)(uint)a << 32) | (uint)b;
		}

		private static double ToUnit(ulong h)
		{
			return (double)(h >> 11) * 1.1102230246251565E-16;
		}
	}
	internal static class LeverTimeline
	{
		public const float Duration = 0.5f;

		public const float Bottom = 0.22f;

		public const float MaxDegrees = 70f;

		public static float Pull(float t)
		{
			if (t <= 0f || t >= 0.5f)
			{
				return 0f;
			}
			if (t < 0.22f)
			{
				return 70f * Smooth(t / 0.22f);
			}
			return 70f * (1f - Smooth((t - 0.22f) / 0.28f));
		}

		public static float Pitch(float t)
		{
			return 0f - Pull(t);
		}

		public static bool ReelsStart(float t)
		{
			return t >= 0.22f;
		}

		private static float Smooth(float u)
		{
			return u * u * (3f - 2f * u);
		}
	}
	internal readonly struct NoticeTimeline
	{
		public float Hold { get; }

		public float Fade { get; }

		public static NoticeTimeline OddsPopup => new NoticeTimeline(2f, 0.4f);

		public static NoticeTimeline BossBanner => new NoticeTimeline(3f, 0.5f);

		public static NoticeTimeline NukeFlash => new NoticeTimeline(0f, 0.6f);

		public NoticeTimeline(float hold, float fade)
		{
			Hold = hold;
			Fade = ((fade < 0f) ? 0f : fade);
		}

		public float Alpha(float t)
		{
			if (t < 0f)
			{
				return 0f;
			}
			if (t <= Hold)
			{
				return 1f;
			}
			if (Fade <= 0f || t >= Hold + Fade)
			{
				return 0f;
			}
			return 1f - (t - Hold) / Fade;
		}

		public bool Visible(float t)
		{
			return Alpha(t) > 0f;
		}
	}
	internal static class NukeArt
	{
		public const string ObjResource = "MuckShrineOfChance.nuke.obj";

		public const string JsonResource = "MuckShrineOfChance.nuke.json";

		internal static readonly IReadOnlyDictionary<string, MaterialSpec> Recolour = new Dictionary<string, MaterialSpec>
		{
			{
				"sticky_band",
				new MaterialSpec(0.35f, 1f, 0.2f, 1f)
			},
			{
				"sticky_spark",
				new MaterialSpec(0.7f, 1f, 0.45f, 1f)
			},
			{
				"sticky_glue",
				new MaterialSpec(0.12f, 0.45f, 0.1f, 0f)
			}
		};

		private static bool built;

		private static Mesh mesh;

		private static Material material;

		public static bool TryGet(out Mesh pickupMesh, out Material pickupMaterial)
		{
			if (!built)
			{
				built = true;
				Build();
			}
			pickupMesh = mesh;
			pickupMaterial = material;
			if ((Object)(object)mesh != (Object)null)
			{
				return (Object)(object)material != (Object)null;
			}
			return false;
		}

		internal static PickupModel Recoloured(PickupModel model)
		{
			Dictionary<string, MaterialSpec> dictionary = new Dictionary<string, MaterialSpec>();
			foreach (KeyValuePair<string, MaterialSpec> material in model.Materials)
			{
				dictionary[material.Key] = (Recolour.TryGetValue(material.Key, out var value) ? value : material.Value);
			}
			return new PickupModel(model.Name, model.Positions, model.Normals, model.SubMeshes, dictionary);
		}

		private static void Build()
		{
			try
			{
				PickupMesh data = PickupMesh.From(Recoloured(PickupModel.Parse(Read("MuckShrineOfChance.nuke.obj"), Read("MuckShrineOfChance.nuke.json"))));
				Material val = DonorMaterial();
				if ((Object)(object)val == (Object)null)
				{
					val = FallbackShader();
				}
				if ((Object)(object)val == (Object)null)
				{
					throw new InvalidOperationException("no shader to draw it with");
				}
				bool vertexColors = !val.HasProperty("_MainTex");
				mesh = ToMesh(data, vertexColors);
				material = ToMaterial(data, val);
			}
			catch (Exception ex)
			{
				mesh = null;
				material = null;
				ShrinePlugin.Log.LogWarning((object)("Nuke pickup model was not built (" + ex.Message + "). The nuke shows as a green light only."));
			}
		}

		private static Material DonorMaterial()
		{
			ItemManager instance = ItemManager.Instance;
			if (instance == null || instance.powerupsWhite == null)
			{
				return null;
			}
			Powerup[] powerupsWhite = instance.powerupsWhite;
			foreach (Powerup val in powerupsWhite)
			{
				if (val != null && (Object)(object)val.material != (Object)null)
				{
					return val.material;
				}
			}
			return null;
		}

		private static string Read(string name)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
			if (stream == null)
			{
				throw new FileNotFoundException(name);
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static Mesh ToMesh(PickupMesh data, bool vertexColors)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = new Mesh
			{
				name = "Nuke pickup",
				hideFlags = (HideFlags)32
			};
			List<Vector3> list = new List<Vector3>(data.Positions.Length);
			List<Vector3> list2 = new List<Vector3>(data.Normals.Length);
			List<Vector2> list3 = new List<Vector2>(data.U.Length);
			List<Color> list4 = new List<Color>(data.Positions.Length);
			for (int i = 0; i < data.Positions.Length; i++)
			{
				list.Add(new Vector3(data.Positions[i].X, data.Positions[i].Y, data.Positions[i].Z));
				list2.Add(new Vector3(data.Normals[i].X, data.Normals[i].Y, data.Normals[i].Z));
				list3.Add(new Vector2(data.U[i], 0.5f));
				list4.Add((Color)(vertexColors ? new Color(data.R[i], data.G[i], data.B[i], 1f) : Color.white));
			}
			val.SetVertices(list);
			val.SetNormals(list2);
			val.SetUVs(0, list3);
			val.SetColors(list4);
			val.SetTriangles(data.Triangles, 0);
			val.RecalculateBounds();
			return val;
		}

		private static Material FallbackShader()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Shader val = Shader.Find("Standard");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Diffuse");
			}
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return new Material(val);
		}

		private static Material ToMaterial(PickupMesh data, Material source)
		{
			//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0073: 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)
			//IL_004f: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(source)
			{
				name = "Nuke pickup",
				hideFlags = (HideFlags)32
			};
			if (!source.HasProperty("_MainTex"))
			{
				MaterialSpec materialSpec = Recolour["sticky_band"];
				val.color = new Color(materialSpec.R, materialSpec.G, materialSpec.B, 1f);
				return val;
			}
			val.mainTexture = (Texture)(object)Strip(data.Palette, "Nuke palette", glowOnly: false);
			val.mainTextureScale = Vector2.one;
			val.mainTextureOffset = Vector2.zero;
			if (val.HasProperty("_Color"))
			{
				val.color = Color.white;
			}
			if (val.HasProperty("_EmissionColor") && val.HasProperty("_EmissionMap"))
			{
				val.SetTexture("_EmissionMap", (Texture)(object)Strip(data.Palette, "Nuke glow", glowOnly: true));
				val.SetColor("_EmissionColor", Color.white);
				val.EnableKeyword("_EMISSION");
			}
			return val;
		}

		private static Texture2D Strip(IReadOnlyList<MaterialSpec> palette, string name, bool glowOnly)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(palette.Count, 1, (TextureFormat)4, false)
			{
				name = name,
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)32
			};
			for (int i = 0; i < palette.Count; i++)
			{
				MaterialSpec materialSpec = palette[i];
				bool flag = !glowOnly || materialSpec.Emission > 0f;
				val.SetPixel(i, 0, (Color)(flag ? new Color(materialSpec.R, materialSpec.G, materialSpec.B, 1f) : Color.black));
			}
			val.Apply(false, true);
			return val;
		}
	}
	internal static class NukeBlast
	{
		public const string Banner = "KABOOM!";

		public static void Play()
		{
			ShrineNotices instance = ShrineNotices.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.Flash();
				instance.ShowBanner("KABOOM!");
			}
			try
			{
				NukeSound.Play();
			}
			catch (Exception arg)
			{
				ShrinePlugin.Log.LogError((object)$"Nuke sound failed: {arg}");
			}
			CameraShaker instance2 = CameraShaker.Instance;
			if ((Object)(object)instance2 != (Object)null)
			{
				instance2.DamageShake(1f);
			}
		}

		public static IEnumerator KillWave(Vector3 at)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			int scene = ((Scene)(ref activeScene)).handle;
			MobManager instance = MobManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.mobs == null)
			{
				yield break;
			}
			List<int> ids = new List<int>();
			List<float> list = new List<float>();
			foreach (KeyValuePair<int, Mob> mob in instance.mobs)
			{
				Mob value = mob.Value;
				if (!((Object)(object)value == (Object)null))
				{
					bool flag = (Object)(object)value.mobType != (Object)null;
					if (NukeRules.Kills(flag && (int)value.mobType.behaviour == 0, (int)value.bossType > 0, flag && value.mobType.boss))
					{
						ids.Add(mob.Key);
						list.Add(Vector3.Distance(at, ((Component)value).transform.position));
					}
				}
			}
			float[] delays = NukeStagger.Delays(list.ToArray(), ShrinePlugin.Cfg.NukeKillDelay.Value, ShrinePlugin.Cfg.NukeKillSpread.Value);
			List<int> order = new List<int>(ids.Count);
			for (int i = 0; i < ids.Count; i++)
			{
				order.Add(i);
			}
			order.Sort((int a, int b) => (delays[a] == delays[b]) ? a.CompareTo(b) : delays[a].CompareTo(delays[b]));
			if (ShrinePlugin.Cfg.VerboseLogging.Value)
			{
				ShrinePlugin.Log.LogInfo((object)$"Nuke: {ids.Count} enemies to kill.");
			}
			float start = Time.time;
			int next = 0;
			while (next < order.Count)
			{
				activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).handle != scene || !LocalClient.serverOwner)
				{
					break;
				}
				for (float num = Time.time - start; next < order.Count && delays[order[next]] <= num; next++)
				{
					Kill(ids[order[next]]);
				}
				if (next < order.Count)
				{
					yield return null;
				}
			}
		}

		public static IEnumerator PayAfter(float seconds, int gold)
		{
			Scene activeScene = SceneManager.GetActiveScene();
			int scene = ((Scene)(ref activeScene)).handle;
			yield return (object)new WaitForSeconds(seconds);
			activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).handle != scene || gold <= 0)
			{
				yield break;
			}
			try
			{
				PayCoins(gold);
				Say(ShrineText.NukeLine(gold));
			}
			catch (Exception arg)
			{
				ShrinePlugin.Log.LogError((object)$"Nuke payout failed: {arg}");
			}
		}

		private static void Kill(int id)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MobManager instance = MobManager.Instance;
				if (!((Object)(object)instance == (Object)null) && instance.mobs != null && instance.mobs.TryGetValue(id, out var value) && !((Object)(object)value == (Object)null) && !((Object)(object)value.hitable == (Object)null))
				{
					int myId = LocalClient.instance.myId;
					Vector3 position = ((Component)value).transform.position;
					value.countedKill = true;
					value.hitable.Damage(0, myId, 0, position);
					ServerSend.PlayerHitMob(myId, id, 0, 0, position, -1);
				}
			}
			catch (Exception arg)
			{
				ShrinePlugin.Log.LogError((object)$"Nuke: killing mob {id} failed: {arg}");
			}
		}

		private static void PayCoins(int gold)
		{
			ItemManager instance = ItemManager.Instance;
			InventoryUI instance2 = InventoryUI.Instance;
			if (instance == null || (Object)(object)instance2 == (Object)null)
			{
				return;
			}
			InventoryItem itemByName = instance.GetItemByName("Coin");
			if ((Object)(object)itemByName == (Object)null)
			{
				ShrinePlugin.Log.LogWarning((object)"Nuke payout: Muck has no item named Coin.");
				return;
			}
			InventoryItem val = Object.Instantiate<InventoryItem>(itemByName);
			val.amount = gold;
			int num = instance2.AddItemToInventory(val);
			if (num > 0)
			{
				val.amount = num;
				instance2.DropItemIntoWorld(val);
			}
			Object.Destroy((Object)(object)val);
		}

		private static void Say(string message)
		{
			ChatBox instance = ChatBox.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.AppendMessage(-1, message, "");
			}
		}
	}
	internal static class NukeIds
	{
		public const int Base = 1302000000;

		public const int UsesPerShrine = 1024;

		public static int For(int shrineId, int useIndex)
		{
			return 1302000000 + (shrineId - 1300000000) * 1024 + (useIndex & 0x3FF);
		}
	}
	public class NukePickup : MonoBehaviour, Interactable, SharedObject
	{
		internal static readonly Vector3 ShrineOffset = new Vector3(0f, 0f, 3f);

		private const float ArmSeconds = 1f;

		private const float RetrySeconds = 2f;

		private const float HoverHeight = 1.2f;

		private const float BlinkSeconds = 5f;

		private const float ClientGraceSeconds = 2f;

		private const float ModelScale = 1.2f;

		private int id;

		private bool taken;

		private float spawnedAt;

		private float lifetime;

		private float nextSend;

		private Vector3 home;

		private Transform model;

		private Renderer modelRenderer;

		private Light glow;

		internal static IEnumerator SpawnAfter(float seconds, int id, Vector3 position)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			int scene = ((Scene)(ref activeScene)).handle;
			yield return (object)new WaitForSeconds(seconds);
			activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).handle == scene)
			{
				Spawn(id, position);
			}
		}

		private static void Spawn(int id, Vector3 spot)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			ResourceManager instance = ResourceManager.Instance;
			if (!((Object)(object)instance == (Object)null) && instance.list != null && !instance.list.ContainsKey(id))
			{
				Vector3 val = Hover(spot);
				GameObject val2 = new GameObject("ShrineNuke");
				val2.transform.position = val;
				NukePickup nukePickup = val2.AddComponent<NukePickup>();
				nukePickup.SetId(id);
				nukePickup.home = val;
				nukePickup.spawnedAt = Time.time;
				nukePickup.lifetime = ShrinePlugin.Cfg.NukeLifetime.Value + (LocalClient.serverOwner ? 0f : 2f);
				nukePickup.BuildLook();
				instance.AddObject(id, val2);
				if (ShrinePlugin.Cfg.VerboseLogging.Value)
				{
					ShrinePlugin.Log.LogInfo((object)$"Nuke {id} spawned at {val}.");
				}
			}
		}

		public void Interact()
		{
		}

		public void LocalExecute()
		{
		}

		public void ServerExecute(int fromClient = -1)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (LocalClient.serverOwner)
			{
				if (ShrinePlugin.Cfg.VerboseLogging.Value)
				{
					ShrinePlugin.Log.LogInfo((object)$"Nuke {id} set off by client {fromClient}.");
				}
				((MonoBehaviour)ShrinePlugin.Instance).StartCoroutine(NukeBlast.KillWave(((Component)this).transform.position));
			}
		}

		public void AllExecute()
		{
			if (!taken)
			{
				taken = true;
				NukeBlast.Play();
				((MonoBehaviour)ShrinePlugin.Instance).StartCoroutine(NukeBlast.PayAfter(ShrinePlugin.Cfg.NukeKillDelay.Value + ShrinePlugin.Cfg.NukeKillSpread.Value, ShrinePlugin.Cfg.NukeGold.Value));
				Remove();
			}
		}

		public void RemoveObject()
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		public string GetName()
		{
			return "Nuke";
		}

		public bool IsStarted()
		{
			return taken;
		}

		public void SetId(int id)
		{
			this.id = id;
		}

		public int GetId()
		{
			return id;
		}

		private void Update()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			float num = Time.time - spawnedAt;
			((Component)this).transform.position = home + Vector3.up * (Mathf.Sin(num * 2f) * 0.15f);
			if ((Object)(object)model != (Object)null)
			{
				model.localRotation = Quaternion.Euler(0f, num * 90f, 0f);
			}
			float num2 = lifetime - num;
			bool enabled = num2 > 5f || Mathf.Repeat(num2, 0.3f) > 0.15f;
			if ((Object)(object)modelRenderer != (Object)null)
			{
				modelRenderer.enabled = enabled;
			}
			if ((Object)(object)glow != (Object)null)
			{
				((Behaviour)glow).enabled = enabled;
			}
			if (num2 <= 0f)
			{
				Remove();
			}
			else if (!taken && num >= 1f && Time.time >= nextSend && Touching())
			{
				nextSend = Time.time + 2f;
				ClientSend.Interact(id);
			}
		}

		private bool Touching()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			PlayerMovement instance = PlayerMovement.Instance;
			if ((Object)(object)instance == (Object)null || instance.IsDead())
			{
				return false;
			}
			Vector3 val = ((Component)instance).transform.position - ((Component)this).transform.position;
			return NukeReach.Touches(val.x, val.y, val.z);
		}

		private static Vector3 Hover(Vector3 spot)
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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)
			int mask = LayerMask.GetMask(new string[2] { "Ground", "Terrain" });
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(spot + Vector3.up * 50f, Vector3.down, ref val, 200f, mask))
			{
				return ((RaycastHit)(ref val)).point + Vector3.up * 1.2f;
			}
			return spot + Vector3.up * 1.2f;
		}

		private void Remove()
		{
			ResourceManager instance = ResourceManager.Instance;
			if ((Object)(object)instance != (Object)null && instance.list != null && instance.list.TryGetValue(id, out var value) && (Object)(object)value == (Object)(object)((Component)this).gameObject)
			{
				instance.RemoveItem(id);
			}
			else
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private void BuildLook()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Glow");
			val.transform.SetParent(((Component)this).transform, false);
			glow = val.AddComponent<Light>();
			glow.type = (LightType)2;
			glow.color = ShrineNotices.NukeGreen;
			glow.range = 6f;
			glow.intensity = 2f;
			if (NukeArt.TryGet(out var pickupMesh, out var pickupMaterial))
			{
				GameObject val2 = new GameObject("Model");
				val2.transform.SetParent(((Component)this).transform, false);
				val2.transform.localScale = Vector3.one * 1.2f;
				val2.AddComponent<MeshFilter>().sharedMesh = pickupMesh;
				modelRenderer = (Renderer)(object)val2.AddComponent<MeshRenderer>();
				modelRenderer.sharedMaterial = pickupMaterial;
				model = val2.transform;
			}
		}
	}
	internal static class NukeReach
	{
		public const float Radius = 2f;

		public const float Height = 3f;

		public static bool Touches(float dx, float dy, float dz)
		{
			if (dx * dx + dz * dz <= 4f)
			{
				return Math.Abs(dy) <= 3f;
			}
			return false;
		}
	}
	internal static class NukeRules
	{
		public static bool Kills(bool neutral, bool bossType, bool mobTypeBoss)
		{
			if (!neutral && !bossType)
			{
				return !mobTypeBoss;
			}
			return false;
		}
	}
	internal static class NukeSound
	{
		private static readonly string[] Preferred = new string[2] { "RockImpactLoud", "FireImpact" };

		private const float Pitch = 0.6f;

		private const float Volume = 1f;

		private static AudioClip clip;

		private static bool warned;

		public static int Pick(IList<string> names)
		{
			string[] preferred = Preferred;
			foreach (string text in preferred)
			{
				for (int j = 0; j < names.Count; j++)
				{
					if (names[j] == text)
					{
						return j;
					}
				}
			}
			return -1;
		}

		public static void Play()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			if ((Object)(object)clip == (Object)null)
			{
				clip = Find();
			}
			if ((Object)(object)clip == (Object)null)
			{
				if (!warned)
				{
					ShrinePlugin.Log.LogWarning((object)"Nuke sound: no RockImpactLoud or FireImpact clip is loaded, so the blast is silent.");
				}
				warned = true;
				return;
			}
			GameObject val = new GameObject("ShrineNukeSound");
			AudioSource obj = val.AddComponent<AudioSource>();
			obj.spatialBlend = 0f;
			obj.clip = clip;
			obj.pitch = 0.6f;
			obj.volume = 1f;
			obj.Play();
			Object.Destroy((Object)val, clip.length / 0.6f + 0.5f);
			if (ShrinePlugin.Cfg.VerboseLogging.Value)
			{
				ShrinePlugin.Log.LogInfo((object)("Nuke sound '" + ((Object)clip).name + "' played."));
			}
		}

		private static AudioClip Find()
		{
			AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>();
			string[] array2 = new string[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = (((Object)(object)array[i] == (Object)null) ? null : ((Object)array[i]).name);
			}
			int num = Pick(array2);
			if (num >= 0)
			{
				return array[num];
			}
			return null;
		}
	}
	internal static class NukeStagger
	{
		public static float[] Delays(float[] distances, float delay, float spread)
		{
			int num = distances.Length;
			int[] array = (from i in Enumerable.Range(0, num)
				orderby distances[i]
				select i).ToArray();
			float[] array2 = new float[num];
			for (int num2 = 0; num2 < num; num2++)
			{
				array2[array[num2]] = ((num == 1) ? delay : (delay + spread * (float)num2 / (float)(num - 1)));
			}
			return array2;
		}
	}
	internal static class OddsLineLayout
	{
		public const float ReadScale = 2.25f;

		public const float MaxWidthMeters = 5.4f;

		public const float PlateWidth = 5.625f;

		public const float PlateHeight = 0.63f;

		public const float ReelsRootY = 4.4f;

		public const float BackingHeight = 1.3f;

		private const float PlateBottomLocal = 0.69f;

		public const float PlateCenterY = 1.005f;

		public const float PlateZ = 0.04f;

		public const float TextZ = -0.02f;

		private const float UnscaledMetersPerSize = 6.666667f;

		private const float AdvanceOverCap = 0.698324f;

		public static float PlateBottomY()
		{
			return 5.09f;
		}

		public static float CharacterSize(int characters)
		{
			if (characters < 1)
			{
				characters = 1;
			}
			float num = (float)characters * 0.698324f;
			return 5.4f / num / 6.666667f;
		}

		public static float CapMeters(float characterSize)
		{
			return characterSize * 6.666667f;
		}

		public static float WorldWidth(float characterSize, int characters)
		{
			return CapMeters(characterSize) * (float)characters * 0.698324f;
		}
	}
	internal static class OddsPopupLayout
	{
		public const float Gap = 12f;

		public const float Cell = 185.6f;

		private const float ScreenMargin = 0.96f;

		public static float Width(int tiers)
		{
			return (float)tiers * 185.6f + (float)(tiers + 1) * 12f;
		}

		public static float CellFor(int tiers, float screenWidth1080)
		{
			float num = screenWidth1080 * 0.96f;
			if (Width(tiers) <= num)
			{
				return 185.6f;
			}
			return (num - (float)(tiers + 1) * 12f) / (float)tiers;
		}

		public static float WidthFor(int tiers, float screenWidth1080)
		{
			return (float)tiers * CellFor(tiers, screenWidth1080) + (float)(tiers + 1) * 12f;
		}
	}
	internal static class OddsSampler
	{
		public const int Stream = 3145728;

		public const int Attempts = 128;

		private const int TierCount = 5;

		public static ShrineOdds Draw(int seed, int shrineId, int useIndex, double min, double max)
		{
			TryDraw(seed, shrineId, useIndex, min, max, out var odds);
			return odds;
		}

		internal static bool TryDraw(int seed, int shrineId, int useIndex, double min, double max, out ShrineOdds odds)
		{
			double num = 1.0 - 5.0 * min;
			if (max - min < 1E-12 || num <= 0.0)
			{
				odds = Equal(0.2);
				return false;
			}
			for (int i = 0; i < 128; i++)
			{
				double[] array = Dirichlet(seed, shrineId, useIndex, i);
				double num2 = min + num * array[0];
				double num3 = min + num * array[1];
				double num4 = min + num * array[2];
				double num5 = min + num * array[3];
				double num6 = min + num * array[4];
				if (num2 <= max && num3 <= max && num4 <= max && num5 <= max && num6 <= max)
				{
					odds = ShrineOdds.Exact(num2, num3, num4, num5, num6);
					return true;
				}
			}
			odds = Equal(0.2);
			return false;
		}

		private static ShrineOdds Equal(double share)
		{
			return ShrineOdds.Exact(share, share, share, share, share);
		}

		private static double[] Dirichlet(int seed, int shrineId, int useIndex, int attempt)
		{
			double[] array = new double[5];
			double num = 0.0;
			for (int i = 0; i < 5; i++)
			{
				int c = 3145728 + useIndex * 640 + attempt * 5 + i;
				double num2 = Hash01.Of(seed, shrineId, c);
				if (num2 < 1E-12)
				{
					num2 = 1E-12;
				}
				num += (array[i] = 0.0 - Math.Log(num2));
			}
			for (int j = 0; j < 5; j++)
			{
				array[j] /= num;
			}
			return array;
		}
	}
	internal readonly struct Vec3
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public Vec3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public static Vec3 operator -(Vec3 a, Vec3 b)
		{
			return new Vec3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}
	}
	internal readonly struct MaterialSpec
	{
		public readonly float R;

		public readonly float G;

		public readonly float B;

		public readonly float Emission;

		public MaterialSpec(float r, float g, float b, float emission)
		{
			R = r;
			G = g;
			B = b;
			Emission = emission;
		}
	}
	internal sealed class SubMesh
	{
		public readonly string Material;

		public readonly int[] Triangles;

		public SubMesh(string material, int[] triangles)
		{
			Material = material;
			Triangles = triangles;
		}
	}
	internal sealed class PickupModel
	{
		private sealed class ObjObject
		{
			public readonly List<Vec3> Positions = new List<Vec3>();

			public readonly List<Vec3> Normals = new List<Vec3>();

			public readonly List<string> MaterialOrder = new List<string>();

			public readonly Dictionary<string, List<int>> Triangles = new Dictionary<string, List<int>>();
		}

		public readonly string Name;

		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly IReadOnlyList<SubMesh> SubMeshes;

		public readonly IReadOnlyDictionary<string, MaterialSpec> Materials;

		public int TriangleCount => Positions.Length / 3;

		public PickupModel(string name, Vec3[] positions, Vec3[] normals, IReadOnlyList<SubMesh> subMeshes, IReadOnlyDictionary<string, MaterialSpec> materials)
		{
			Name = name;
			Positions = positions;
			Normals = normals;
			SubMeshes = subMeshes;
			Materials = materials;
		}

		public static PickupModel Parse(string objText, string json)
		{
			if (!(JsonReader.Parse(json) is Dictionary<string, object> obj))
			{
				throw new FormatException("pickup json: top level is not an object.");
			}
			string text = Str(obj, "name");
			Dictionary<string, MaterialSpec> dictionary = ReadMaterials(Obj(obj, "materials"));
			Dictionary<string, ObjObject> dictionary2 = ReadObj(objText, text);
			if (dictionary2.Count != 1)
			{
				throw new FormatException($"{text}: a pickup OBJ needs exactly one object, it has {dictionary2.Count}.");
			}
			ObjObject objObject = null;
			foreach (ObjObject value in dictionary2.Values)
			{
				objObject = value;
			}
			List<SubMesh> list = new List<SubMesh>();
			foreach (string item in objObject.MaterialOrder)
			{
				if (!dictionary.ContainsKey(item))
				{
					throw new FormatException(text + ": uses undefined material '" + item + "'.");
				}
				list.Add(new SubMesh(item, objObject.Triangles[item].ToArray()));
			}
			PickupModel pickupModel = new PickupModel(text, objObject.Positions.ToArray(), objObject.Normals.ToArray(), list, dictionary);
			int num = (int)Num(obj, "tris");
			if (pickupModel.TriangleCount != num)
			{
				throw new FormatException($"{text}: OBJ has {pickupModel.TriangleCount} triangles, the json says {num}.");
			}
			return pickupModel;
		}

		private static Dictionary<string, ObjObject> ReadObj(string text, string name)
		{
			if (text == null)
			{
				throw new FormatException(name + ": OBJ text is null.");
			}
			List<Vec3> list = new List<Vec3>();
			List<Vec3> list2 = new List<Vec3>();
			Dictionary<string, ObjObject> dictionary = new Dictionary<string, ObjObject>();
			ObjObject objObject = null;
			string text2 = null;
			string[] array = text.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length == 0)
				{
					continue;
				}
				switch (array2[0])
				{
				case "v":
				{
					Vec3 vec2 = ReadVec(array2, name, i);
					list.Add(new Vec3(0f - vec2.X, vec2.Y, vec2.Z));
					break;
				}
				case "vn":
				{
					Vec3 vec = ReadVec(array2, name, i);
					list2.Add(new Vec3(0f - vec.X, vec.Y, vec.Z));
					break;
				}
				case "o":
				{
					string text3 = string.Join(" ", array2, 1, array2.Length - 1);
					if (dictionary.ContainsKey(text3))
					{
						throw new FormatException(name + ": OBJ object " + text3 + " appears twice.");
					}
					objObject = new ObjObject();
					dictionary.Add(text3, objObject);
					text2 = null;
					break;
				}
				case "usemtl":
					text2 = ((array2.Length > 1) ? array2[1] : null);
					break;
				case "f":
					if (objObject == null || text2 == null)
					{
						throw new FormatException($"{name}: OBJ line {i + 1} has a face outside an object or material.");
					}
					if (array2.Length != 4)
					{
						throw new FormatException($"{name}: OBJ line {i + 1} is not a triangle.");
					}
					AddTriangle(objObject, text2, list, list2, array2, name, i);
					break;
				}
			}
			return dictionary;
		}

		private static void AddTriangle(ObjObject obj, string material, List<Vec3> vertices, List<Vec3> normals, string[] tokens, string name, int line)
		{
			if (!obj.Triangles.TryGetValue(material, out var value))
			{
				value = new List<int>();
				obj.Triangles.Add(material, value);
				obj.MaterialOrder.Add(material);
			}
			int[] array = new int[3] { 1, 3, 2 };
			foreach (int num in array)
			{
				string[] array2 = tokens[num].Split(new char[1] { '/' });
				Vec3 item = vertices[Index(array2[0], vertices.Count, name, line)];
				if (array2.Length < 3 || array2[2].Length == 0)
				{
					throw new FormatException($"{name}: OBJ line {line + 1} has a corner without a normal.");
				}
				value.Add(obj.Positions.Count);
				obj.Positions.Add(item);
				obj.Normals.Add(normals[Index(array2[2], normals.Count, name, line)]);
			}
		}

		private static int Index(string raw, int count, string name, int line)
		{
			if (!int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result == 0)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} has a bad index '{raw}'.");
			}
			int num = ((result > 0) ? (result - 1) : (count + result));
			if (num < 0 || num >= count)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} index {raw} is out of range.");
			}
			return num;
		}

		private static Vec3 ReadVec(string[] tokens, string name, int line)
		{
			if (tokens.Length < 4)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} needs three numbers.");
			}
			return new Vec3(Float(tokens[1], name, line), Float(tokens[2], name, line), Float(tokens[3], name, line));
		}

		private static float Float(string raw, string name, int line)
		{
			if (!float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				throw new FormatException($"{name}: OBJ line {line + 1} has a bad number '{raw}'.");
			}
			return result;
		}

		private static Dictionary<string, MaterialSpec> ReadMaterials(Dictionary<string, object> raw)
		{
			Dictionary<string, MaterialSpec> dictionary = new Dictionary<string, MaterialSpec>();
			foreach (KeyValuePair<string, object> item in raw)
			{
				if (!(item.Value is Dictionary<string, object> obj))
				{
					throw new FormatException("pickup json: material " + item.Key + " is not an object.");
				}
				Vec3 vec = ReadVec3(obj, "rgb");
				dictionary.Add(item.Key, new MaterialSpec(vec.X, vec.Y, vec.Z, (float)Num(obj, "emission")));
			}
			return dictionary;
		}

		private static Vec3 ReadVec3(Dictionary<string, object> obj, string key)
		{
			List<object> list = Arr(obj, key);
			if (list.Count != 3 || !(list[0] is double num) || !(list[1] is double num2) || !(list[2] is double num3))
			{
				throw new FormatException("pickup json: '" + key + "' is not three numbers.");
			}
			return new Vec3((float)num, (float)num2, (float)num3);
		}

		private static object Field(Dictionary<string, object> obj, string key)
		{
			if (!obj.TryGetValue(key, out var value))
			{
				throw new FormatException("pickup json: missing '" + key + "'.");
			}
			return value;
		}

		private static string Str(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as string) ?? throw new FormatException("pickup json: '" + key + "' is not a string.");
		}

		private static double Num(Dictionary<string, object> obj, string key)
		{
			object obj2 = Field(obj, key);
			if (obj2 is double)
			{
				return (double)obj2;
			}
			throw new FormatException("pickup json: '" + key + "' is not a number.");
		}

		private static List<object> Arr(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as List<object>) ?? throw new FormatException("pickup json: '" + key + "' is not an array.");
		}

		private static Dictionary<string, object> Obj(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as Dictionary<string, object>) ?? throw new FormatException("pickup json: '" + key + "' is not an object.");
		}
	}
	internal sealed class PickupMesh
	{
		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly float[] U;

		public readonly float[] R;

		public readonly float[] G;

		public readonly float[] B;

		public readonly int[] Triangles;

		public readonly IReadOnlyList<MaterialSpec> Palette;

		private PickupMesh(Vec3[] positions, Vec3[] normals, float[] u, float[] r, float[] g, float[] b, int[] triangles, IReadOnlyList<MaterialSpec> palette)
		{
			Positions = positions;
			Normals = normals;
			U = u;
			R = r;
			G = g;
			B = b;
			Triangles = triangles;
			Palette = palette;
		}

		public static PickupMesh From(PickupModel model)
		{
			Vec3 vec = new Vec3(float.MaxValue, float.MaxValue, float.MaxValue);
			Vec3 vec2 = new Vec3(float.MinValue, float.MinValue, float.MinValue);
			Vec3[] positions = model.Positions;
			for (int i = 0; i < positions.Length; i++)
			{
				Vec3 vec3 = positions[i];
				vec = new Vec3(Math.Min(vec.X, vec3.X), Math.Min(vec.Y, vec3.Y), Math.Min(vec.Z, vec3.Z));
				vec2 = new Vec3(Math.Max(vec2.X, vec3.X), Math.Max(vec2.Y, vec3.Y), Math.Max(vec2.Z, vec3.Z));
			}
			Vec3 vec4 = new Vec3((vec.X + vec2.X) / 2f, (vec.Y + vec2.Y) / 2f, (vec.Z + vec2.Z) / 2f);
			int count = model.SubMeshes.Count;
			List<Vec3> list = new List<Vec3>(model.Positions.Length);
			List<Vec3> list2 = new List<Vec3>(model.Positions.Length);
			List<float> list3 = new List<float>(model.Positions.Length);
			List<float> list4 = new List<float>(model.Positions.Length);
			List<float> list5 = new List<float>(model.Positions.Length);
			List<float> list6 = new List<float>(model.Positions.Length);
			List<MaterialSpec> list7 = new List<MaterialSpec>(count);
			for (int j = 0; j < count; j++)
			{
				SubMesh subMesh = model.SubMeshes[j];
				MaterialSpec item = model.Materials[subMesh.Material];
				list7.Add(item);
				float item2 = ((float)j + 0.5f) / (float)count;
				int[] triangles = subMesh.Triangles;
				foreach (int num in triangles)
				{
					list.Add(model.Positions[num] - vec4);
					list2.Add(model.Normals[num]);
					list3.Add(item2);
					list4.Add(item.R);
					list5.Add(item.G);
					list6.Add(item.B);
				}
			}
			int[] array = new int[list.Count];
			for (int k = 0; k < array.Length; k++)
			{
				array[k] = k;
			}
			return new PickupMesh(list.ToArray(), list2.ToArray(), list3.ToArray(), list4.ToArray(), list5.ToArray(), list6.ToArray(), array, list7);
		}
	}
	internal sealed class JsonReader
	{
		private readonly string text;

		private int pos;

		private JsonReader(string text)
		{
			this.text = text;
		}

		public static object Parse(string text)
		{
			if (text == null)
			{
				throw new FormatException("JSON text is null.");
			}
			JsonReader jsonReader = new JsonReader(text);
			object result = jsonReader.ReadValue();
			jsonReader.SkipWhitespace();
			if (jsonReader.pos != text.Length)
			{
				throw jsonReader.Error("trailing characters");
			}
			return result;
		}

		private object ReadValue()
		{
			SkipWhitespace();
			if (pos >= text.Length)
			{
				throw Error("unexpected end");
			}
			char c = text[pos];
			switch (c)
			{
			case '{':
				return ReadObject();
			case '[':
				return ReadArray();
			case '"':
				return ReadString();
			case 't':
				Expect("true");
				return true;
			case 'f':
				Expect("false");
				return false;
			case 'n':
				Expect("null");
				return null;
			default:
				switch (c)
				{
				case '-':
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					return ReadNumber();
				default:
					throw Error($"unexpected '{c}'");
				}
			}
		}

		private Dictionary<string, object> ReadObject()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			pos++;
			SkipWhitespace();
			if (Peek() == '}')
			{
				pos++;
				return dictionary;
			}
			while (true)
			{
				SkipWhitespace();
				if (Peek() != '"')
				{
					throw Error("expected a key");
				}
				string key = ReadString();
				SkipWhitespace();
				if (Peek() != ':')
				{
					break;
				}
				pos++;
				dictionary[key] = ReadValue();
				SkipWhitespace();
				char c = Peek();
				pos++;
				switch (c)
				{
				case ',':
					break;
				case '}':
					return dictionary;
				default:
					throw Error("expected ',' or '}'");
				}
			}
			throw Error("expected ':'");
		}

		private List<object> ReadArray()
		{
			List<object> list = new List<object>();
			pos++;
			SkipWhitespace();
			if (Peek() == ']')
			{
				pos++;
				return list;
			}
			while (true)
			{
				list.Add(ReadValue());
				SkipWhitespace();
				char c = Peek();
				pos++;
				switch (c)
				{
				case ',':
					break;
				case ']':
					return list;
				default:
					throw Error("expected ',' or ']'");
				}
			}
		}

		private string ReadString()
		{
			pos++;
			StringBuilder stringBuilder = new StringBuilder();
			while (pos < text.Length)
			{
				char c = text[pos++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					break;
				case '\\':
				{
					if (pos >= text.Length)
					{
						throw Error("unterminated escape");
					}
					char c2 = text[pos++];
					switch (c2)
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (pos + 4 > text.Length)
						{
							throw Error("short \\u escape");
						}
						stringBuilder.Append((char)int.Parse(text.Substring(pos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture));
						pos += 4;
						break;
					default:
						throw Error($"bad escape '\\{c2}'");
					}
					break;
				}
				}
			}
			throw Error("unterminated string");
		}

		private double ReadNumber()
		{
			int num = pos;
			while (pos < this.text.Length && "+-0123456789.eE".IndexOf(this.text[pos]) >= 0)
			{
				pos++;
			}
			string text = this.text.Substring(num, pos - num);
			if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				throw Error("bad number '" + text + "'");
			}
			return result;
		}

		private void Expect(string word)
		{
			if (string.CompareOrdinal(text, pos, word, 0, word.Length) != 0)
			{
				throw Error("expected '" + word + "'");
			}
			pos += word.Length;
		}

		private char Peek()
		{
			if (pos >= text.Length)
			{
				return '\0';
			}
			return text[pos];
		}

		private void SkipWhitespace()
		{
			while (pos < text.Length && char.IsWhiteSpace(text[pos]))
			{
				pos++;
			}
		}

		private FormatException Error(string what)
		{
			return new FormatException($"JSON: {what} at offset {pos}.");
		}
	}
	internal static class PlayerNames
	{
		public static string Of(int clientId)
		{
			Dictionary<int, PlayerManager> players = GameManager.players;
			if (players != null && players.TryGetValue(clientId, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.username))
			{
				return value.username;
			}
			return "Someone";
		}
	}
	internal static class ReelGlyphLayout
	{
		public const int FontSize = 64;

		public const float FaceWidth = 0.7f;

		public const float FaceHeight = 0.9f;

		private const float MeasuredCharacterSize = 0.005f;

		private const float MeasuredCapMeters = 0.03f;

		private const float TargetCapMeters = 0.36f;

		private const float MaxWidthMeters = 0.64f;

		private const float CapUnits = 716f;

		public static float CapMeters(float characterSize)
		{
			return characterSize * 6f;
		}

		public static float CharacterSize(ReelSymbol symbol)
		{
			return CharacterSizeFor(AdvanceUnits(symbol));
		}

		public static float CharacterSize(string face)
		{
			return CharacterSizeFor(722f * (float)(string.IsNullOrEmpty(face) ? 1 : face.Length));
		}

		private static float CharacterSizeFor(float advanceUnits)
		{
			float num = WidthOverCap(advanceUnits);
			float num2 = 0.060000002f;
			float num3 = 0.36f * num;
			if (num3 > 0.64f)
			{
				num2 *= 0.64f / num3;
			}
			return num2;
		}

		public static float WorldWidth(float characterSize, ReelSymbol symbol)
		{
			return CapMeters(characterSize) * WidthOverCap(AdvanceUnits(symbol));
		}

		private static float WidthOverCap(float advanceUnits)
		{
			return advanceUnits / 716f * (7f / 9f);
		}

		private static float AdvanceUnits(ReelSymbol symbol)
		{
			return symbol switch
			{
				ReelSymbol.Bar => 2056f, 
				ReelSymbol.Skull => 667f, 
				ReelSymbol.Nuke => 722f, 
				_ => 556f, 
			};
		}
	}
	internal enum ReelSymbol
	{
		Lemon,
		Cherry,
		Bar,
		Seven,
		Skull,
		Nuke
	}
	internal static class ReelSymbols
	{
		public const int Count = 5;

		public static ReelSymbol ForWin(ShrineOutcome outcome)
		{
			switch (outcome)
			{
			case ShrineOutcome.White:
				return ReelSymbol.Cherry;
			case ShrineOutcome.Blue:
				return ReelSymbol.Bar;
			case ShrineOutcome.Orange:
				return ReelSymbol.Seven;
			case ShrineOutcome.Boss:
				return ReelSymbol.Skull;
			case ShrineOutcome.Nuke:
				return ReelSymbol.Nuke;
			default:
				if (!ShrineOutcomes.IsRegistered(outcome))
				{
					return ReelSymbol.Lemon;
				}
				return (ReelSymbol)outcome;
			}
		}

		public static ReelSymbol[] For(ShrineOutcome outcome, double r0, double r1, double r2)
		{
			if (outcome != ShrineOutcome.Nothing)
			{
				ReelSymbol reelSymbol = ForWin(outcome);
				return new ReelSymbol[3] { reelSymbol, reelSymbol, reelSymbol };
			}
			ReelSymbol[] array = new ReelSymbol[3]
			{
				(ReelSymbol)ShrineItemPick.Index(r0, 5),
				(ReelSymbol)ShrineItemPick.Index(r1, 5),
				(ReelSymbol)ShrineItemPick.Index(r2, 5)
			};
			if (array[0] == array[1] && array[1] == array[2])
			{
				array[2] = (ReelSymbol)((int)(array[2] + 1) % 5);
			}
			return array;
		}
	}
	internal static class ReelTimeline
	{
		public const float SpinSeconds = 2.4f;

		private const float FacesPerSecond = 12f;

		public static float StopTime(int reel)
		{
			if (reel <= 0)
			{
				return 1.2f;
			}
			if (reel == 1)
			{
				return 1.8f;
			}
			return 2.4f;
		}

		public static bool Settled(float t)
		{
			return t >= 2.4f;
		}

		public static ReelSymbol Face(float t, int reel, ReelSymbol final)
		{
			if (t < StopTime(reel))
			{
				return (ReelSymbol)(((int)(t * 12f) + reel * 2) % 5);
			}
			return final;
		}
	}
	internal static class ShrineBounds
	{
		public const float DefaultMinPercent = 1f;

		public const float DefaultMaxPercent = 50f;

		public static bool Ok(float minPercent, float maxPercent)
		{
			if (float.IsNaN(minPercent) || float.IsNaN(maxPercent))
			{
				return false;
			}
			if (float.IsInfinity(minPercent) || float.IsInfinity(maxPercent))
			{
				return false;
			}
			if (minPercent < 0f || maxPercent > 100f || minPercent > maxPercent)
			{
				return false;
			}
			double num = minPercent;
			double num2 = maxPercent;
			if (5.0 * num <= 100.0)
			{
				return 100.0 <= 5.0 * num2;
			}
			return false;
		}

		public static void Fractions(float minPercent, float maxPercent, out double min, out double max)
		{
			if (!Ok(minPercent, maxPercent))
			{
				min = 0.01;
				max = 0.5;
			}
			else
			{
				min = (double)minPercent / 100.0;
				max = (double)maxPercent / 100.0;
			}
		}
	}
	internal enum ForcedOutcome
	{
		Off,
		Nothing,
		White,
		Blue,
		Orange,
		Boss,
		Nuke
	}
	internal sealed class ShrineConfig
	{
		public readonly ConfigEntry<int> Count;

		public readonly ConfigEntry<float> MinSpacing;

		public readonly ConfigEntry<int> BasePrice;

		public readonly ConfigEntry<float> PriceGrowth;

		public readonly ConfigEntry<bool> ScaleWithChestPrice;

		public readonly ConfigEntry<int> MaxSuccesses;

		public readonly ConfigEntry<bool> RandomizeOdds;

		public readonly ConfigEntry<float> MinChance;

		public readonly ConfigEntry<float> MaxChance;

		public readonly ConfigEntry<float> ChanceNothing;

		public readonly ConfigEntry<float> ChanceWhite;

		public readonly ConfigEntry<float> ChanceBlue;

		public readonly ConfigEntry<float> ChanceOrange;

		public readonly ConfigEntry<float> ChanceBoss;

		public readonly ConfigEntry<ForcedOutcome> ForceOutcome;

		public readonly ConfigEntry<bool> NukeEnabled;

		public readonly ConfigEntry<float> NukeChance;

		public readonly ConfigEntry<int> NukeGold;

		public readonly ConfigEntry<float> NukeKillDelay;

		public readonly ConfigEntry<float> NukeKillSpread;

		public readonly ConfigEntry<float> NukeLifetime;

		public readonly ConfigEntry<bool> VerboseLogging;

		private double warnedSum = double.NaN;

		private bool warnedBounds;

		public ShrineConfig(ConfigFile cfg)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Expected O, but got Unknown
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Expected O, but got Unknown
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Expected O, but got Unknown
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Expected O, but got Unknown
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Expected O, but got Unknown
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Expected O, but got Unknown
			//IL_0390: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Expected O, but got Unknown
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Expected O, but got Unknown
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Expected O, but got Unknown
			Count = cfg.Bind<int>("World", "Count", 6, new ConfigDescription("How many Shrines of Chance to place on grass. Not a wiki number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), Array.Empty<object>()));
			MinSpacing = cfg.Bind<float>("World", "MinSpacing", 60f, new ConfigDescription("Minimum distance in meters between shrines. Not a wiki number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			BasePrice = cfg.Bind<int>("Price", "BasePrice", 17, new ConfigDescription("Coins charged on the first use. Risk of Rain 2 wiki: $17, mapped 1:1 onto Muck coins.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), Array.Empty<object>()));
			PriceGrowth = cfg.Bind<float>("Price", "PriceGrowth", 1.4f, new ConfigDescription("Price multiplier applied for each earlier use. Risk of Rain 2 wiki: 1.4. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			ScaleWithChestPrice = cfg.Bind<bool>("Price", "ScaleWithChestPrice", true, "Multiply the price by GameManager.ChestPriceMultiplier for the current day. Not a wiki number. Every player must use the same value.");
			MaxSuccesses = cfg.Bind<int>("Odds", "MaxSuccesses", 2, new ConfigDescription("Successful item drops before the shrine is spent. Risk of Rain 2 wiki: 2. Nothing and a boss do not count.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			RandomizeOdds = cfg.Bind<bool>("Odds", "RandomizeOdds", true, "Each paid roll draws a fresh set of odds. Five tiers, each between MinChance and MaxChance, summing to 100. Turn this off to use the Chance values below.");
			MinChance = cfg.Bind<float>("Odds", "MinChance", 1f, new ConfigDescription("Smallest percent any tier can roll when RandomizeOdds is on. Five times this must be at most 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MaxChance = cfg.Bind<float>("Odds", "MaxChance", 50f, new ConfigDescription("Largest percent any tier can roll when RandomizeOdds is on. Five times this must be at least 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ChanceNothing = cfg.Bind<float>("Odds", "ChanceNothing", 42.73f, new ConfigDescription("Percent chance of nothing, used only when RandomizeOdds is off. The five Chance values are normalised to 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ChanceWhite = cfg.Bind<float>("Odds", "ChanceWhite", 34.18f, new ConfigDescription("Percent chance of a White powerup, used only when RandomizeOdds is off. The five Chance values are normalised to 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ChanceBlue = cfg.Bind<float>("Odds", "ChanceBlue", 17.09f, new ConfigDescription("Percent chance of a Blue powerup, used only when RandomizeOdds is off. The five Chance values are normalised to 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ChanceOrange = cfg.Bind<float>("Odds", "ChanceOrange", 5f, new ConfigDescription("Percent chance of an Orange powerup, used only when RandomizeOdds is off. The five Chance values are normalised to 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ChanceBoss = cfg.Bind<float>("Odds", "ChanceBoss", 1f, new ConfigDescription("Percent chance to summon a boss, used only when RandomizeOdds is off. A boss does not count as a successful drop. The five Chance values are normalised to 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			NukeEnabled = cfg.Bind<bool>("Nuke", "Enabled", true, "Three nukes drop a nuke pickup by the shrine, like the Call of Duty Zombies power-up. Touching it kills every enemy that is not a boss and pays every player. Every player must use the same value.");
			NukeChance = cfg.Bind<float>("Nuke", "Chance", 5f, new ConfigDescription("Percent of every paid roll that gives a nuke. The other five tiers share the rest. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			NukeGold = cfg.Bind<int>("Nuke", "Gold", 400, new ConfigDescription("Coins each player gets after a nuke, alive or dead. Call of Duty Zombies gives 400 points. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>()));
			NukeKillDelay = cfg.Bind<float>("Nuke", "KillDelay", 0.5f, new ConfigDescription("Seconds from the touch to the first death. The host's value runs the kills.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			NukeKillSpread = cfg.Bind<float>("Nuke", "KillSpread", 1.5f, new ConfigDescription("Seconds over which the deaths ripple out from the nearest enemy to the farthest. Coins arrive when it ends.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			NukeLifetime = cfg.Bind<float>("Nuke", "Lifetime", 30f, new ConfigDescription("Seconds an untouched nuke stays before it fades. It blinks for the last 5. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			ForceOutcome = cfg.Bind<ForcedOutcome>("Debug", "ForceOutcome", ForcedOutcome.Off, "Forces every roll to this outcome. Off uses random odds, or the Chance values when RandomizeOdds is off. Any other value is debug only, and it changes the config code.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each placement and each roll.");
		}

		public ShrineOdds Odds()
		{
			if (ForceOutcome.Value != ForcedOutcome.Off)
			{
				return ShrineOdds.Forced(AsOutcome(ForceOutcome.Value));
			}
			ShrineOdds shrineOdds = ShrineOdds.From(ChanceNothing.Value, ChanceWhite.Value, ChanceBlue.Value, ChanceOrange.Value, ChanceBoss.Value);
			if (shrineOdds.Normalised && shrineOdds.InputSum != warnedSum && ShrinePlugin.Log != null)
			{
				warnedSum = shrineOdds.InputSum;
				ShrinePlugin.Log.LogWarning((object)("Shrine of Chance odds sum to " + shrineOdds.InputSum.ToString("0.##") + ". Scaled to 100: Nothing " + Percent(shrineOdds, ShrineOutcome.Nothing) + "%, White " + Percent(shrineOdds, ShrineOutcome.White) + "%, Blue " + Percent(shrineOdds, ShrineOutcome.Blue) + "%, Orange " + Percent(shrineOdds, ShrineOutcome.Orange) + "%, Boss " + Percent(shrineOdds, ShrineOutcome.Boss) + "%."));
			}
			return shrineOdds;
		}

		public double NukeShare()
		{
			if (!NukeEnabled.Value)
			{
				return 0.0;
			}
			return (double)NukeChance.Value / 100.0;
		}

		public void ChanceBounds(out double min, out double max)
		{
			if (!ShrineBounds.Ok(MinChance.Value, MaxChance.Value) && !warnedBounds && ShrinePlugin.Log != null)
			{
				warnedBounds = true;
				ShrinePlugin.Log.LogWarning((object)("MinChance " + MinChance.Value.ToString("0.##") + " and MaxChance " + MaxChance.Value.ToString("0.##") + " cannot give five tiers that sum to 100. Using 1 and 50."));
			}
			ShrineBounds.Fractions(MinChance.Value, MaxChance.Value, out min, out max);
		}

		public string Code()
		{
			return ShrineConfigStamp.Code(ShrineConfigStamp.WithRegistered(ShrineConfigStamp.Describe(Count.Value, MinSpacing.Value, BasePrice.Value, PriceGrowth.Value, ScaleWithChestPrice.Value, MaxSuccesses.Value, ChanceNothing.Value, ChanceWhite.Value, ChanceBlue.Value, ChanceOrange.Value, ChanceBoss.Value, (int)ForceOutcome.Value, RandomizeOdds.Value, MinChance.Value, MaxChance.Value, NukeEnabled.Value, NukeChance.Value, NukeGold.Value, NukeLifetime.Value), ShrineOutcomeRegistry.Stamp()));
		}

		internal static ShrineOutcome AsOutcome(ForcedOutcome forced)
		{
			return forced switch
			{
				ForcedOutcome.White => ShrineOutcome.White, 
				ForcedOutcome.Blue => ShrineOutcome.Blue, 
				ForcedOutcome.Orange => ShrineOutcome.Orange, 
				ForcedOutcome.Boss => ShrineOutcome.Boss, 
				ForcedOutcome.Nuke => ShrineOutcome.Nuke, 
				_ => ShrineOutcome.Nothing, 
			};
		}

		private static string Percent(ShrineOdds odds, ShrineOutcome outcome)
		{
			return (odds[outcome] * 100.0).ToString("0.##");
		}
	}
	internal static class ShrineConfigStamp
	{
		public static string Describe(int count, float minSpacing, int basePrice, float growth, bool scaleWithChestPrice, int maxSuccesses, float nothing, float white, float blue, float orange, float boss, int forceOutcome, bool randomizeOdds, float minChance, float maxChance, bool nukeEnabled, float nukeChance, int nukeGold, float nukeLifetime)
		{
			return string.Join("|", count.ToString(), Milli(minSpacing), basePrice.ToString(), Milli(growth), scaleWithChestPrice ? "1" : "0", maxSuccesses.ToString(), Milli(nothing), Milli(white), Milli(blue), Milli(orange), Milli(boss), forceOutcome.ToString(), randomizeOdds ? "1" : "0", Milli(minChance), Milli(maxChance), nukeEnabled ? "1" : "0", Milli(nukeChance), nukeGold.ToString(), Milli(nukeLifetime));
		}

		public static string WithRegistered(string describe, string registered)
		{
			if (!string.IsNullOrEmpty(registered))
			{
				return describe + "|" + registered;
			}
			return describe;
		}

		public static string Code(string text)
		{
			uint num = 2166136261u;
			for (int i = 0; i < text.Length; i++)
			{
				num ^= text[i];
				num *= 16777619;
			}
			return num.ToString("X8");
		}

		private static string Milli(float value)
		{
			return ((int)Math.Round((double)value * 1000.0, MidpointRounding.AwayFromZero)).ToString();
		}
	}
	internal struct Charge
	{
		public bool Pay;

		public int Price;

		public ShrineOutcome Outcome;

		public int UseIndex;
	}
	internal struct HostStep
	{
		public bool Refused;

		public int UseIndex;

		public ShrineOutcome Outcome;
	}
	internal sealed class ShrineExchange
	{
		private readonly ShrineState shown;

		private readonly ShrineState host;

		private bool offering;

		private int offeredPrice;

		private int offeredUse;

		private ShrineOutcome offeredOutcome;

		public ShrineState Shown => shown;

		public int HostUseIndex => host.UseIndex;

		public bool HostSpent => host.Spent;

		public bool Offering => offering;

		public ShrineExchange(int maxSuccesses)
		{
			shown = new ShrineState(maxSuccesses);
			host = new ShrineState(maxSuccesses);
		}

		public bool TryOffer(int money, int price, ShrineOutcome outcome, int useIndex = 0)
		{
			if (shown.Spent || offering || money < price)
			{
				return false;
			}
			offering = true;
			offeredPrice = price;
			offeredOutcome = outcome;
			offeredUse = useIndex;
			return true;
		}

		public void CancelOffer()
		{
			offering = false;
		}

		public Charge TakeCharge()
		{
			if (!offering || shown.Spent)
			{
				offering = false;
				return default(Charge);
			}
			Charge result = new Charge
			{
				Pay = true,
				Price = offeredPrice,
				Outcome = offeredOutcome,
				UseIndex = offeredUse
			};
			offering = false;
			return result;
		}

		public HostStep HostResolve(ShrineOutcome outcome)
		{
			if (host.Spent)
			{
				return new HostStep
				{
					Refused = true,
					UseIndex = host.UseIndex,
					Outcome = ShrineOutcome.Nothing
				};
			}
			int useIndex = host.UseIndex;
			host.Record(outcome);
			return new HostStep
			{
				UseIndex = useIndex,
				Outcome = outcome
			};
		}

		public void Commit(ShrineOutcome outcome)
		{
			shown.Record(outcome);
		}
	}
	internal static class ShrineIds
	{
		public const int Base = 1300000000;

		public static int For(int attempt)
		{
			return 1300000000 + attempt;
		}

		public static bool IsShrine(int id)
		{
			if (id >= 1300000000)
			{
				return id < 1301000000;
			}
			return false;
		}
	}
	internal static class ShrineItemPick
	{
		public static int Index(double r, int length)
		{
			if (length <= 0)
			{
				return -1;
			}
			return Math.Min(length - 1, (int)(r * (double)length));
		}
	}
	public class ShrineNotices : MonoBehaviour
	{
		internal static readonly Color NukeGreen = new Color(0.35f, 1f, 0.2f);

		private static readonly string[] TierNames = new string[6] { "Nothing", "White", "Blue", "Legendary", "Boss", "Nuke" };

		private static readonly Color[] TierColors = (Color[])(object)new Color[6]
		{
			new Color(0.32f, 0.32f, 0.32f),
			new Color(0.93f, 0.93f, 0.93f),
			new Color(0.18f, 0.42f, 0.95f),
			new Color(1f, 0.52f, 0.08f),
			new Color(0.82f, 0.12f, 0.1f),
			NukeGreen
		};

		internal static ShrineNotices Instance;

		private Texture2D pixel;

		private GUIStyle labelStyle;

		private float oddsStart = -1f;

		private int[] oddsPct;

		private string[] oddsNames;

		private Color[] oddsColors;

		private float bannerStart = -1f;

		private string banner;

		private float flashStart = -1f;

		internal void ShowOdds(ShrineOdds odds)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			oddsPct = ShrineText.Percents(odds);
			ShrineOutcome[] array = ShrineText.ShownOutcomes(odds);
			oddsNames = new string[array.Length];
			oddsColors = (Color[])(object)new Color[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				ShrineOutcomeSpec shrineOutcomeSpec = odds.Spec(array[i]);
				oddsNames[i] = ((shrineOutcomeSpec != null) ? shrineOutcomeSpec.Label : TierNames[(int)array[i]]);
				oddsColors[i] = shrineOutcomeSpec?.Color ?? TierColors[(int)array[i]];
			}
			oddsStart = Time.time;
		}

		internal void Flash()
		{
			flashStart = Time.time;
		}

		internal void ShowBanner(string text)
		{
			banner = text;
			bannerStart = Time.time;
		}

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			pixel = new Texture2D(1, 1, (TextureFormat)4, false);
			pixel.SetPixel(0, 0, Color.white);
			pixel.Apply();
		}

		private void OnEnable()
		{
			SceneManager.activeSceneChanged += ClearOnSceneChange;
		}

		private void OnDisable()
		{
			SceneManager.activeSceneChanged -= ClearOnSceneChange;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			if ((Object)(object)pixel != (Object)null)
			{
				Object.Destroy((Object)(object)pixel);
			}
		}

		private void ClearOnSceneChange(Scene previous, Scene next)
		{
			oddsPct = null;
			oddsNames = null;
			oddsColors = null;
			oddsStart = -1f;
			banner = null;
			bannerStart = -1f;
			flashStart = -1f;
		}

		private void OnGUI()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)pixel == (Object)null) && Screen.height >= 1)
			{
				Color color = GUI.color;
				DrawOdds((float)Screen.height / 1080f);
				DrawBanner((float)Screen.height / 1080f);
				DrawFlash();
				GUI.color = color;
			}
		}

		private void DrawOdds(float scale)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: 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_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: 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)
			if (oddsPct == null)
			{
				return;
			}
			float t = Time.time - oddsStart;
			float num = NoticeTimeline.OddsPopup.Alpha(t);
			if (!(num <= 0f))
			{
				int num2 = oddsPct.Length;
				float screenWidth = (float)Screen.width / scale;
				float num3 = OddsPopupLayout.WidthFor(num2, screenWidth) * scale;
				float num4 = 168f * scale;
				float num5 = ((float)Screen.width - num3) * 0.5f;
				float num6 = (float)Screen.height * 0.14f;
				DrawPlate(new Rect(num5, num6, num3, num4), new Color(0.05f, 0.05f, 0.05f, 0.82f * num));
				float num7 = 12f * scale;
				float num8 = OddsPopupLayout.CellFor(num2, screenWidth) * scale;
				int fontSize = Mathf.RoundToInt(26f * scale);
				int fontSize2 = Mathf.RoundToInt(40f * scale);
				Rect rect = default(Rect);
				for (int i = 0; i < num2; i++)
				{
					((Rect)(ref rect))..ctor(num5 + num7 + (float)i * (num8 + num7), num6 + 16f * scale, num8, num4 - 32f * scale);
					Color color = oddsColors[i];
					color.a = num;
					DrawPlate(rect, color);
					Color color2 = SlotMachineView.InkOn(oddsColors[i]);
					color2.a = num;
					Label(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 8f * scale, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height * 0.38f), oddsNames[i], fontSize, color2);
					Label(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height * 0.38f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height * 0.55f), oddsPct[i] + "%", fontSize2, color2);
				}
			}
		}

		private void DrawBanner(float scale)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(banner))
			{
				float t = Time.time - bannerStart;
				float num = NoticeTimeline.BossBanner.Alpha(t);
				if (!(num <= 0f))
				{
					float num2 = 1200f * scale;
					float num3 = 140f * scale;
					float num4 = ((float)Screen.width - num2) * 0.5f;
					float num5 = (float)Screen.height * 0.36f;
					DrawPlate(new Rect(num4, num5, num2, num3), new Color(0.15f, 0.03f, 0.02f, 0.9f * num));
					Color color = default(Color);
					((Color)(ref color))..ctor(1f, 0.38f, 0.08f, num);
					Label(new Rect(num4 + 24f * scale, num5, num2 - 48f * scale, num3), banner, Mathf.RoundToInt(48f * scale), color);
				}
			}
		}

		private void DrawFlash()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!(flashStart < 0f))
			{
				float num = NoticeTimeline.NukeFlash.Alpha(Time.time - flashStart);
				if (!(num <= 0f))
				{
					DrawPlate(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), new Color(1f, 1f, 1f, num));
				}
			}
		}

		private void DrawPlate(Rect rect, Color color)
		{
			//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)
			GUI.color = color;
			GUI.DrawTexture(rect, (Texture)(object)pixel);
		}

		private void Label(Rect rect, string text, int fontSize, Color color)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0055: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			if (labelStyle == null)
			{
				labelStyle = new GUIStyle(GUI.skin.label);
			}
			labelStyle.fontSize = ((fontSize < 12) ? 12 : fontSize);
			labelStyle.alignment = (TextAnchor)4;
			labelStyle.fontStyle = (FontStyle)1;
			labelStyle.normal.textColor = color;
			GUI.color = color;
			GUI.Label(rect, text, labelStyle);
		}
	}
	internal enum ShrineOutcome
	{
		Nothing,
		White,
		Blue,
		Orange,
		Boss,
		Nuke
	}
	internal enum ShrineAction
	{
		None,
		DropPowerup,
		SpawnBoss,
		SpawnNuke,
		RunRegistered
	}
	internal static class ShrineOutcomes
	{
		public const int FirstRegistered = 6;

		public static ShrineAction ActionOf(ShrineOutcome outcome)
		{
			switch (outcome)
			{
			case ShrineOutcome.White:
			case ShrineOutcome.Blue:
			case ShrineOutcome.Orange:
				return ShrineAction.DropPowerup;
			case ShrineOutcome.Boss:
				return ShrineAction.SpawnBoss;
			case ShrineOutcome.Nuke:
				return ShrineAction.SpawnNuke;
			default:
				if (!IsRegistered(outcome))
				{
					return ShrineAction.None;
				}
				return ShrineAction.RunRegistered;
			}
		}

		public static bool IsRegistered(ShrineOutcome outcome)
		{
			return outcome >= (ShrineOutcome)6;
		}

		public static bool CountsAsSuccess(ShrineOutcome outcome)
		{
			if (outcome != ShrineOutcome.White && outcome != ShrineOutcome.Blue && outcome != ShrineOutcome.Orange)
			{
				return outcome == ShrineOutcome.Nuke;
			}
			return true;
		}
	}
	internal readonly struct ShrineOdds
	{
		public static readonly ShrineOdds Default = new ShrineOdds(new double[6] { 0.4273, 0.3418, 0.1709, 0.05, 0.01, 0.0 }, normalised: false, 100.0);

		private const int Tiers = 6;

		private readonly double[] shares;

		private readonly ShrineOutcomeSpec[] registered;

		private readonly double[] registeredShares;

		public bool Normalised { get; }

		public double InputSum { get; }

		public double this[ShrineOutcome outcome]
		{
			get
			{
				if (outcome >= ShrineOutcome.Nothing && outcome < (ShrineOutcome)6)
				{
					if (shares == null)
					{
						return 0.0;
					}
					return shares[(int)outcome];
				}
				int num = (int)(outcome - 6);
				if (registeredShares == null || num < 0 || num >= registeredShares.Length)
				{
					return 0.0;
				}
				return registeredShares[num];
			}
		}

		public int RegisteredCount
		{
			get
			{
				if (registered != null)
				{
					return registered.Length;
				}
				return 0;
			}
		}

		private ShrineOdds(double[] shares, bool normalised, double inputSum)
			: this(shares, normalised, inputSum, null, null)
		{
		}

		private ShrineOdds(double[] shares, bool normalised, double inputSum, ShrineOutcomeSpec[] registered, double[] registeredShares)
		{
			this.shares = shares;
			this.registered = registered;
			this.registeredShares = registeredShares;
			Normalised = normalised;
			InputSum = inputSum;
		}

		public ShrineOutcomeSpec Spec(ShrineOutcome outcome)
		{
			int num = (int)(outcome - 6);
			if (registered == null || num < 0 || num >= registered.Length)
			{
				return null;
			}
			return registered[num];
		}

		public ShrineOdds WithRegistered(ShrineOutcomeSpec[] specs, double[] shares)
		{
			if (specs == null || specs.Length == 0)
			{
				return this;
			}
			double[] array = new double[specs.Length];
			double num = 0.0;
			for (int i = 0; i < specs.Length; i++)
			{
				double num2 = ((i < shares.Length) ? shares[i] : 0.0);
				array[i] = ((double.IsNaN(num2) || num2 < 0.0) ? 0.0 : Math.Min(num2, 1.0));
				num += array[i];
			}
			if (num > 1.0)
			{
				for (int j = 0; j < array.Length; j++)
				{
					array[j] /= num;
				}
				num = 1.0;
			}
			double num3 = 1.0 - num;
			double[] array2 = new double[6];
			for (int k = 0; k < 6; k++)
			{
				array2[k] = this[(ShrineOutcome)k] * num3;
			}
			return new ShrineOdds(array2, Normalised, InputSum, (ShrineOutcomeSpec[])specs.Clone(), array);
		}

		public int RegisteredOutcome(string id)
		{
			for (int i = 0; i < RegisteredCount; i++)
			{
				if (registered[i].Id == id)
				{
					return 6 + i;
				}
			}
			return -1;
		}

		public ShrineOdds WithMissing(string id)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			ShrineOutcomeSpec shrineOutcomeSpec = new ShrineOutcomeSpec(id, "?", "?", default(Color), "The reels line up for a mod you don't have.", () => 0.0, delegate
			{
			});
			int registeredCount = RegisteredCount;
			ShrineOutcomeSpec[] array = new ShrineOutcomeSpec[registeredCount + 1];
			double[] array2 = new double[registeredCount + 1];
			for (int num = 0; num < registeredCount; num++)
			{
				array[num] = registered[num];
				array2[num] = registeredShares[num];
			}
			array[registeredCount] = shrineOutcomeSpec;
			return new ShrineOdds(shares, Normalised, InputSum, array, array2);
		}

		public static ShrineOdds From(float nothing, float white, float blue, float orange, float boss)
		{
			double num = Clean(nothing);
			double num2 = Clean(white);
			double num3 = Clean(blue);
			double num4 = Clean(orange);
			double num5 = Clean(boss);
			double num6 = num + num2 + num3 + num4 + num5;
			if (num6 <= 0.0)
			{
				return new ShrineOdds(Default.shares, normalised: true, num6);
			}
			bool normalised = Math.Abs(num6 - 100.0) > 0.01;
			return new ShrineOdds(new double[6]
			{
				num / num6,
				num2 / num6,
				num3 / num6,
				num4 / num6,
				num5 / num6,
				0.0
			}, normalised, num6);
		}

		public static ShrineOdds Exact(double nothing, double white, double blue, double orange, double boss)
		{
			return new ShrineOdds(new double[6] { nothing, white, blue, orange, boss, 0.0 }, normalised: false, (nothing + white + blue + orange + boss) * 100.0);
		}

		public static ShrineOdds Forced(ShrineOutcome outcome)
		{
			double[] array = new double[6];
			array[(int)outcome] = 1.0;
			return new ShrineOdds(array, normalised: false, 100.0);
		}

		public ShrineOdds WithNuke(double share)
		{
			if (double.IsNaN(share) || share < 0.0)
			{
				share = 0.0;
			}
			if (share > 1.0)
			{
				share = 1.0;
			}
			double[] array = new double[6];
			for (int i = 0; i < 5; i++)
			{
				array[i] = this[(ShrineOutcome)i] * (1.0 - share);
			}
			array[5] = share;
			return new ShrineOdds(array, Normalised, InputSum);
		}

		public ShrineOutcome Pick(double r)
		{
			double num = 0.0;
			ShrineOutcome result = ShrineOutcome.Nothing;
			bool flag = false;
			int num2 = 6 + RegisteredCount;
			for (int i = 0; i < num2; i++)
			{
				ShrineOutcome shrineOutcome = (ShrineOutcome)i;
				double num3 = this[shrineOutcome];
				if (!(num3 <= 0.0))
				{
					result = shrineOutcome;
					flag = true;
					num += num3;
					if (r < num)
					{
						return shrineOutcome;
					}
				}
			}
			if (!flag)
			{
				return ShrineOutcome.Nothing;
			}
			return result;
		}

		private static double Clean(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value) || value < 0f)
			{
				return 0.0;
			}
			return value;
		}
	}
	public sealed class ShrineOutcomeSpec
	{
		public string Id { get; }

		public string Label { get; }

		public string ReelFace { get; }

		public Color Color { get; }

		public string WinMessage { get; }

		public Func<double> Share { get; }

		public Action<ShrineOutcomeRoll> OnRoll { get; }

		public ShrineOutcomeSpec(string id, string label, string reelFace, Color color, string winMessage, Func<double> share, Action<ShrineOutcomeRoll> onRoll)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("An outcome needs an id.", "id");
			}
			Id = id;
			Label = (string.IsNullOrEmpty(label) ? id : label);
			ReelFace = (string.IsNullOrEmpty(reelFace) ? Label : reelFace);
			Color = color;
			WinMessage = winMessage ?? "";
			Share = share ?? throw new ArgumentNullException("share");
			OnRoll = onRoll ?? throw new ArgumentNullException("onRoll");
		}
	}
	public readonly struct ShrineOutcomeRoll
	{
		public string OutcomeId { get; }

		public int ShrineId { get; }

		public int UseIndex { get; }

		public int ClientId { get; }

		public Vector3 ShrinePosition { get; }

		public ShrineOutcomeRoll(string outcomeId, int shrineId, int useIndex, int clientId, Vector3 shrinePosition)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			OutcomeId = outcomeId;
			ShrineId = shrineId;
			UseIndex = useIndex;
			ClientId = clientId;
			ShrinePosition = shrinePosition;
		}
	}
	public static class ShrineOutcomeRegistry
	{
		private static ShrineOutcomeSpec[] sorted = new ShrineOutcomeSpec[0];

		internal static Action<string> LogInfo;

		internal static Action<string> LogError;

		internal static IReadOnlyList<ShrineOutcomeSpec> All => sorted;

		public static void Register(ShrineOutcomeSpec spec)
		{
			if (spec == null)
			{
				throw new ArgumentNullException("spec");
			}
			List<ShrineOutcomeSpec> list = new List<ShrineOutcomeSpec>(sorted);
			foreach (ShrineOutcomeSpec item in list)
			{
				if (item.Id == spec.Id)
				{
					throw new ArgumentException("Shrine outcome '" + spec.Id + "' is already registered.", "spec");
				}
			}
			list.Add(spec);
			list.Sort((ShrineOutcomeSpec a, ShrineOutcomeSpec b) => string.CompareOrdinal(a.Id, b.Id));
			sorted = list.ToArray();
			LogInfo?.Invoke("Shrine outcome '" + spec.Id + "' (" + spec.Label + ") registered.");
		}

		internal static ShrineOdds Apply(ShrineOdds odds)
		{
			ShrineOutcomeSpec[] array = sorted;
			if (array.Length == 0)
			{
				return odds;
			}
			double[] array2 = new double[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = ReadShare(array[i]);
			}
			return odds.WithRegistered(array, array2);
		}

		internal static string Stamp()
		{
			ShrineOutcomeSpec[] array = sorted;
			string[] array2 = new string[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = array[i].Id + "=" + ((int)Math.Round(ReadShare(array[i]) * 100000.0)).ToString(CultureInfo.InvariantCulture);
			}
			return string.Join(",", array2);
		}

		internal static void Clear()
		{
			sorted = new ShrineOutcomeSpec[0];
		}

		private static double ReadShare(ShrineOutcomeSpec spec)
		{
			try
			{
				return spec.Share();
			}
			catch (Exception arg)
			{
				LogError?.Invoke($"Shrine outcome '{spec.Id}' share threw, so it gets 0: {arg}");
				return 0.0;
			}
		}
	}
	internal static class ShrinePayout
	{
		public static void Drop(int shrineId, int seed, HostStep step, Vector3 at)
		{
			//IL_0083: 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)
			Powerup[] array = Pool(ItemManager.Instance, step.Outcome);
			int length = ((array != null) ? array.Length : 0);
			int num = ShrineItemPick.Index(ShrineRoll.ItemRoll(seed, shrineId, step.UseIndex), length);
			if (array == null || num < 0 || (Object)(object)array[num] == (Object)null)
			{
				ShrinePlugin.Log.LogWarning((object)$"Shrine {shrineId} rolled {step.Outcome} but the pool has nothing to drop.");
				return;
			}
			int nextId = ItemManager.Instance.GetNextId();
			int id = array[num].id;
			ItemManager.Instance.DropPowerupAtPosition(id, at, nextId);
			ServerSend.DropPowerupAtPosition(id, nextId, at);
			if (ShrinePlugin.Cfg.VerboseLogging.Value)
			{
				ShrinePlugin.Log.LogInfo((object)$"Shrine {shrineId} use {step.UseIndex} dropped powerup {id}.");
			}
		}

		public static void SpawnBoss(int shrineId, int seed, int useIndex, Vector3 shrinePos)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)MobSpawner.Instance == (Object)null || (Object)(object)MobManager.Instance == (Object)null || (Object)(object)GameLoop.Instance == (Object)null)
				{
					ShrinePlugin.Log.LogWarning((object)$"Shrine {shrineId} rolled a boss but the spawner is missing.");
					return;
				}
				MobType[] bosses = GameLoop.Instance.bosses;
				List<MobType> list = new List<MobType>();
				if (bosses != null)
				{
					for (int i = 0; i < bosses.Length; i++)
					{
						if ((Object)(object)bosses[i] != (Object)null)
						{
							list.Add(bosses[i]);
						}
					}
				}
				if (list.Count == 0)
				{
					ShrinePlugin.Log.LogWarning((object)$"Shrine {shrineId} rolled a boss but no boss types are loaded.");
					return;
				}
				int index = ShrineItemPick.Index(ShrineRoll.ItemRoll(seed, shrineId, useIndex), list.Count);
				MobType val = list[index];
				if (TryGround(shrinePos, shrineId, out var at))
				{
					int nextId = MobManager.Instance.GetNextId();
					float num = 0.9f + 0.1f * (float)GameManager.instance.GetPlayersAlive();
					MobSpawner.Instance.ServerSpawnNewMob(nextId, val.id, at, 1.5f, num, (BossType)2, -1);
					if (ShrinePlugin.Cfg.VerboseLogging.Value)
					{
						ShrinePlugin.Log.LogInfo((object)$"Shrine {shrineId} use {useIndex} spawned boss {val.name}.");
					}
				}
			}
			catch (Exception arg)
			{
				ShrinePlugin.L