Decompiled source of Chaos Night v0.6.4

BepInEx/plugins/ChaosNight/ChaosNight.dll

Decompiled 2 weeks 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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ChaosNight.Core;
using Extensions;
using HarmonyLib;
using Mirror;
using MoreMountains.Feedbacks;
using Newtonsoft.Json;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ChaosNight
{
	public static class AllInPolicy
	{
		public static bool CanExpand(bool enabled, bool available, bool reserved, bool correctTable, bool positiveBank, bool freeBet, bool percentageCalculation)
		{
			if (enabled && available && !reserved && correctTable && positiveBank && !freeBet)
			{
				return !percentageCalculation;
			}
			return false;
		}

		public static bool AcceptCharge(bool extraHand, bool outstandingAllIn, bool reserved, bool aboveOrdinaryCap, bool fullCurrentBank, bool available, bool authenticPlayer)
		{
			if (reserved || (extraHand && outstandingAllIn))
			{
				return false;
			}
			if (!extraHand && fullCurrentBank)
			{
				if (available)
				{
					return authenticPlayer;
				}
				return false;
			}
			return !aboveOrdinaryCap;
		}

		public static bool Consume(bool nativeSuccess, bool reservedFullBank, bool extraHand)
		{
			if (nativeSuccess && reservedFullBank)
			{
				return !extraHand;
			}
			return false;
		}

		public static bool ExactNativeLong(bool nonnegative, bool withinLongRange, bool roundTrips)
		{
			if (nonnegative && withinLongRange)
			{
				return roundTrips;
			}
			return false;
		}
	}
	public sealed class ChaosAllIn : IDisposable
	{
		internal sealed class CallScope
		{
			internal GameBase Game;

			internal BigNumber Bet;

			internal bool Input;

			internal bool Extra;

			internal bool Percentage;
		}

		internal sealed class ChargeState
		{
			internal bool Reserved;

			internal GameBase Game;

			internal BigNumber PaidStake;
		}

		private sealed class LabelQuote
		{
			internal BigNumber Min;

			internal BigNumber Max;
		}

		internal static ChaosAllIn Active;

		private readonly Func<bool> allowed;

		private readonly Func<bool> available;

		private readonly Func<bool> consume;

		private readonly Func<PlayerProfile, bool> authentic;

		private readonly Action<string> log;

		private readonly HashSet<GameBase> outstanding = new HashSet<GameBase>();

		private readonly Dictionary<Keypad, LabelQuote> labels = new Dictionary<Keypad, LabelQuote>();

		private readonly List<Keypad> scratch = new List<Keypad>();

		private bool disposed;

		private bool reserved;

		private bool reported;

		private bool faulted;

		[ThreadStatic]
		private static int ordinaryDepth;

		[ThreadStatic]
		internal static CallScope Scope;

		private static readonly FieldInfo MoneyInstance = AccessTools.Field(typeof(MoneyManager), "_instance");

		private static readonly FieldInfo FreeBet = AccessTools.Field(typeof(GameBase), "isGoldenChipApplied");

		private static readonly FieldInfo LabelText = AccessTools.Field(typeof(Keypad), "minMaxBetText");

		private static readonly MethodInfo RouletteTotal = AccessTools.Method(typeof(Roulette), "GetTotalBet", Type.EmptyTypes, (Type[])null);

		private static readonly FieldInfo RouletteBets = AccessTools.Field(typeof(Roulette), "_bets");

		private static readonly MethodInfo UpdateLabels = AccessTools.Method(typeof(Keypad), "UpdateMinMaxBetText", new Type[2]
		{
			typeof(BigNumber),
			typeof(BigNumber)
		}, (Type[])null);

		private static readonly MethodInfo RenderLabels = AccessTools.Method(typeof(Keypad), "RequestRender", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo CurrentBetSetter = AccessTools.PropertySetter(typeof(GameBase), "currentBet");

		private static readonly BigNumber RouletteSafeBank = BigNumber.FromDouble(4.993592041279217E+306);

		public ChaosAllIn(Func<bool> featureAllowed, Func<bool> permissionAvailable, Func<bool> consumePermission, Func<PlayerProfile, bool> authenticPlayer, Action<string> logger)
		{
			if (featureAllowed == null || permissionAvailable == null || consumePermission == null || authenticPlayer == null)
			{
				throw new ArgumentNullException("callback");
			}
			if (Active != null)
			{
				throw new InvalidOperationException("BET IT ALL is already initialized.");
			}
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Verified BET IT ALL native API is unavailable.");
			}
			allowed = featureAllowed;
			available = permissionAvailable;
			consume = consumePermission;
			authentic = authenticPlayer;
			log = logger;
			Active = this;
		}

		private bool Enabled()
		{
			if (!disposed && !faulted)
			{
				return allowed();
			}
			return false;
		}

		private MoneyManager Money()
		{
			object? value = MoneyInstance.GetValue(null);
			return (MoneyManager)((value is MoneyManager) ? value : null);
		}

		private bool Table(GameBase game)
		{
			//IL_0018: 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)
			if ((Object)(object)game == (Object)null || !((Component)game).gameObject.activeInHierarchy)
			{
				return false;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			if (!((Scene)(ref activeScene)).IsValid() || !((Scene)(ref activeScene)).isLoaded || ((Scene)(ref activeScene)).name != "CasinoScene")
			{
				return false;
			}
			NetworkIdentity netIdentity = ((NetworkBehaviour)game).netIdentity;
			if ((Object)(object)netIdentity == (Object)null || netIdentity.netId == 0 || !object.ReferenceEquals(((Component)netIdentity).GetComponent<GameBase>(), game))
			{
				return false;
			}
			NetworkIdentity value;
			if (!NetworkServer.active)
			{
				if (NetworkClient.active && ((NetworkBehaviour)game).isClient && NetworkClient.spawned.TryGetValue(netIdentity.netId, out value))
				{
					return object.ReferenceEquals(value, netIdentity);
				}
				return false;
			}
			if (((NetworkBehaviour)game).isServer && NetworkServer.spawned.TryGetValue(netIdentity.netId, out value))
			{
				return object.ReferenceEquals(value, netIdentity);
			}
			return false;
		}

		private bool IsFree(GameBase game)
		{
			return (bool)FreeBet.GetValue(game);
		}

		private bool AmountSupported(GameBase game, BigNumber amount)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_004c: 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)
			//IL_0054: 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)
			if (game is Roulette)
			{
				return amount <= RouletteSafeBank;
			}
			if (!(game is Plinko))
			{
				return true;
			}
			BigNumber val = BigNumber.op_Implicit(long.MaxValue);
			if (amount < BigNumber.Zero || amount > val)
			{
				return false;
			}
			BigNumber val2 = BigNumber.op_Implicit(((BigNumber)(ref amount)).ToLong());
			return AllInPolicy.ExactNativeLong(nonnegative: true, withinLongRange: true, val2 == amount);
		}

		private string AmountMessage(GameBase game)
		{
			if (!(game is Plinko))
			{
				return "BET IT ALL: roulette payout would overflow.";
			}
			return "BET IT ALL: use whole dollars up to 9.22E18 on Plinko.";
		}

		private BigNumber OrdinaryMax(GameBase game)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			ordinaryDepth++;
			try
			{
				return game.MaxBet;
			}
			finally
			{
				ordinaryDepth--;
			}
		}

		private bool OrdinaryRoulette(GameBase game, BigNumber cap)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!(game is Roulette))
			{
				return false;
			}
			if (!(RouletteBets.GetValue(game) is Dictionary<string, BigNumber> { Count: <=128 } dictionary))
			{
				return false;
			}
			foreach (BigNumber value in dictionary.Values)
			{
				if (value > cap || value < BigNumber.Zero)
				{
					return false;
				}
			}
			return true;
		}

		internal void MaxBet(GameBase game, ref BigNumber result)
		{
			//IL_002b: 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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if (ordinaryDepth != 0 || disposed || faulted)
			{
				return;
			}
			try
			{
				MoneyManager val = Money();
				if ((!((Object)(object)val != (Object)null) || AmountSupported(game, val.balance)) && AllInPolicy.CanExpand(Enabled(), available(), reserved, Table(game), (Object)(object)val != (Object)null && val.balance > BigNumber.Zero, (Object)(object)game == (Object)null || IsFree(game), Scope != null && Scope.Percentage))
				{
					BigNumber balance = val.balance;
					if (Scope == null || !object.ReferenceEquals(Scope.Game, game) || (!Scope.Input && Scope.Extra) || !(Scope.Bet != balance))
					{
						result = BigNumber.Max(result, balance);
					}
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		internal CallScope EnterGame(GameBase game, bool extra)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			//IL_0052: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			CallScope scope = Scope;
			BigNumber bet = BigNumber.Zero;
			try
			{
				if ((Object)(object)game != (Object)null)
				{
					bet = (BigNumber)((game is Roulette) ? ((BigNumber)RouletteTotal.Invoke(game, null)) : game.currentBet);
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
			CallScope callScope = new CallScope();
			callScope.Game = game;
			callScope.Bet = bet;
			callScope.Extra = extra;
			Scope = callScope;
			return scope;
		}

		internal bool BeforeStart(GameBase game)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || !NetworkServer.active || (Object)(object)game == (Object)null)
			{
				return true;
			}
			try
			{
				if (Scope != null && Scope.Extra && outstanding.Contains(game))
				{
					Reject(game, "BET IT ALL cannot double or split.");
					return false;
				}
				if (!Enabled() || !Table(game) || IsFree(game) || game.isPlaying || Scope == null || Scope.Extra)
				{
					return true;
				}
				MoneyManager val = Money();
				if ((Object)(object)val == (Object)null)
				{
					return true;
				}
				bool flag = val.balance > BigNumber.Zero && Scope.Bet == val.balance;
				if (flag && !available())
				{
					Reject(game, "BET IT ALL used. Choose a smaller bet.");
					return false;
				}
				if (flag && !AmountSupported(game, val.balance))
				{
					Reject(game, AmountMessage(game));
					return false;
				}
				BigNumber val2 = OrdinaryMax(game);
				if (Scope.Bet <= val2 || OrdinaryRoulette(game, val2))
				{
					return true;
				}
				if (!reserved && available() && Scope.Bet == val.balance)
				{
					return true;
				}
				Reject(game, available() ? "BET IT ALL: press ALL for the full crew bank." : "BET IT ALL used. Choose a normal bet.");
				return false;
			}
			catch (Exception error)
			{
				Report(error);
				return false;
			}
		}

		private void Reject(GameBase game, string message)
		{
			Keypad val = (((Object)(object)game == (Object)null) ? null : game.Keypad);
			if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).isServer)
			{
				val.ServerInvalidBetAmountFb();
				val.Network_errorMessage = message;
				RenderLabels.Invoke(val, null);
			}
		}

		internal CallScope EnterInput(Keypad keypad, string input)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			CallScope scope = Scope;
			CallScope callScope = new CallScope();
			callScope.Game = (((Object)(object)keypad == (Object)null) ? null : keypad.casinoGame);
			callScope.Bet = BigNumber.Parse(input);
			callScope.Input = true;
			Scope = callScope;
			return scope;
		}

		internal CallScope EnterPercentage(Keypad keypad)
		{
			CallScope scope = Scope;
			CallScope callScope = new CallScope();
			callScope.Game = (((Object)(object)keypad == (Object)null) ? null : keypad.casinoGame);
			callScope.Percentage = true;
			Scope = callScope;
			return scope;
		}

		internal void Percentage(Keypad keypad, decimal percentage, bool bankCap, ref BigNumber result)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: 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)
			if (disposed || faulted || !NetworkServer.active || !bankCap || percentage != 1m)
			{
				return;
			}
			try
			{
				GameBase val = (((Object)(object)keypad == (Object)null) ? null : keypad.casinoGame);
				MoneyManager val2 = Money();
				if ((!((Object)(object)val2 != (Object)null) || AmountSupported(val, val2.balance)) && AllInPolicy.CanExpand(Enabled(), available(), reserved, Table(val), (Object)(object)val2 != (Object)null && val2.balance > BigNumber.Zero, (Object)(object)val == (Object)null || IsFree(val), percentageCalculation: false))
				{
					result = val2.balance;
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		internal bool BeforeAllButton(Keypad keypad)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || !NetworkServer.active || (Object)(object)keypad == (Object)null)
			{
				return true;
			}
			try
			{
				GameBase casinoGame = keypad.casinoGame;
				MoneyManager val = Money();
				if (Enabled() && available() && Table(casinoGame) && !IsFree(casinoGame) && (Object)(object)val != (Object)null && !AmountSupported(casinoGame, val.balance))
				{
					Reject(casinoGame, AmountMessage(casinoGame));
					return false;
				}
				return true;
			}
			catch (Exception error)
			{
				Report(error);
				return false;
			}
		}

		internal bool BeforeCharge(BigNumber amount, PlayerProfile player, ChangeType kind, out ChargeState state)
		{
			//IL_0013: 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)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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)
			//IL_00d8: 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)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			state = null;
			if (disposed || !NetworkServer.active || (int)kind != 0 || amount >= BigNumber.Zero || Scope == null || Scope.Input || Scope.Percentage || (Object)(object)Scope.Game == (Object)null)
			{
				return true;
			}
			try
			{
				GameBase game = Scope.Game;
				bool extra = Scope.Extra;
				if (extra && outstanding.Contains(game))
				{
					Reject(game, "BET IT ALL cannot double or split.");
					return false;
				}
				if (!Enabled() || !Table(game) || IsFree(game))
				{
					return true;
				}
				MoneyManager val = Money();
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				BigNumber val2 = -amount;
				BigNumber balance = val.balance;
				BigNumber val3 = OrdinaryMax(game);
				bool flag = balance > BigNumber.Zero && val2 == balance;
				bool flag2 = available();
				if (!extra && flag && flag2 && !AmountSupported(game, balance))
				{
					Reject(game, AmountMessage(game));
					return false;
				}
				bool aboveOrdinaryCap = !extra && val2 > val3 && !OrdinaryRoulette(game, val3);
				if (!AllInPolicy.AcceptCharge(extra, outstandingAllIn: false, reserved, aboveOrdinaryCap, flag, flag2, (Object)(object)player != (Object)null && authentic(player)))
				{
					Reject(game, "BET IT ALL needs the current full crew bank.");
					return false;
				}
				if (extra || !flag || !flag2)
				{
					return true;
				}
				reserved = true;
				state = new ChargeState
				{
					Reserved = true,
					Game = game,
					PaidStake = val2
				};
				return true;
			}
			catch (Exception error)
			{
				Report(error);
				return false;
			}
		}

		internal void AfterCharge(ChargeState state, bool success)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (state == null || !AllInPolicy.Consume(success, state.Reserved, extraHand: false))
			{
				return;
			}
			try
			{
				outstanding.Add(state.Game);
				if (!consume())
				{
					throw new InvalidOperationException("Paid BET IT ALL permission could not be committed.");
				}
				CurrentBetSetter.Invoke(state.Game, new object[1] { state.PaidStake });
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		internal void FinishCharge(ChargeState state)
		{
			if (state != null && state.Reserved)
			{
				reserved = false;
				RefreshLabels();
			}
		}

		internal void ForgetGame(GameBase game)
		{
			if (!object.ReferenceEquals(game, null))
			{
				outstanding.Remove(game);
			}
		}

		internal void ForgetKeypad(Keypad keypad)
		{
			if (!object.ReferenceEquals(keypad, null))
			{
				labels.Remove(keypad);
			}
		}

		internal void Label(Keypad keypad, BigNumber min, BigNumber max)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || (Object)(object)keypad == (Object)null)
			{
				return;
			}
			try
			{
				if (labels.Count < 512 || labels.ContainsKey(keypad))
				{
					labels[keypad] = new LabelQuote
					{
						Min = min,
						Max = max
					};
				}
				GameBase casinoGame = keypad.casinoGame;
				MoneyManager val = Money();
				object? value = LabelText.GetValue(keypad);
				TextMeshPro val2 = (TextMeshPro)((value is TextMeshPro) ? value : null);
				if (!((Object)(object)val2 == (Object)null) && Enabled() && available() && !reserved && Table(casinoGame) && !((Object)(object)val == (Object)null) && !(val.balance <= BigNumber.Zero) && !IsFree(casinoGame) && AmountSupported(casinoGame, val.balance))
				{
					string text = ((TMP_Text)val2).text;
					int num = text?.LastIndexOf('\n') ?? (-1);
					if (num >= 0)
					{
						((TMP_Text)val2).text = text.Substring(0, num + 1) + "BET IT ALL: " + MoneyFormatter.FormatWithDollar(val.balance, false);
						RenderLabels.Invoke(keypad, null);
					}
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		public void RefreshLabels()
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			if (disposed)
			{
				return;
			}
			if (NetworkServer.active && ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.RefreshAll();
			}
			scratch.Clear();
			foreach (Keypad key in labels.Keys)
			{
				scratch.Add(key);
			}
			try
			{
				foreach (Keypad item in scratch)
				{
					if ((Object)(object)item == (Object)null)
					{
						labels.Remove(item);
						continue;
					}
					LabelQuote labelQuote = labels[item];
					UpdateLabels.Invoke(item, new object[2] { labelQuote.Min, labelQuote.Max });
					RenderLabels.Invoke(item, null);
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
			finally
			{
				scratch.Clear();
			}
		}

		private void Report(Exception error)
		{
			faulted = true;
			if (reported)
			{
				return;
			}
			reported = true;
			if (log == null)
			{
				return;
			}
			try
			{
				log("BET IT ALL held after a native integration error: " + error.GetBaseException().Message);
			}
			catch
			{
			}
		}

		private static bool NativeApiAvailable()
		{
			if (MoneyInstance != null && MoneyInstance.IsStatic && FreeBet != null && FreeBet.FieldType == typeof(bool) && LabelText != null && LabelText.FieldType == typeof(TextMeshPro) && RouletteTotal != null && RouletteTotal.ReturnType == typeof(BigNumber) && RouletteBets != null && RouletteBets.FieldType == typeof(Dictionary<string, BigNumber>) && UpdateLabels != null && RenderLabels != null && CurrentBetSetter != null && AccessTools.PropertyGetter(typeof(GameBase), "MaxBet") != null)
			{
				return AccessTools.Method(typeof(Keypad), "GetPercentageBet", new Type[2]
				{
					typeof(decimal),
					typeof(bool)
				}, (Type[])null) != null;
			}
			return false;
		}

		public static string SelfTest()
		{
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("BET IT ALL native API changed.");
			}
			if (!AllInPolicy.AcceptCharge(extraHand: false, outstandingAllIn: false, reserved: false, aboveOrdinaryCap: true, fullCurrentBank: true, available: true, authenticPlayer: true) || AllInPolicy.AcceptCharge(extraHand: false, outstandingAllIn: false, reserved: false, aboveOrdinaryCap: true, fullCurrentBank: false, available: true, authenticPlayer: true) || AllInPolicy.AcceptCharge(extraHand: true, outstandingAllIn: true, reserved: false, aboveOrdinaryCap: false, fullCurrentBank: false, available: true, authenticPlayer: true) || AllInPolicy.Consume(nativeSuccess: false, reservedFullBank: true, extraHand: false))
			{
				throw new InvalidOperationException("BET IT ALL charge policy failed.");
			}
			return "PASS: BET IT ALL native API and full-bankroll charge policy (offline)";
		}

		public void Dispose()
		{
			disposed = true;
			outstanding.Clear();
			labels.Clear();
			scratch.Clear();
			reserved = false;
			if (object.ReferenceEquals(Active, this))
			{
				Active = null;
			}
		}
	}
	[HarmonyPatch(typeof(GameBase), "get_MaxBet")]
	internal static class AllInMaxBetPatch
	{
		private static void Postfix(GameBase __instance, ref BigNumber __result)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.MaxBet(__instance, ref __result);
			}
		}
	}
	[HarmonyPatch]
	internal static class AllInStartScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(GameBase), "TryStartGame", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Poker), "TryStartGame", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Roulette), "TryStartGame", (Type[])null, (Type[])null);
		}

		private static bool Prefix(GameBase __instance, out ChaosAllIn.CallScope __state)
		{
			__state = ChaosAllIn.Scope;
			if (ChaosAllIn.Active == null)
			{
				return true;
			}
			ChaosAllIn.Active.EnterGame(__instance, extra: false);
			return ChaosAllIn.Active.BeforeStart(__instance);
		}

		private static Exception Finalizer(ChaosAllIn.CallScope __state, Exception __exception)
		{
			ChaosAllIn.Scope = __state;
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class AllInExtraScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Blackjack), "PlayerDouble", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Blackjack), "PlayerSplit", (Type[])null, (Type[])null);
		}

		private static bool Prefix(GameBase __instance, out ChaosAllIn.CallScope __state)
		{
			__state = ChaosAllIn.Scope;
			if (ChaosAllIn.Active == null)
			{
				return true;
			}
			ChaosAllIn.Active.EnterGame(__instance, extra: true);
			return ChaosAllIn.Active.BeforeStart(__instance);
		}

		private static Exception Finalizer(ChaosAllIn.CallScope __state, Exception __exception)
		{
			ChaosAllIn.Scope = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Keypad), "ApplyInput")]
	internal static class AllInInputScopePatch
	{
		private static void Prefix(Keypad __instance, string __0, out ChaosAllIn.CallScope __state)
		{
			__state = ChaosAllIn.Scope;
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.EnterInput(__instance, __0);
			}
		}

		private static Exception Finalizer(ChaosAllIn.CallScope __state, Exception __exception)
		{
			ChaosAllIn.Scope = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Keypad), "GetPercentageBet")]
	internal static class AllInPercentagePatch
	{
		private static void Prefix(Keypad __instance, out ChaosAllIn.CallScope __state)
		{
			__state = ChaosAllIn.Scope;
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.EnterPercentage(__instance);
			}
		}

		private static void Postfix(Keypad __instance, decimal __0, bool __1, ref BigNumber __result)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.Percentage(__instance, __0, __1, ref __result);
			}
		}

		private static Exception Finalizer(ChaosAllIn.CallScope __state, Exception __exception)
		{
			ChaosAllIn.Scope = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Keypad), "SetBetAll")]
	internal static class AllInAllButtonPatch
	{
		private static bool Prefix(Keypad __instance)
		{
			if (ChaosAllIn.Active != null)
			{
				return ChaosAllIn.Active.BeforeAllButton(__instance);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MoneyManager), "TryChangeBalance")]
	internal static class AllInPaidChargePatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(BigNumber amount, PlayerProfile changer, ChangeType changeType, ref bool __result, out ChaosAllIn.ChargeState __state)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (ChaosAllIn.Active == null || ChaosAllIn.Active.BeforeCharge(amount, changer, changeType, out __state))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static void Postfix(ChaosAllIn.ChargeState __state, bool __result)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.AfterCharge(__state, __result);
			}
		}

		private static Exception Finalizer(ChaosAllIn.ChargeState __state, Exception __exception)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.FinishCharge(__state);
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(GameBase), "ResetGame")]
	internal static class AllInResetPatch
	{
		private static void Postfix(GameBase __instance)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.ForgetGame(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(GameBase), "OnDisable")]
	internal static class AllInDisablePatch
	{
		private static void Prefix(GameBase __instance)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.ForgetGame(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "OnDisable")]
	internal static class AllInKeypadDisablePatch
	{
		private static void Prefix(Keypad __instance)
		{
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.ForgetKeypad(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "UpdateMinMaxBetText")]
	internal static class AllInLabelPatch
	{
		private static void Postfix(Keypad __instance, BigNumber __0, BigNumber __1)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (ChaosAllIn.Active != null)
			{
				ChaosAllIn.Active.Label(__instance, __0, __1);
			}
		}
	}
	public static class BedWakePolicy
	{
		public static bool CanStand(bool enabled, bool clientReady, bool localOwner, bool home, bool nativeWakeScope, bool spawnBoxInput, bool hasBody, bool nativeComponentsReady)
		{
			if (enabled && clientReady && localOwner && home && nativeWakeScope && spawnBoxInput && hasBody)
			{
				return nativeComponentsReady;
			}
			return false;
		}

		public static bool SameOwner(object expected, object actual)
		{
			if (expected != null)
			{
				return object.ReferenceEquals(expected, actual);
			}
			return false;
		}
	}
	internal static class BedWakeCallScope<T> where T : class
	{
		[ThreadStatic]
		internal static T Current;

		internal static T Enter(T owner)
		{
			T current = Current;
			Current = owner;
			return current;
		}

		internal static void Restore(T previous)
		{
			Current = previous;
		}
	}
	public sealed class ChaosBedWake : IDisposable
	{
		internal static ChaosBedWake Active;

		private readonly Func<bool> canUse;

		private readonly Action<string> log;

		private bool disposed;

		private bool reported;

		private static readonly FieldInfo Waking = AccessTools.Field(typeof(PlayerController), "_isWakingUp");

		private static readonly FieldInfo Body = AccessTools.Field(typeof(PlayerController), "_rb");

		private static readonly FieldInfo Carry = AccessTools.Field(typeof(PlayerController), "_pc");

		private static readonly MethodInfo FreeState = AccessTools.Method(typeof(PlayerController), "SetPlayerState", new Type[1] { typeof(PlayerState) }, (Type[])null);

		public ChaosBedWake(Func<bool> canUseNativeFeatures, Action<string> logger)
		{
			if (canUseNativeFeatures == null)
			{
				throw new ArgumentNullException("canUseNativeFeatures");
			}
			if (Active != null)
			{
				throw new InvalidOperationException("Native bed wake is already initialized.");
			}
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Verified native bed-wake API is unavailable.");
			}
			canUse = canUseNativeFeatures;
			log = logger;
			Active = this;
		}

		internal bool TryStand(PlayerController controller)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Invalid comparison between Unknown and I4
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || (Object)(object)controller == (Object)null || !BedWakePolicy.SameOwner(BedWakeCallScope<PlayerController>.Current, controller))
			{
				return false;
			}
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				NetworkIdentity netIdentity = ((NetworkBehaviour)controller).netIdentity;
				NetworkIdentity value;
				bool localOwner = (Object)(object)NetworkClient.localPlayer != (Object)null && ((NetworkBehaviour)controller).isLocalPlayer && BedWakePolicy.SameOwner(NetworkClient.localPlayer, netIdentity) && BedWakePolicy.SameOwner(((Component)netIdentity).GetComponent<PlayerController>(), controller) && netIdentity.netId != 0 && NetworkClient.spawned.TryGetValue(netIdentity.netId, out value) && BedWakePolicy.SameOwner(value, netIdentity);
				bool home = ((Scene)(ref activeScene)).IsValid() && ((Scene)(ref activeScene)).isLoaded && ((Scene)(ref activeScene)).name == "HomeScene";
				if (!BedWakePolicy.CanStand(canUse(), NetworkClient.active && NetworkClient.ready, localOwner, home, nativeWakeScope: true, (int)InputEvents.ActiveLayer == 2, controller.hasBody, nativeComponentsReady: true))
				{
					return false;
				}
				object? value2 = Body.GetValue(controller);
				Rigidbody val = (Rigidbody)((value2 is Rigidbody) ? value2 : null);
				object? value3 = Carry.GetValue(controller);
				PlayerCarry val2 = (PlayerCarry)((value3 is PlayerCarry) ? value3 : null);
				if (!((Component)controller).gameObject.activeInHierarchy || !((Behaviour)controller).isActiveAndEnabled || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)controller.head == (Object)null || !BedWakePolicy.SameOwner(val, ((Component)controller).GetComponent<Rigidbody>()) || !BedWakePolicy.SameOwner(val2, ((Component)controller).GetComponent<PlayerCarry>()))
				{
					return false;
				}
				Waking.SetValue(controller, true);
				InputEvents.ActiveLayer = (InputLayer)0;
				controller.IsLocked = false;
				PlayerState state = controller.State;
				controller.State = (PlayerState)0;
				if ((int)state == 0)
				{
					FreeState.Invoke(controller, new object[1] { (object)(PlayerState)0 });
				}
				return true;
			}
			catch (Exception error)
			{
				Report(error);
				return false;
			}
		}

		private void Report(Exception error)
		{
			if (reported)
			{
				return;
			}
			reported = true;
			if (log == null)
			{
				return;
			}
			try
			{
				log("Standing bed wake held; native wake retained: " + error.GetBaseException().Message);
			}
			catch
			{
			}
		}

		private static bool NativeApiAvailable()
		{
			if (Waking != null && Waking.FieldType == typeof(bool) && !Waking.IsStatic && Body != null && Body.FieldType == typeof(Rigidbody) && Carry != null && Carry.FieldType == typeof(PlayerCarry) && FreeState != null && !FreeState.IsStatic && FreeState.ReturnType == typeof(void) && AccessTools.Method(typeof(PlayerController), "WakeUp", Type.EmptyTypes, (Type[])null) != null && AccessTools.Method(typeof(PlayerController), "UserCode_TargetWakeUp__NetworkConnection", new Type[1] { typeof(NetworkConnection) }, (Type[])null) != null)
			{
				return AccessTools.Method(typeof(PlayerController), "DelayedWakeUp", Type.EmptyTypes, (Type[])null) != null;
			}
			return false;
		}

		public static string SelfTest()
		{
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Native bed-wake API shape changed.");
			}
			if (!BedWakePolicy.CanStand(enabled: true, clientReady: true, localOwner: true, home: true, nativeWakeScope: true, spawnBoxInput: true, hasBody: true, nativeComponentsReady: true) || BedWakePolicy.CanStand(enabled: true, clientReady: true, localOwner: false, home: true, nativeWakeScope: true, spawnBoxInput: true, hasBody: true, nativeComponentsReady: true) || BedWakePolicy.CanStand(enabled: true, clientReady: true, localOwner: true, home: false, nativeWakeScope: true, spawnBoxInput: true, hasBody: true, nativeComponentsReady: true) || BedWakePolicy.CanStand(enabled: true, clientReady: true, localOwner: true, home: true, nativeWakeScope: false, spawnBoxInput: true, hasBody: true, nativeComponentsReady: true) || BedWakePolicy.CanStand(enabled: true, clientReady: true, localOwner: true, home: true, nativeWakeScope: true, spawnBoxInput: true, hasBody: false, nativeComponentsReady: true))
			{
				throw new InvalidOperationException("Native bed-wake scope policy failed.");
			}
			return "PASS: native standing bed-wake API and local-owner/Home/target scope (offline)";
		}

		public void Dispose()
		{
			disposed = true;
			if (object.ReferenceEquals(Active, this))
			{
				Active = null;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "UserCode_TargetWakeUp__NetworkConnection")]
	internal static class BedWakeNativeTargetScopePatch
	{
		private static void Prefix(PlayerController __instance, out PlayerController __state)
		{
			__state = BedWakeCallScope<PlayerController>.Enter(__instance);
		}

		private static Exception Finalizer(Exception __exception, PlayerController __state)
		{
			BedWakeCallScope<PlayerController>.Restore(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(PlayerController), "WakeUp")]
	internal static class BedWakeStandingPatch
	{
		private static bool Prefix(PlayerController __instance)
		{
			if (ChaosBedWake.Active != null)
			{
				return !ChaosBedWake.Active.TryStand(__instance);
			}
			return true;
		}
	}
	public static class BetDisplayPolicy
	{
		public static bool CanRefresh(bool enabled, bool server, bool casino, bool gameManager, bool gameState, bool liveKeypad, bool liveGame, bool correctBinding)
		{
			if (enabled && server && casino && gameManager && gameState && liveKeypad && liveGame)
			{
				return correctBinding;
			}
			return false;
		}

		public static bool LimitsChanged(bool hasPrevious, double oldMin, long oldMinExponent, double oldMax, long oldMaxExponent, double newMin, long newMinExponent, double newMax, long newMaxExponent)
		{
			if (hasPrevious && oldMin == newMin && oldMinExponent == newMinExponent && oldMax == newMax)
			{
				return oldMaxExponent != newMaxExponent;
			}
			return true;
		}
	}
	public sealed class ChaosBetDisplay : IDisposable
	{
		private sealed class Quote
		{
			internal GameBase Game;

			internal bool HasPrevious;

			internal double Min;

			internal double Max;

			internal long MinExponent;

			internal long MaxExponent;
		}

		private const int MaximumTrackedKeypads = 512;

		internal static ChaosBetDisplay Active;

		private readonly Func<bool> canRefresh;

		private readonly Action<string> log;

		private readonly Dictionary<Keypad, Quote> keypads = new Dictionary<Keypad, Quote>();

		private readonly List<Keypad> scratch = new List<Keypad>();

		private bool disposed;

		private bool refreshing;

		private bool reported;

		private static readonly FieldInfo ManagerInstance = AccessTools.Field(typeof(GameManager), "_instance");

		private static readonly MethodInfo RequestRender = AccessTools.Method(typeof(Keypad), "RequestRender", Type.EmptyTypes, (Type[])null);

		public ChaosBetDisplay(Func<bool> refreshAllowed, Action<string> logger)
		{
			if (refreshAllowed == null)
			{
				throw new ArgumentNullException("refreshAllowed");
			}
			if (Active != null)
			{
				throw new InvalidOperationException("Native bet display is already initialized.");
			}
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Verified native bet-display API is unavailable.");
			}
			canRefresh = refreshAllowed;
			log = logger;
			Active = this;
		}

		public void RefreshAll()
		{
			if (disposed || refreshing || !NetworkServer.active)
			{
				return;
			}
			refreshing = true;
			try
			{
				scratch.Clear();
				foreach (Keypad key in keypads.Keys)
				{
					scratch.Add(key);
				}
				foreach (Keypad item in scratch)
				{
					if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy)
					{
						keypads.Remove(item);
					}
					else
					{
						RefreshOne(item, force: false);
					}
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
			finally
			{
				scratch.Clear();
				refreshing = false;
			}
		}

		internal void Observe(Keypad keypad, bool force)
		{
			if (disposed || (Object)(object)keypad == (Object)null)
			{
				return;
			}
			try
			{
				if (!keypads.TryGetValue(keypad, out var _))
				{
					if (keypads.Count >= 512)
					{
						return;
					}
					keypads.Add(keypad, new Quote());
				}
				if (force)
				{
					keypads[keypad].HasPrevious = false;
				}
				RefreshOne(keypad, force);
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		internal void Forget(Keypad keypad)
		{
			if (!disposed && !object.ReferenceEquals(keypad, null))
			{
				keypads.Remove(keypad);
			}
		}

		private void RefreshOne(Keypad keypad, bool force)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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)
			//IL_00a4: Invalid comparison between Unknown and I4
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || !NetworkServer.active || !canRefresh() || (Object)(object)keypad == (Object)null)
			{
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			object? value = ManagerInstance.GetValue(null);
			GameManager val = (GameManager)((value is GameManager) ? value : null);
			GameBase casinoGame = keypad.casinoGame;
			bool correctBinding = (Object)(object)casinoGame != (Object)null && object.ReferenceEquals(casinoGame.Keypad, keypad);
			if (BetDisplayPolicy.CanRefresh(enabled: true, server: true, ((Scene)(ref activeScene)).IsValid() && ((Scene)(ref activeScene)).isLoaded && ((Scene)(ref activeScene)).name == "CasinoScene", (Object)(object)val != (Object)null, (Object)(object)val != (Object)null && ((NetworkBehaviour)val).isServer && (int)val.state == 1, ((NetworkBehaviour)keypad).isServer && ((Component)keypad).gameObject.activeInHierarchy && ((Behaviour)keypad).isActiveAndEnabled, (Object)(object)casinoGame != (Object)null && ((NetworkBehaviour)casinoGame).isServer && ((Component)casinoGame).gameObject.activeInHierarchy, correctBinding) && keypads.TryGetValue(keypad, out var value2))
			{
				BigNumber minBet = casinoGame.MinBet;
				BigNumber maxBet = casinoGame.MaxBet;
				if (force || !object.ReferenceEquals(value2.Game, casinoGame) || BetDisplayPolicy.LimitsChanged(value2.HasPrevious, value2.Min, value2.MinExponent, value2.Max, value2.MaxExponent, minBet.Mantissa, minBet.Exponent, maxBet.Mantissa, maxBet.Exponent))
				{
					keypad.ServerUpdateMinMaxBetText(minBet, maxBet);
					value2.Game = casinoGame;
					value2.HasPrevious = true;
					value2.Min = minBet.Mantissa;
					value2.MinExponent = minBet.Exponent;
					value2.Max = maxBet.Mantissa;
					value2.MaxExponent = maxBet.Exponent;
				}
			}
		}

		internal void Render(Keypad keypad)
		{
			if (disposed || (Object)(object)keypad == (Object)null || !NetworkClient.active)
			{
				return;
			}
			try
			{
				if (canRefresh())
				{
					RequestRender.Invoke(keypad, null);
				}
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		private void Report(Exception error)
		{
			if (reported)
			{
				return;
			}
			reported = true;
			if (log == null)
			{
				return;
			}
			try
			{
				log("Native bet-label refresh held: " + error.GetBaseException().Message);
			}
			catch
			{
			}
		}

		private static bool NativeApiAvailable()
		{
			if (ManagerInstance != null && ManagerInstance.IsStatic && RequestRender != null && RequestRender.ReturnType == typeof(void) && AccessTools.PropertyGetter(typeof(GameBase), "MinBet") != null && AccessTools.PropertyGetter(typeof(GameBase), "MaxBet") != null && AccessTools.Method(typeof(Keypad), "ServerUpdateMinMaxBetText", new Type[2]
			{
				typeof(BigNumber),
				typeof(BigNumber)
			}, (Type[])null) != null && AccessTools.Method(typeof(Keypad), "UserCode_RpcSyncMinMaxBetLabels__Double__Int64__Double__Int64", new Type[4]
			{
				typeof(double),
				typeof(long),
				typeof(double),
				typeof(long)
			}, (Type[])null) != null)
			{
				return AccessTools.PropertySetter(typeof(GameManager), "currentQuota") != null;
			}
			return false;
		}

		public static string SelfTest()
		{
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Native bet-display API shape changed.");
			}
			if (!BetDisplayPolicy.CanRefresh(enabled: true, server: true, casino: true, gameManager: true, gameState: true, liveKeypad: true, liveGame: true, correctBinding: true) || BetDisplayPolicy.CanRefresh(enabled: true, server: false, casino: true, gameManager: true, gameState: true, liveKeypad: true, liveGame: true, correctBinding: true) || !BetDisplayPolicy.LimitsChanged(hasPrevious: true, 4.8, 1L, 4.8, 2L, 4.1, 1L, 4.1, 2L) || BetDisplayPolicy.LimitsChanged(hasPrevious: true, 4.1, 1L, 4.1, 2L, 4.1, 1L, 4.1, 2L))
			{
				throw new InvalidOperationException("Native bet-display refresh policy failed.");
			}
			return "PASS: native bet-display API and host quote refresh policy (offline)";
		}

		public void Dispose()
		{
			disposed = true;
			keypads.Clear();
			scratch.Clear();
			if (object.ReferenceEquals(Active, this))
			{
				Active = null;
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "set_currentQuota")]
	internal static class BetDisplayQuotaPatch
	{
		private static void Postfix()
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.RefreshAll();
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "StartDay")]
	internal static class BetDisplayDayPatch
	{
		private static void Postfix()
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.RefreshAll();
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "OnEnable")]
	internal static class BetDisplayEnablePatch
	{
		private static void Postfix(Keypad __instance)
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.Observe(__instance, force: true);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "OnCasinoGameSet")]
	internal static class BetDisplayBindingPatch
	{
		private static void Postfix(Keypad __instance)
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.Observe(__instance, force: true);
				ChaosBetDisplay.Active.Render(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "ApplyInput")]
	internal static class BetDisplayInputPatch
	{
		private static void Prefix(Keypad __instance)
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.Observe(__instance, force: false);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "OnDisable")]
	internal static class BetDisplayDisablePatch
	{
		private static void Prefix(Keypad __instance)
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.Forget(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Keypad), "UserCode_RpcSyncMinMaxBetLabels__Double__Int64__Double__Int64")]
	internal static class BetDisplayLabelsReceivedPatch
	{
		private static void Postfix(Keypad __instance)
		{
			if (ChaosBetDisplay.Active != null)
			{
				ChaosBetDisplay.Active.Render(__instance);
			}
		}
	}
	public enum BodyRepairDisposition
	{
		Eligible,
		InvalidIdentity,
		NotReady,
		IncompleteSetup,
		AlreadyWhole,
		TemporarilyBusy
	}
	public sealed class BodyRepairProof
	{
		public bool CurrentConnection;

		public bool Authenticated;

		public bool Ready;

		public bool OwnedSpawnedCharacter;

		public bool CurrentOrgans;

		public bool ActiveCharacter;

		public bool NativeReferencesReady;

		public bool NativeDataBound;

		public bool Sleeping;

		public bool Waking;

		public bool MovementLocked;

		public bool LeftEye;

		public bool RightEye;

		public bool Body;

		public bool Mouth;
	}
	public static class BodyRepairPolicy
	{
		public const int MaximumPlayers = 32;

		public static bool CanStart(bool enabled, bool server, bool actualHost, bool validPhase, bool partyReady, bool sceneReady, bool resetting, bool shredderProcessing)
		{
			if (enabled && server && actualHost && validPhase && partyReady && sceneReady && !resetting)
			{
				return !shredderProcessing;
			}
			return false;
		}

		public static BodyRepairDisposition Classify(BodyRepairProof proof)
		{
			if (proof == null || !proof.CurrentConnection || !proof.Authenticated || !proof.OwnedSpawnedCharacter || !proof.CurrentOrgans)
			{
				return BodyRepairDisposition.InvalidIdentity;
			}
			if (!proof.Ready || !proof.ActiveCharacter)
			{
				return BodyRepairDisposition.NotReady;
			}
			if (!proof.NativeReferencesReady || !proof.NativeDataBound)
			{
				return BodyRepairDisposition.IncompleteSetup;
			}
			if (proof.LeftEye && proof.RightEye && proof.Body && proof.Mouth)
			{
				return BodyRepairDisposition.AlreadyWhole;
			}
			if (proof.Sleeping || proof.Waking || proof.MovementLocked)
			{
				return BodyRepairDisposition.TemporarilyBusy;
			}
			return BodyRepairDisposition.Eligible;
		}
	}
	public sealed class ChaosBodyRepair : IDisposable
	{
		private sealed class NativeBusy
		{
			public bool Sleeping;

			public bool Locked;

			public float WakeUntil;
		}

		private sealed class Target
		{
			public NetworkConnectionToClient Connection;

			public PlayerOrgans Organs;

			public PlayerProfile Profile;
		}

		private sealed class Plan
		{
			public OrganManager Manager;

			public readonly List<Target> Targets = new List<Target>();

			public int Whole;

			public int Busy;

			public int Incomplete;
		}

		internal static ChaosBodyRepair Active;

		private readonly Func<bool> canUse;

		private readonly Func<List<NetworkConnectionToClient>> readyParty;

		private readonly Action<string> log;

		private readonly List<BodyShreddingMachine> machines = new List<BodyShreddingMachine>();

		private readonly Dictionary<PlayerController, NativeBusy> busy = new Dictionary<PlayerController, NativeBusy>();

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

		private bool disposed;

		private bool applying;

		private bool trackingOverflow;

		private static readonly FieldInfo ManagerInstance = AccessTools.Field(typeof(GameManager), "_instance");

		private static readonly FieldInfo OrganInstance = AccessTools.Field(typeof(OrganManager), "_instance");

		private static readonly FieldInfo Transitioning = AccessTools.Field(typeof(GameManager), "_isTransitioning");

		private static readonly FieldInfo SceneReady = AccessTools.Field(typeof(GameManager), "_scenePlayReady");

		private static readonly FieldInfo ExpectedPlayers = AccessTools.Field(typeof(GameManager), "_expectedScenePlayers");

		private static readonly FieldInfo SteamOrgans = AccessTools.Field(typeof(OrganManager), "OrganDataBySteamId");

		private static readonly FieldInfo LeftModel = AccessTools.Field(typeof(PlayerOrgans), "leftEyeModel");

		private static readonly FieldInfo RightModel = AccessTools.Field(typeof(PlayerOrgans), "rightEyeModel");

		private static readonly FieldInfo BodyModel = AccessTools.Field(typeof(PlayerOrgans), "bodyModel");

		private static readonly FieldInfo MouthModel = AccessTools.Field(typeof(PlayerOrgans), "mouthModel");

		private static readonly FieldInfo OrganController = AccessTools.Field(typeof(PlayerOrgans), "_pc");

		private static readonly FieldInfo OrganProfile = AccessTools.Field(typeof(PlayerOrgans), "_pp");

		private static readonly FieldInfo OrganRigidbody = AccessTools.Field(typeof(PlayerOrgans), "_rb");

		private static readonly FieldInfo OrganDrag = AccessTools.Field(typeof(PlayerOrgans), "_cd");

		private static readonly FieldInfo OrganSettings = AccessTools.Field(typeof(PlayerOrgans), "_ps");

		private static readonly FieldInfo OrganEyesUI = AccessTools.Field(typeof(PlayerOrgans), "_pe");

		private static readonly FieldInfo LocalWaking = AccessTools.Field(typeof(PlayerController), "_isWakingUp");

		private static readonly FieldInfo Processing = AccessTools.Field(typeof(BodyShreddingMachine), "_isProcessing");

		public ChaosBodyRepair(Func<bool> nativeFeaturesAllowed, Func<List<NetworkConnectionToClient>> readyNativeParty, Action<string> logger)
		{
			if (nativeFeaturesAllowed == null || readyNativeParty == null)
			{
				throw new ArgumentNullException("Body repair guards");
			}
			if (Active != null)
			{
				throw new InvalidOperationException("Body repair already initialized.");
			}
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Verified native body repair API is unavailable.");
			}
			canUse = nativeFeaturesAllowed;
			readyParty = readyNativeParty;
			log = logger;
			Active = this;
		}

		public bool CanRepair(out string note)
		{
			try
			{
				Plan plan;
				return BuildPlan(out plan, out note);
			}
			catch (Exception error)
			{
				Report("body repair availability", error);
				note = "Player organ data is not ready yet.";
				return false;
			}
		}

		public bool TryRepair(out string note)
		{
			if (!BuildPlanSafe(out var plan, out note))
			{
				return false;
			}
			applying = true;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			try
			{
				foreach (Target target in plan.Targets)
				{
					try
					{
						if (!FeaturesEnabled() || !CurrentOwned(target.Connection, target.Profile, target.Organs))
						{
							num3++;
							continue;
						}
						BodyRepairProof proof = Proof(plan.Manager, target.Connection, target.Profile, target.Organs);
						if (BodyRepairPolicy.Classify(proof) != BodyRepairDisposition.Eligible)
						{
							num3++;
							continue;
						}
						plan.Manager.SetOrganDataBySteamId(target.Profile.steamId, true, true, true, true);
						if (BoundData(plan.Manager, target.Connection, target.Profile, target.Organs, out var steam, out var current2) && Whole(steam) && Whole(current2))
						{
							num++;
							continue;
						}
						num2++;
						Report("body repair confirmation", new InvalidOperationException("Native organ records did not confirm this character's repair."));
					}
					catch (Exception error)
					{
						num2++;
						Report("native body repair", error);
					}
				}
			}
			finally
			{
				applying = false;
			}
			note = "Repaired body parts for " + num + " player" + ((num == 1) ? "" : "s") + ".";
			if (plan.Whole > 0)
			{
				object obj = note;
				note = string.Concat(obj, " ", plan.Whole, " already whole.");
			}
			if (plan.Busy + num3 > 0)
			{
				object obj2 = note;
				note = string.Concat(obj2, " ", plan.Busy + num3, " skipped while busy or changing state.");
			}
			if (plan.Incomplete > 0)
			{
				object obj3 = note;
				note = string.Concat(obj3, " ", plan.Incomplete, " skipped until organ data finishes syncing.");
			}
			if (num2 > 0)
			{
				object obj4 = note;
				note = string.Concat(obj4, " ", num2, " could not be confirmed.");
			}
			if (num > 0)
			{
				Write(note);
			}
			return num > 0;
		}

		private bool BuildPlanSafe(out Plan plan, out string note)
		{
			try
			{
				return BuildPlan(out plan, out note);
			}
			catch (Exception error)
			{
				plan = null;
				note = "Player organ data is not ready yet.";
				Report("body repair preflight", error);
				return false;
			}
		}

		private bool BuildPlan(out Plan plan, out string note)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Invalid comparison between Unknown and I4
			plan = null;
			note = "Only the current host can repair the party.";
			if (disposed || applying || !NetworkServer.active || NetworkServer.localConnection == null)
			{
				return false;
			}
			NetworkConnectionToClient localConnection = (NetworkConnectionToClient)(object)NetworkServer.localConnection;
			if ((Object)(object)((NetworkConnection)localConnection).identity == (Object)null || !object.ReferenceEquals(NetworkClient.localPlayer, ((NetworkConnection)localConnection).identity))
			{
				return false;
			}
			note = "Wait until every player is loaded and the room is compatible.";
			if (!FeaturesEnabled())
			{
				return false;
			}
			List<NetworkConnectionToClient> list = readyParty();
			if (!ExactParty(list, localConnection))
			{
				return false;
			}
			object? value = ManagerInstance.GetValue(null);
			GameManager val = (GameManager)((value is GameManager) ? value : null);
			object? value2 = OrganInstance.GetValue(null);
			OrganManager val2 = (OrganManager)((value2 is OrganManager) ? value2 : null);
			Scene activeScene = SceneManager.GetActiveScene();
			bool flag = (Object)(object)val != (Object)null && ((NetworkBehaviour)val).isServer && (Object)(object)val2 != (Object)null && ((NetworkBehaviour)val2).isServer && ((((Scene)(ref activeScene)).name == "HomeScene" && (int)val.state == 0) || (((Scene)(ref activeScene)).name == "CasinoScene" && (int)val.state == 1));
			note = "Wait until native scene loading or the reset finishes.";
			if (!flag || (bool)Transitioning.GetValue(val) || !NativeSceneReady(val, list))
			{
				return false;
			}
			Prune(list);
			note = "Wait until the body-part machine finishes processing.";
			if (trackingOverflow || RemovalBusy())
			{
				return false;
			}
			if (!BodyRepairPolicy.CanStart(enabled: true, server: true, actualHost: true, flag, partyReady: true, sceneReady: true, resetting: false, shredderProcessing: false))
			{
				return false;
			}
			plan = new Plan
			{
				Manager = val2
			};
			foreach (NetworkConnectionToClient item in list)
			{
				PlayerProfile component = ((Component)((NetworkConnection)item).identity).GetComponent<PlayerProfile>();
				PlayerOrgans component2 = ((Component)((NetworkConnection)item).identity).GetComponent<PlayerOrgans>();
				switch (BodyRepairPolicy.Classify(Proof(val2, item, component, component2)))
				{
				case BodyRepairDisposition.Eligible:
					plan.Targets.Add(new Target
					{
						Connection = item,
						Profile = component,
						Organs = component2
					});
					break;
				case BodyRepairDisposition.AlreadyWhole:
					plan.Whole++;
					break;
				case BodyRepairDisposition.TemporarilyBusy:
					plan.Busy++;
					break;
				default:
					plan.Incomplete++;
					break;
				}
			}
			if (plan.Targets.Count > 0)
			{
				note = "Repair missing body parts for " + plan.Targets.Count + " player" + ((plan.Targets.Count == 1) ? "" : "s") + ".";
				if (plan.Busy + plan.Incomplete > 0)
				{
					object obj = note;
					note = string.Concat(obj, " ", plan.Busy + plan.Incomplete, " will be skipped until ready.");
				}
				return true;
			}
			note = ((plan.Busy > 0) ? "Wait for sleeping, waking or movement-locked players to finish before repairing them." : ((plan.Incomplete > 0) ? "Wait until current player organ data finishes syncing." : "Every current player already has all body parts."));
			return false;
		}

		private BodyRepairProof Proof(OrganManager manager, NetworkConnectionToClient connection, PlayerProfile profile, PlayerOrgans organs)
		{
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Invalid comparison between Unknown and I4
			BodyRepairProof bodyRepairProof = new BodyRepairProof();
			bodyRepairProof.CurrentConnection = LiveConnection(connection);
			bodyRepairProof.Authenticated = ((NetworkConnection)(connection?)).isAuthenticated ?? false;
			bodyRepairProof.Ready = ((NetworkConnection)(connection?)).isReady ?? false;
			bodyRepairProof.OwnedSpawnedCharacter = CurrentOwned(connection, profile, organs);
			bodyRepairProof.CurrentOrgans = bodyRepairProof.OwnedSpawnedCharacter && (Object)(object)organs != (Object)null && ((NetworkBehaviour)organs).isServer;
			bodyRepairProof.ActiveCharacter = (Object)(object)organs != (Object)null && ((Behaviour)organs).isActiveAndEnabled && ((Component)organs).gameObject.activeInHierarchy;
			if (!bodyRepairProof.OwnedSpawnedCharacter)
			{
				return bodyRepairProof;
			}
			PlayerController component = ((Component)((NetworkConnection)connection).identity).GetComponent<PlayerController>();
			bodyRepairProof.NativeReferencesReady = NativeReferences(((NetworkConnection)connection).identity, profile, organs, component);
			bodyRepairProof.NativeDataBound = BoundData(manager, connection, profile, organs, out var steam, out var _);
			if (bodyRepairProof.NativeDataBound)
			{
				bodyRepairProof.LeftEye = steam.leftEye;
				bodyRepairProof.RightEye = steam.rightEye;
				bodyRepairProof.Body = steam.body;
				bodyRepairProof.Mouth = steam.mouth;
			}
			if ((Object)(object)component != (Object)null && busy.TryGetValue(component, out var value))
			{
				bodyRepairProof.Sleeping = value.Sleeping;
				bodyRepairProof.Waking = !Finite(Time.time) || Time.time < value.WakeUntil;
				bodyRepairProof.MovementLocked = value.Locked;
			}
			if ((Object)(object)component != (Object)null && ((NetworkBehaviour)component).isLocalPlayer)
			{
				bodyRepairProof.Waking |= (bool)LocalWaking.GetValue(component);
				bodyRepairProof.MovementLocked |= component.IsLocked;
			}
			if ((Object)(object)component != (Object)null)
			{
				bodyRepairProof.MovementLocked |= (int)component.State == 2;
			}
			return bodyRepairProof;
		}

		private static bool NativeReferences(NetworkIdentity identity, PlayerProfile profile, PlayerOrgans organs, PlayerController controller)
		{
			if ((Object)(object)identity == (Object)null || (Object)(object)profile == (Object)null || (Object)(object)organs == (Object)null || (Object)(object)controller == (Object)null || (Object)(object)controller.head == (Object)null || !object.ReferenceEquals(OrganController.GetValue(organs), controller) || !object.ReferenceEquals(OrganProfile.GetValue(organs), profile) || !object.ReferenceEquals(OrganRigidbody.GetValue(organs), ((Component)identity).GetComponent<Rigidbody>()) || (Object)(object)((Component)identity).GetComponent<Rigidbody>() == (Object)null || !object.ReferenceEquals(OrganDrag.GetValue(organs), ((Component)identity).GetComponent<CustomDrag>()) || (Object)(object)((Component)identity).GetComponent<CustomDrag>() == (Object)null || (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null || (Object)(object)((Component)identity).GetComponent<PlayerMouth>() == (Object)null)
			{
				return false;
			}
			object? value = LeftModel.GetValue(organs);
			GameObject model = (GameObject)((value is GameObject) ? value : null);
			object? value2 = RightModel.GetValue(organs);
			GameObject model2 = (GameObject)((value2 is GameObject) ? value2 : null);
			object? value3 = BodyModel.GetValue(organs);
			GameObject model3 = (GameObject)((value3 is GameObject) ? value3 : null);
			object? value4 = MouthModel.GetValue(organs);
			GameObject val = (GameObject)((value4 is GameObject) ? value4 : null);
			if (OwnModel(model, identity) && OwnModel(model2, identity) && OwnModel(model3, identity) && OwnModel(val, identity) && (Object)(object)val.GetComponent<MeshRenderer>() != (Object)null && ((Component)controller.head).transform.IsChildOf(((Component)identity).transform))
			{
				if (((NetworkBehaviour)organs).isLocalPlayer)
				{
					object? value5 = OrganEyesUI.GetValue(organs);
					return (Object)((value5 is PlayerEyesUI) ? value5 : null) != (Object)null;
				}
				return true;
			}
			return false;
		}

		private static bool OwnModel(GameObject model, NetworkIdentity identity)
		{
			if ((Object)(object)model != (Object)null)
			{
				return model.transform.IsChildOf(((Component)identity).transform);
			}
			return false;
		}

		private static bool BoundData(OrganManager manager, NetworkConnectionToClient connection, PlayerProfile profile, PlayerOrgans organs, out PlayerOrganData steam, out PlayerOrganData current)
		{
			steam = null;
			current = null;
			Dictionary<ulong, PlayerOrganData> dictionary = (((Object)(object)manager == (Object)null) ? null : (SteamOrgans.GetValue(manager) as Dictionary<ulong, PlayerOrganData>));
			if (dictionary != null && manager.OrganData != null && (Object)(object)profile != (Object)null && profile.steamId != 0 && connection != null && dictionary.TryGetValue(profile.steamId, out steam) && steam != null && manager.OrganData.TryGetValue(connection.connectionId, out current) && current != null && object.ReferenceEquals(steam.organsReference, organs) && object.ReferenceEquals(current.organsReference, organs) && steam.leftEye == current.leftEye && steam.rightEye == current.rightEye && steam.body == current.body)
			{
				return steam.mouth == current.mouth;
			}
			return false;
		}

		private static bool Whole(PlayerOrganData data)
		{
			if (data != null && data.leftEye && data.rightEye && data.body)
			{
				return data.mouth;
			}
			return false;
		}

		private static bool Finite(float number)
		{
			if (!float.IsNaN(number))
			{
				return !float.IsInfinity(number);
			}
			return false;
		}

		private static bool CurrentOwned(NetworkConnectionToClient connection, PlayerProfile profile, PlayerOrgans organs)
		{
			if (LiveConnection(connection) && ((NetworkConnection)connection).isAuthenticated && ((NetworkConnection)connection).isReady && (Object)(object)((NetworkConnection)connection).identity != (Object)null && ((NetworkConnection)connection).identity.netId != 0 && ((NetworkConnection)connection).identity.isServer && (Object)(object)profile != (Object)null && profile.steamId != 0 && (Object)(object)organs != (Object)null && object.ReferenceEquals(((NetworkConnection)connection).identity.connectionToClient, connection) && NetworkServer.spawned.TryGetValue(((NetworkConnection)connection).identity.netId, out var value) && object.ReferenceEquals(value, ((NetworkConnection)connection).identity) && object.ReferenceEquals(((NetworkBehaviour)profile).netIdentity, ((NetworkConnection)connection).identity) && object.ReferenceEquals(((NetworkBehaviour)organs).netIdentity, ((NetworkConnection)connection).identity) && object.ReferenceEquals(((Component)((NetworkConnection)connection).identity).GetComponent<PlayerProfile>(), profile))
			{
				return object.ReferenceEquals(((Component)((NetworkConnection)connection).identity).GetComponent<PlayerOrgans>(), organs);
			}
			return false;
		}

		private static bool LiveConnection(NetworkConnectionToClient connection)
		{
			if (connection != null && NetworkServer.connections.TryGetValue(connection.connectionId, out var value))
			{
				return object.ReferenceEquals(value, connection);
			}
			return false;
		}

		private static bool ExactParty(List<NetworkConnectionToClient> party, NetworkConnectionToClient host)
		{
			if (party == null || party.Count < 1 || party.Count > 32 || party.Count != NetworkServer.connections.Count || !party.Contains(host))
			{
				return false;
			}
			HashSet<int> hashSet = new HashSet<int>();
			HashSet<ulong> hashSet2 = new HashSet<ulong>();
			foreach (NetworkConnectionToClient item in party)
			{
				if (!LiveConnection(item) || (Object)(object)((NetworkConnection)item).identity == (Object)null || !hashSet.Add(item.connectionId))
				{
					return false;
				}
				PlayerProfile component = ((Component)((NetworkConnection)item).identity).GetComponent<PlayerProfile>();
				if (!CurrentOwned(item, component, ((Component)((NetworkConnection)item).identity).GetComponent<PlayerOrgans>()) || !hashSet2.Add(component.steamId))
				{
					return false;
				}
			}
			return true;
		}

		private static bool NativeSceneReady(GameManager manager, List<NetworkConnectionToClient> party)
		{
			HashSet<int> hashSet = SceneReady.GetValue(manager) as HashSet<int>;
			if ((int)ExpectedPlayers.GetValue(manager) <= 0 || hashSet == null || hashSet.Count < party.Count)
			{
				return false;
			}
			foreach (NetworkConnectionToClient item in party)
			{
				if (!hashSet.Contains(item.connectionId))
				{
					return false;
				}
			}
			return true;
		}

		internal void RegisterMachine(BodyShreddingMachine machine)
		{
			if (disposed || (Object)(object)machine == (Object)null)
			{
				return;
			}
			for (int num = machines.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)machines[num] == (Object)null)
				{
					machines.RemoveAt(num);
				}
			}
			if (!machines.Contains(machine))
			{
				if (machines.Count < 32)
				{
					machines.Add(machine);
				}
				else
				{
					trackingOverflow = true;
				}
			}
		}

		internal void NativeServerStopped()
		{
			machines.Clear();
			busy.Clear();
			trackingOverflow = false;
		}

		private bool RemovalBusy()
		{
			for (int num = machines.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)machines[num] == (Object)null)
				{
					machines.RemoveAt(num);
				}
				else if (((NetworkBehaviour)machines[num]).isServer && ((Component)machines[num]).gameObject.activeInHierarchy && (bool)Processing.GetValue(machines[num]))
				{
					return true;
				}
			}
			return false;
		}

		internal void NativeLock(PlayerController controller, bool locked)
		{
			NativeBusy nativeBusy = BusyState(controller);
			if (nativeBusy != null)
			{
				nativeBusy.Locked = locked;
			}
		}

		internal void NativeBed(PlayerController controller)
		{
			NativeBusy nativeBusy = BusyState(controller);
			if (nativeBusy != null)
			{
				nativeBusy.Sleeping = true;
				nativeBusy.Locked = true;
			}
		}

		internal void NativeWake(PlayerController controller)
		{
			NativeBusy nativeBusy = BusyState(controller);
			if (nativeBusy != null)
			{
				nativeBusy.Sleeping = false;
				nativeBusy.Locked = false;
				nativeBusy.WakeUntil = (Finite(Time.time) ? (Time.time + 3.25f) : float.PositiveInfinity);
			}
		}

		private NativeBusy BusyState(PlayerController controller)
		{
			if (disposed || !NetworkServer.active || (Object)(object)controller == (Object)null)
			{
				return null;
			}
			if (busy.TryGetValue(controller, out var value))
			{
				return value;
			}
			List<PlayerController> list = null;
			foreach (PlayerController key in busy.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					if (list == null)
					{
						list = new List<PlayerController>();
					}
					list.Add(key);
				}
			}
			if (list != null)
			{
				foreach (PlayerController item in list)
				{
					busy.Remove(item);
				}
			}
			if (busy.Count >= 64)
			{
				trackingOverflow = true;
				return null;
			}
			value = new NativeBusy();
			busy.Add(controller, value);
			return value;
		}

		private void Prune(List<NetworkConnectionToClient> party)
		{
			HashSet<PlayerController> hashSet = new HashSet<PlayerController>();
			foreach (NetworkConnectionToClient item in party)
			{
				PlayerController component = ((Component)((NetworkConnection)item).identity).GetComponent<PlayerController>();
				if ((Object)(object)component != (Object)null)
				{
					hashSet.Add(component);
				}
			}
			List<PlayerController> list = null;
			foreach (PlayerController key in busy.Keys)
			{
				if ((Object)(object)key == (Object)null || !hashSet.Contains(key))
				{
					if (list == null)
					{
						list = new List<PlayerController>();
					}
					list.Add(key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (PlayerController item2 in list)
			{
				busy.Remove(item2);
			}
		}

		private bool FeaturesEnabled()
		{
			if (disposed)
			{
				return false;
			}
			try
			{
				return canUse();
			}
			catch (Exception error)
			{
				Report("body repair guard", error);
				return false;
			}
		}

		private void Write(string message)
		{
			if (log != null)
			{
				try
				{
					log(message);
				}
				catch
				{
				}
			}
		}

		private void Report(string operation, Exception error)
		{
			if (errors.Count < 8 && errors.Add(operation))
			{
				Write("Native " + operation + " held: " + error.GetBaseException().Message);
			}
		}

		private static bool NativeApiAvailable()
		{
			if (ManagerInstance != null && ManagerInstance.IsStatic && OrganInstance != null && OrganInstance.IsStatic && Transitioning != null && SceneReady != null && ExpectedPlayers != null && SteamOrgans != null && LeftModel != null && RightModel != null && BodyModel != null && MouthModel != null && OrganController != null && OrganProfile != null && OrganRigidbody != null && OrganDrag != null && OrganSettings != null && OrganEyesUI != null && LocalWaking != null && Processing != null && AccessTools.Method(typeof(OrganManager), "SetOrganDataBySteamId", new Type[5]
			{
				typeof(ulong),
				typeof(bool),
				typeof(bool),
				typeof(bool),
				typeof(bool)
			}, (Type[])null) != null && AccessTools.Method(typeof(PlayerController), "ServerLock", new Type[1] { typeof(bool) }, (Type[])null) != null && AccessTools.Method(typeof(PlayerController), "ServerWakeUp", Type.EmptyTypes, (Type[])null) != null && AccessTools.Method(typeof(SpawnBoxPlayerRagdollTrigger), "AssignPlayer", new Type[1] { typeof(PlayerController) }, (Type[])null) != null && AccessTools.Method(typeof(BodyShreddingMachine), "OnStartClient", Type.EmptyTypes, (Type[])null) != null)
			{
				return AccessTools.Method(typeof(CustomNetworkManager), "OnStopServer", Type.EmptyTypes, (Type[])null) != null;
			}
			return false;
		}

		public static string SelfTest()
		{
			if (!NativeApiAvailable())
			{
				throw new InvalidOperationException("Native body repair API shape changed.");
			}
			BodyRepairProof bodyRepairProof = new BodyRepairProof();
			bodyRepairProof.CurrentConnection = true;
			bodyRepairProof.Authenticated = true;
			bodyRepairProof.Ready = true;
			bodyRepairProof.OwnedSpawnedCharacter = true;
			bodyRepairProof.CurrentOrgans = true;
			bodyRepairProof.ActiveCharacter = true;
			bodyRepairProof.NativeReferencesReady = true;
			bodyRepairProof.NativeDataBound = true;
			bodyRepairProof.LeftEye = true;
			bodyRepairProof.RightEye = true;
			bodyRepairProof.Mouth = true;
			bodyRepairProof.Body = false;
			BodyRepairProof proof = bodyRepairProof;
			if (BodyRepairPolicy.Classify(proof) != BodyRepairDisposition.Eligible || BodyRepairPolicy.CanStart(enabled: true, server: false, actualHost: true, validPhase: true, partyReady: true, sceneReady: true, resetting: false, shredderProcessing: false))
			{
				throw new InvalidOperationException("Body repair policy failed.");
			}
			return "PASS: native scoped body repair API and host/missing-body policy (offline)";
		}

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				machines.Clear();
				busy.Clear();
				if (object.ReferenceEquals(Active, this))
				{
					Active = null;
				}
			}
		}
	}
	[HarmonyPatch(typeof(BodyShreddingMachine), "OnStartClient")]
	internal static class BodyRepairMachinePatch
	{
		private static void Postfix(BodyShreddingMachine __instance)
		{
			if (ChaosBodyRepair.Active != null)
			{
				ChaosBodyRepair.Active.RegisterMachine(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "ServerLock")]
	internal static class BodyRepairLockPatch
	{
		private static void Postfix(PlayerController __instance, bool __0)
		{
			if (ChaosBodyRepair.Active != null)
			{
				ChaosBodyRepair.Active.NativeLock(__instance, __0);
			}
		}
	}
	[HarmonyPatch(typeof(SpawnBoxPlayerRagdollTrigger), "AssignPlayer")]
	internal static class BodyRepairBedPatch
	{
		private static void Prefix(PlayerController __0)
		{
			if (ChaosBodyRepair.Active != null)
			{
				ChaosBodyRepair.Active.NativeBed(__0);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "ServerWakeUp")]
	internal static class BodyRepairWakePatch
	{
		private static void Postfix(PlayerController __instance)
		{
			if (ChaosBodyRepair.Active != null)
			{
				ChaosBodyRepair.Active.NativeWake(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(CustomNetworkManager), "OnStopServer")]
	internal static class BodyRepairServerStopPatch
	{
		private static void Postfix()
		{
			if (ChaosBodyRepair.Active != null)
			{
				ChaosBodyRepair.Active.NativeServerStopped();
			}
		}
	}
}
namespace ChaosNight.Core
{
	[Serializable]
	public sealed class Rules
	{
		public bool Enabled = true;

		public int Intensity = 4;

		public double JackpotPercent = 3.0;

		public bool ContractsEnabled = true;

		public bool ClosingTimeEnabled = true;

		public double MaxBonusQuotaFraction = 0.35;

		public int Seed;

		public string DisabledModifiers = "";

		public bool PurchasesSupported;

		public int MaxTicketsPerDay = 3;

		public int ChapterRounds = 4;

		public bool NegativesEnabled = true;

		public double NegativeDayPercent = 50.0;

		public bool TimedNegativesEnabled;

		public int PenaltySeverity = 2;
	}
	public enum RewardKind
	{
		Cash,
		Tickets,
		QuotaFactor,
		ClosingTimeFactor,
		Notice,
		PenaltyCash
	}
	[Serializable]
	public sealed class Reward
	{
		public string Id;

		public RewardKind Kind;

		public double Amount;

		public string Description;

		public ulong Player;

		public int Day;

		public int Round;

		public double CashMantissa;

		public long CashExponent;

		public bool CashBound;

		public ulong Actor;

		public string ActorName;

		public bool TaskCompleted;

		public bool NpcKill;

		public double ReservedAmount;
	}
	[Serializable]
	public sealed class NegativeProgress
	{
		public string Id;

		public string Name;

		public string Description;

		public string PairedModifierId;

		public string RiskBasis;

		public string PenaltyId;

		public int Required;

		public int Count;

		public double ProfitTarget;

		public double Profit;

		public double BaseRiskRatio;

		public double RiskRatio;

		public double DeadlineSeconds;

		public double RemainingSeconds;

		public bool Timed;

		public bool Completed;

		public bool Failed;

		public bool WaivedCompletion;

		public ulong Completer;

		public ulong[] Roster = new ulong[0];

		public List<ulong> Participants = new List<ulong>();

		public List<int> Games = new List<int>();
	}
	[Serializable]
	public sealed class ResultInput
	{
		public long Sequence;

		public ulong Player;

		public int Game;

		public double BetRatio;

		public double PayoutRatio;

		public double BalanceRatio;

		public int EffectsMask = -1;

		public int FeaturedGameAtStart = -1;
	}
	[Serializable]
	public sealed class Choice
	{
		public string Id;

		public string Name;

		public string Description;

		public string Kind;
	}
	[Serializable]
	public sealed class PlayerProgress
	{
		public ulong Id;

		public int Plays;

		public int Wins;

		public double Profit;
	}
	[Serializable]
	public sealed class GameProgress
	{
		public int Id;

		public int Plays;

		public int Wins;

		public double Profit;
	}
	[Serializable]
	public sealed class Cooldown
	{
		public string Id;

		public int Round;
	}
	[Serializable]
	public sealed class RunState
	{
		public int Version = 1;

		public string RunId;

		public uint RandomState;

		public Rules ActiveRules;

		public int Day = -1;

		public int Floor = -1;

		public int Round;

		public int Chapter = 1;

		public bool DayOpen;

		public bool DayEnded;

		public bool DayEnabled;

		public bool BetItAllUsed;

		public int BetItAllConsumedDay = -1;

		public ulong[] Players = new ulong[0];

		public int[] AvailableGames = new int[0];

		public List<PlayerProgress> PlayerProgress = new List<PlayerProgress>();

		public List<GameProgress> GameProgress = new List<GameProgress>();

		public List<string> ModifierIds = new List<string>();

		public List<string> ModifierNames = new List<string>();

		public List<string> ModifierDescriptions = new List<string>();

		public List<string> ModifierDeck = new List<string>();

		public List<string> ContractDeck = new List<string>();

		public List<Cooldown> ModifierCooldowns = new List<Cooldown>();

		public List<string> PerkIds = new List<string>();

		public List<string> PerkNames = new List<string>();

		public List<string> PerkDescriptions = new List<string>();

		public List<Choice> Choices = new List<Choice>();

		public int ChoiceRound;

		public int PendingPerkDrafts;

		public string ContractId = "";

		public string ContractName = "";

		public string ContractDescription = "";

		public string ContractProgress = "";

		public double ContractTarget;

		public bool ContractCompleted;

		public int ContractFeaturedGame;

		public double QuotaFactor = 1.0;

		public double ClosingTimeFactor = 1.0;

		public double CashAwarded;

		public int DeliveryVersion;

		public bool LegacyCash;

		public double CashDelivered;

		public List<Reward> PendingCashRewards = new List<Reward>();

		public List<Reward> CashReceipts = new List<Reward>();

		public int NegativeVersion;

		public uint NegativeRandomState;

		public int NegativeScheduleVersion;

		public uint NegativeDayRandomState;

		public bool NegativeDayChosen;

		public double NegativeDaySeconds;

		public double NegativeElapsedSeconds;

		public bool NegativesResolved;

		public List<NegativeProgress> Negatives = new List<NegativeProgress>();

		public List<string> NegativeDeck = new List<string>();

		public List<Cooldown> NegativeCooldowns = new List<Cooldown>();

		public double PenaltyReserved;

		public double PenaltyDelivered;

		public List<Reward> PendingPenaltyRewards = new List<Reward>();

		public List<Reward> PenaltyReceipts = new List<Reward>();

		public List<string> KilledNpcKeys = new List<string>();

		public double NpcCashReserved;

		public double NpcCashDelivered;

		public int TicketsAwarded;

		public double NativeProfit;

		public double NativeLosses;

		public double PeakBalance;

		public double LowBalance;

		public double LastBalance;

		public bool BalanceKnown;

		public double Purchases;

		public int QualifyingPlays;

		public int SmallStakePlays;

		public double SmallStakeProfit;

		public int HighRollerPlays;

		public int WinningStreak;

		public int BestDayStreak;

		public bool HadSetback;

		public bool ComebackCompleted;

		public bool RelayCompleted;

		public bool VarietyCompleted;

		public bool StreakCompleted;

		public bool RiskDividendCompleted;

		public bool SafetyFundUsed;

		public bool RescueUsed;

		public int FeaturedGame;

		public int RotationPlays;

		public bool JackpotChecked;

		public bool GoldenPayoutPending;

		public int LastJackpotRound = -100;

		public int WildcardChapter = -1;

		public int SecondOpinionChapter = -1;

		public int SecondDrawChapter = -1;

		public int HouseCreditChapter = -1;

		public long LastSequence;

		public long AwardSequence;

		public List<string> RewardIds = new List<string>();

		public List<string> AppliedRewardIds = new List<string>();

		public List<string> DiscoveredModifiers = new List<string>();

		public List<string> DiscoveredJackpots = new List<string>();

		public List<string> CompletedContractIds = new List<string>();

		public List<string> Medals = new List<string>();

		public List<string> RecentEvents = new List<string>();

		public int DaysSurvived;

		public int ContractsCompleted;

		public int JackpotsFound;

		public int BestRunStreak;

		public double BiggestRecoveryRatio;

		public string NextModifierPreview = "";
	}
	public sealed class Definition
	{
		public readonly string Id;

		public readonly string Name;

		public readonly string Description;

		public Definition(string id, string name, string description)
		{
			Id = id;
			Name = name;
			Description = description;
		}
	}
	public sealed class Engine
	{
		public const double MinimumStake = 0.001;

		public const int GoldenPayoutBit = 1073741824;

		public const int MaximumPendingCashRewards = 256;

		public const int MaximumCashReceipts = 64;

		public const int MaximumPenaltyReceipts = 64;

		public const int MaximumNpcKillsPerDay = 512;

		public RunState State;

		private Rules pendingRules;

		private ulong resultActor;

		public static readonly Definition[] Modifiers = new Definition[17]
		{
			new Definition("house_match", "House Match", "Earn 20% extra on casino profit. Today's cash target is 10% higher."),
			new Definition("closing_time", "Closing Time", "20% less time, 20% less cash to earn."),
			new Definition("pressure_cooker", "Pressure Cooker", "Beat today's higher cash target for 1 ticket."),
			new Definition("breather", "Breather", "An easier day: 15% less cash to earn."),
			new Definition("hot_table", "Hot Table", "Win at the featured game for 25% extra profit."),
			new Definition("variety_show", "Variety Show", "Win on 3 different game types, or all available types if fewer."),
			new Definition("crew_relay", "Crew Relay", "Everyone finishes 1 casino game. Playing solo? Finish 3."),
			new Definition("risk_dividend", "Risk Dividend", "Finish 3 larger bets for 1 ticket. The menu shows the minimum bet."),
			new Definition("lucky_rotation", "Lucky Rotation", "Win at the featured game for 20% extra profit. It changes after 4 finished games."),
			new Definition("rebate_night", "Rebate Night", "Get 10% of casino losses back."),
			new Definition("comeback_night", "Comeback Night", "After shared cash drops, win it back for a bonus. The menu shows the amount."),
			new Definition("streak_night", "Streak Night", "Win 3 crew games in a row for a cash bonus."),
			new Definition("ticket_rush", "Ticket Rush", "Meet today's cash target for 2 tickets."),
			new Definition("frugal_friends", "Frugal Friends", "Meet today's target without overspending in the shop. The menu shows your ticket budget."),
			new Definition("plain_luck", "Plain Luck", "Perks paused today. Meet the cash target for a new perk choice."),
			new Definition("double_trouble", "Double Trouble", "An extra modifier today. Meet the cash target for 1 extra ticket."),
			new Definition("bet_it_all", "BET IT ALL", "Bet all shared cash on one casino game. One use for the crew today; refunds don't restore it.")
		};

		public static readonly Definition[] Contracts = new Definition[12]
		{
			new Definition("casino_tour", "Casino Tour", "Play 3 different game types, or all available types if fewer."),
			new Definition("everybody_in", "Everybody In", "Everyone finishes 1 casino game. Playing solo? Finish 3."),
			new Definition("profit_ladder", "Profit Ladder", "Make today's casino profit goal after losses."),
			new Definition("recovery_crew", "Recovery Crew", "After shared cash drops, win it back."),
			new Definition("budget_night", "Budget Night", "Meet today's target and stay within the shop ticket budget."),
			new Definition("small_stakes", "Small Stakes", "Make casino profit with small bets. The menu shows the bet limit."),
			new Definition("high_roller", "High Roller", "Finish 3 larger bets. The menu shows the minimum bet."),
			new Definition("team_streak", "Team Streak", "Win 3 crew games in a row."),
			new Definition("clean_run", "Clean Run", "Meet today's target without rebates, Safety Fund, House Refund or Debt Relief."),
			new Definition("featured_game", "Featured Game", "Make today's profit goal at the featured game."),
			new Definition("strong_finish", "Strong Finish", "Beat a bigger casino profit goal after losses."),
			new Definition("balanced_crew", "Balanced Crew", "2 different players each win a game. Playing solo? Win 2.")
		};

		public static readonly Definition[] Perks = new Definition[8]
		{
			new Definition("scout", "Scout", "See the next day's modifier."),
			new Definition("wildcard", "Wildcard", "Reroll today's modifiers before betting. Once per chapter; Closing Time stays locked."),
			new Definition("second_opinion", "Second Opinion", "Reroll today's bonus goal before betting. Once per chapter."),
			new Definition("house_credit", "House Credit", "Your first completed goal each chapter gets an extra cash bonus."),
			new Definition("safety_fund", "Safety Fund", "Get 15% of the crew's first casino loss back each day."),
			new Definition("team_dividend", "Team Dividend", "Bonus goals pay 25% more cash."),
			new Definition("second_draw", "Second Draw", "Reroll an open perk choice once per chapter."),
			new Definition("negotiator", "Negotiator", "Choose between 2 bonus goals before betting at each chapter start.")
		};

		public static readonly Definition[] Jackpots = new Definition[5]
		{
			new Definition("vault", "The Vault Opens", "Choose cash, up to 3 tickets, or a new perk."),
			new Definition("golden_payout", "Golden Payout", "The crew's next new winning bet gets matching profit, within today's bonus cap."),
			new Definition("house_refund", "House Refund", "Get half of today's casino losses back."),
			new Definition("lucky_shipment", "Lucky Shipment", "Choose another crew perk."),
			new Definition("debt_relief", "Debt Relief", "Less cash to earn today. The menu shows the new target.")
		};

		public static readonly Definition[] NegativeModifiers = new Definition[8]
		{
			new Definition("participation", "Everybody Plays", "Everyone finishes 1 game before day end. Playing solo? Finish 3."),
			new Definition("variety", "Change Tables", "Play 2 different game types before day end."),
			new Definition("net_profit", "Earn Your Keep", "Make the casino profit goal before day end. The menu shows the amount."),
			new Definition("six_plays", "Keep Playing", "Finish 6 casino games as a crew before day end."),
			new Definition("timed_participation", "Early Participation", "Everyone finishes 1 game before the timer runs out. Playing solo? Finish 3."),
			new Definition("timed_variety", "Early Table Tour", "Play 2 different game types before the timer runs out."),
			new Definition("timed_net_profit", "Early Profit", "Make the casino profit goal before the timer runs out."),
			new Definition("timed_six_plays", "Early Six", "Finish 6 casino games as a crew before the timer runs out.")
		};

		public Engine(Rules rules)
		{
			pendingRules = Normalize(rules);
			State = new RunState
			{
				DeliveryVersion = 1,
				NegativeVersion = 1,
				NegativeScheduleVersion = 1
			};
		}

		public void SetRules(Rules rules)
		{
			pendingRules = Normalize(rules);
		}

		public void Restore(RunState saved)
		{
			if (saved == null || saved.Version != 1)
			{
				throw new ArgumentException("Unsupported Chaos checkpoint.");
			}
			if (saved.Round < 0 || saved.LastSequence < 0 || saved.AwardSequence < 0 || !Finite(saved.CashAwarded) || saved.CashAwarded < 0.0 || !Finite(saved.QuotaFactor) || saved.QuotaFactor < 0.65 || saved.QuotaFactor > 1.5)
			{
				throw new ArgumentException("Invalid Chaos checkpoint.");
			}
			double[] array = new double[9] { saved.NativeProfit, saved.NativeLosses, saved.PeakBalance, saved.LowBalance, saved.LastBalance, saved.Purchases, saved.SmallStakeProfit, saved.ContractTarget, saved.BiggestRecoveryRatio };
			foreach (double n in array)
			{
				if (!Finite(n))
				{
					throw new ArgumentException("Non-finite Chaos checkpoint value.");
				}
			}
			if (saved.NativeLosses < 0.0 || saved.Purchases < 0.0 || saved.TicketsAwarded < 0 || saved.TicketsAwarded > 10 || saved.QualifyingPlays < 0 || saved.HighRollerPlays < 0 || saved.ClosingTimeFactor < 0.8 || saved.ClosingTimeFactor > 1.0)
			{
				throw new ArgumentException("Invalid Chaos checkpoint counters.");
			}
			if (saved.BetItAllConsumedDay < -1)
			{
				throw new ArgumentException("Invalid BET IT ALL consumption day.");
			}
			if (saved.BetItAllUsed && saved.BetItAllConsumedDay < 0 && saved.Day >= 0)
			{
				saved.BetItAllConsumedDay = saved.Day;
			}
			State = saved;
			State.BetItAllUsed = State.Day >= 0 && State.BetItAllConsumedDay == State.Day;
			RepairLists();
			if (State.DeliveryVersion < 0 || State.DeliveryVersion > 1)
			{
				throw new ArgumentException("Unsupported cash-delivery checkpoint.");
			}
			if (State.DeliveryVersion == 0)
			{
				if (State.PendingCashRewards.Count != 0 || State.CashReceipts.Count != 0)
				{
					throw new ArgumentException("Unversioned cash-delivery ledger.");
				}
				State.CashDelivered = State.CashAwarded;
				State.LegacyCash = State.CashAwarded > 0.0;
				State.DeliveryVersion = 1;
			}
			ValidateCashLedger();
			if (State.ModifierIds.Count > 2 || State.PerkIds.Count > 3 || State.Choices.Count > 3 || !KnownIds(State.ModifierIds, Modifiers) || !KnownIds(State.PerkIds, Perks) || (State.ContractId.Length > 0 && Find(Contracts, State.ContractId) == null))
			{
				throw new ArgumentException("Unknown or incompatible Chaos checkpoint content.");
			}
			foreach (Choice choice in State.Choices)
			{
				if (choice == null || (choice.Kind != "perk" && choice.Kind != "contract" && choice.Kind != "vault") || (choice.Kind == "perk" && Find(Perks, choice.Id) == null) || (choice.Kind == "contract" && Find(Contracts, choice.Id) == null) || (choice.Kind == "vault" && choice.Id != "vault_cash" && choice.Id != "vault_tickets" && choice.Id != "vault_perk"))
				{
					throw new ArgumentException("Invalid Chaos checkpoint choice.");
				}
			}
			State.ActiveRules = Normalize(State.ActiveRules ?? pendingRules);
			if (State.NegativeScheduleVersion == 0)
			{
				State.NegativeDayChosen = State.Negatives.Count > 0;
				State.NegativeScheduleVersion = 1;
			}
			ValidateNegatives();
			if (State.Round == 0 && string.IsNullOrEmpty(State.RunId))
			{
				State.RandomState = 0u;
			}
			else if (State.RandomState == 0)
			{
				State.RandomState = 1831565813u;
			}
			SyncDescriptions();
			UpdateProgress();
		}

		public List<Reward> BeginDay(int day, int floor, ulong[] players, int[] gameTypes, int worldSeed, double originalDuration = 300.0)
		{
			resultActor = 0uL;
			List<Reward> list = new List<Reward>();
			if (day < 0 || floor < 0)
			{
				return list;
			}
			if (State.Day == day && State.Floor == floor && State.Round > 0)
			{
				return list;
			}
			if (State.DayOpen)
			{
				list.AddRange(EndDay(quotaMet: false));
			}
			CancelPendingPenalties();
			list.RemoveAll((Reward r) => r.Kind == RewardKind.PenaltyCash);
			State.ActiveRules = Normalize(pendingRules);
			if (State.RandomState == 0)
			{
				State.RandomState = (uint)(((State.ActiveRules.Seed == 0) ? worldSeed : State.ActiveRules.Seed) ^ -1640531527);
				if (State.RandomState == 0)
				{
					State.RandomState = 1831565813u;
				}
				State.RunId = State.RandomState.ToString("x8", CultureInfo.InvariantCulture);
			}
			State.Day = day;
			State.Floor = floor;
			State.Round++;
			State.Chapter = 1 + (State.Round - 1) / State.ActiveRules.ChapterRounds;
			State.DayOpen = true;
			State.DayEnded = false;
			State.DayEnabled = State.ActiveRules.Enabled;
			State.BetItAllUsed = State.BetItAllConsumedDay == day;
			State.Players = Unique(players);
			State.AvailableGames = Unique(gameTypes);
			State.PlayerProgress.Clear();
			State.GameProgress.Clear();
			ulong[] players2 = State.Players;
			foreach (ulong id in players2)
			{
				State.PlayerProgress.Add(new PlayerProgress
				{
					Id = id
				});
			}
			int[] availableGames = State.AvailableGames;
			foreach (int id2 in availableGames)
			{
				State.GameProgress.Add(new GameProgress
				{
					Id = id2
				});
			}
			State.ModifierIds.Clear();
			State.ModifierNames.Clear();
			State.ModifierDescriptions.Clear();
			State.ContractId = "";
			State.ContractName = "";
			State.ContractDescription = "";
			State.ContractProgress = "";
			State.ContractCompleted = false;
			State.ContractTarget = 0.3 + 0.05 * (double)Math.Min(4, State.Chapter - 1);
			State.QuotaFactor = 1.0;
			State.ClosingTimeFactor = 1.0;
			State.NegativeVersion = 1;
			State.Negatives.Clear();
			State.NegativesResolved = false;
			State.NegativeScheduleVersion = 1;
			State.NegativeDayChosen = false;
			State.KilledNpcKeys.Clear();
			State.NpcCashReserved = 0.0;
			State.NpcCashDelivered = 0.0;
			State.NegativeElapsedSeconds = 0.0;
			State.PenaltyReserved = 0.0;
			State.PenaltyDelivered = 0.0;
			State.NegativeDaySeconds = ((Finite(originalDuration) && originalDuration > 0.0) ? Math.Min(86400.0, originalDuration) : 300.0);
			State.CashAwarded = 0.0;
			State.CashDelivered = 0.0;
			State.LegacyCash = false;
			State.TicketsAwarded = 0;
			State.NativeProfit = 0.0;
			State.NativeLosses = 0.0;
			State.Purchases = 0.0;
			State.QualifyingPlays = 0;
			State.SmallStakePlays = 0;
			State.SmallStakeProfit = 0.0;
			State.HighRollerPlays = 0;
			State.WinningStreak = 0;
			State.BestDayStreak = 0;
			State.BalanceKnown = false;
			State.HadSetback = false;
			State.ComebackCompleted = false;
			State.RelayCompleted = false;
			State.VarietyCompleted = false;
			State.StreakCompleted = false;
			State.RiskDividendCompleted = false;
			State.SafetyFundUsed = false;
			State.RescueUsed = false;
			State.JackpotChecked = false;
			State.GoldenPayoutPending = false;
			State.RotationPlays = 0;
			if (State.Choices.Count > 0 && State.Choices[0].Kind != "perk")
			{
				State.Choices.Clear();
			}
			State.FeaturedGame = ((State.AvailableGames.Length == 0) ? (-1) : State.AvailableGames[Next(State.AvailableGames.Length)]);
			if (!State.DayEnabled)
			{
				SyncDescriptions();
				return list;
			}
			SelectModifiers();
			ApplyFactors(list);
			if (State.ActiveRules.ContractsEnabled && State.AvailableGames.Length > 0)
			{
				if (PerkActive("negotiator") && (State.Round - 1) % State.ActiveRules.ChapterRounds == 0 && State.Choices.Count == 0)
				{
					OfferContractChoices();
				}
				else
				{
					SetContract(DrawContract());
				}
			}
			ChooseNegativeDay();
			SelectNegatives(list);
			SyncDescriptions();
			UpdateProgress();
			Issue(list, RewardKind.Notice, 0.0, "Chaos round " + State.Round + ": " + string.Join(" + ", State.ModifierNames.ToArray()), 0uL);
			RefreshPreview();
			return list;
		}

		public List<Reward> Settle(ResultInput input)
		{
			resultActor = 0uL;
			List<Reward> list = new List<Reward>();
			if (input == null || !State.DayOpen || !State.DayEnabled || input.Sequence <= State.LastSequence)
			{
				return list;
			}
			State.LastSequence = input.Sequence;
			if (input.Sequence <= 0 || !Finite(input.BetRatio) || !Finite(input.PayoutRatio) || !Finite(input.BalanceRatio) || input.BetRatio < 0.001 || input.PayoutRatio < 0.0 || input.BalanceRatio < 0.0 || input.BetRatio > 1E+100 || input.PayoutRatio > 1E+100 || !Contains(State.Av