Decompiled source of GamblersReach v0.8.4

BepInEx/plugins/GamblersReach/GamblersReach.Core.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using HowToFish.ExpansionKit.Packs;
using HowToFish.ExpansionKit.Progression;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("GamblersReach.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GamblersReach.Core")]
[assembly: AssemblyTitle("GamblersReach.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GamblersReach.Core
{
	internal static class Identifiers
	{
		internal static string Require(string value, string parameterName)
		{
			if (string.IsNullOrWhiteSpace(value) || value.Length > 128 || value != value.Trim())
			{
				throw new ArgumentException("An identifier must contain 1–128 characters without surrounding whitespace.", parameterName);
			}
			for (int i = 0; i < value.Length; i++)
			{
				if (char.IsControl(value[i]))
				{
					throw new ArgumentException("Identifiers cannot contain control characters.", parameterName);
				}
			}
			return value;
		}
	}
}
namespace GamblersReach.Core.Wildlife
{
	public readonly struct HawkFlightPose
	{
		public Vector3 Position { get; }

		public Vector3 Velocity { get; }

		public HawkFlightPose(Vector3 position, Vector3 velocity)
		{
			Position = position;
			Velocity = velocity;
		}
	}
	public sealed class HawkFlightCurve
	{
		private readonly Vector3 start;

		private readonly Vector3 startVelocity;

		private readonly Vector3 end;

		private readonly Vector3 endVelocity;

		public double Duration { get; }

		public HawkFlightCurve(Vector3 start, Vector3 startVelocity, Vector3 end, Vector3 endVelocity, double duration)
		{
			if (!Finite(start) || !Finite(startVelocity) || !Finite(end) || !Finite(endVelocity) || double.IsNaN(duration) || double.IsInfinity(duration) || duration < 1E-06 || duration > 3.4028234663852886E+38)
			{
				throw new ArgumentException("A flight curve requires finite positions, velocities and a positive duration.");
			}
			this.start = start;
			this.startVelocity = startVelocity;
			this.end = end;
			this.endVelocity = endVelocity;
			Duration = duration;
		}

		public HawkFlightPose Sample(double elapsed)
		{
			if (double.IsNaN(elapsed) || double.IsInfinity(elapsed) || elapsed < 0.0)
			{
				throw new ArgumentOutOfRangeException("elapsed");
			}
			float num = (float)Duration;
			float num2 = (float)Math.Min(1.0, elapsed / Duration);
			float num3 = num2 * num2;
			float num4 = num3 * num2;
			Vector3 position = start * (2f * num4 - 3f * num3 + 1f) + startVelocity * (num * (num4 - 2f * num3 + num2)) + end * (-2f * num4 + 3f * num3) + endVelocity * (num * (num4 - num3));
			Vector3 velocity = start * ((6f * num3 - 6f * num2) / num) + startVelocity * (3f * num3 - 4f * num2 + 1f) + end * ((-6f * num3 + 6f * num2) / num) + endVelocity * (3f * num3 - 2f * num2);
			return new HawkFlightPose(position, velocity);
		}

		internal static bool Finite(Vector3 value)
		{
			if (!float.IsNaN(value.X) && !float.IsInfinity(value.X) && !float.IsNaN(value.Y) && !float.IsInfinity(value.Y) && !float.IsNaN(value.Z))
			{
				return !float.IsInfinity(value.Z);
			}
			return false;
		}
	}
	public sealed class HawkFlightHistory
	{
		public const double InterpolationDelay = 0.1;

		private readonly List<(Vector3 Position, double Time)> samples = new List<(Vector3, double)>();

		public bool HasSamples => samples.Count != 0;

		public void Receive(Vector3 position, double receivedAt)
		{
			if (!HawkFlightCurve.Finite(position) || !Finite(receivedAt) || (samples.Count != 0 && receivedAt < samples[samples.Count - 1].Time))
			{
				throw new ArgumentException("Hawk position samples require finite, monotonic arrival times.");
			}
			if (samples.Count != 0 && receivedAt == samples[samples.Count - 1].Time)
			{
				samples[samples.Count - 1] = (position, receivedAt);
			}
			else
			{
				samples.Add((position, receivedAt));
			}
			if (samples.Count > 12)
			{
				samples.RemoveAt(0);
			}
		}

		public HawkFlightPose Sample(double now)
		{
			if (!HasSamples || !Finite(now))
			{
				throw new InvalidOperationException("The hawk has no valid received flight history.");
			}
			double num = now - 0.1;
			if (samples.Count == 1 || num <= samples[0].Time)
			{
				return new HawkFlightPose(samples[0].Position, Vector3.Zero);
			}
			for (int i = 0; i < samples.Count - 1; i++)
			{
				(Vector3, double) tuple = samples[i];
				(Vector3, double) tuple2 = samples[i + 1];
				if (!(num > tuple2.Item2))
				{
					return new HawkFlightCurve(tuple.Item1, Velocity(i), tuple2.Item1, Velocity(i + 1), tuple2.Item2 - tuple.Item2).Sample(num - tuple.Item2);
				}
			}
			(Vector3, double) tuple3 = samples[samples.Count - 1];
			Vector3 vector = Velocity(samples.Count - 1);
			return new HawkFlightPose(tuple3.Item1 + vector * (float)Math.Min(0.1, num - tuple3.Item2), (num - tuple3.Item2 > 0.1) ? Vector3.Zero : vector);
		}

		private Vector3 Velocity(int index)
		{
			(Vector3, double) tuple = samples[Math.Max(0, index - 1)];
			(Vector3, double) tuple2 = samples[Math.Min(samples.Count - 1, index + 1)];
			Vector3 vector = (tuple2.Item1 - tuple.Item1) / (float)(tuple2.Item2 - tuple.Item2);
			if (!(vector.LengthSquared() > 2500f))
			{
				return vector;
			}
			return Vector3.Normalize(vector) * 50f;
		}

		private static bool Finite(double value)
		{
			if (!double.IsNaN(value))
			{
				return !double.IsInfinity(value);
			}
			return false;
		}
	}
	public static class HawkRules
	{
		public const int Health = 60;

		public const int BaseWorth = 20;

		public const int DiveDamage = 8;

		public const int MaximumAlive = 1;

		public const double NativeSeagullSpawnSeconds = 140.0;

		public const double SpawnSeconds = 420.0;

		public const double WarningSeconds = 1.2;

		public const double RecoverySeconds = 4.0;

		public const double CircleSeconds = 8.0;

		public const float StrikeRadius = 0.75f;

		public const float TargetRange = 35f;
	}
	public enum HawkStage
	{
		Circling,
		Telegraph,
		Diving,
		Recovering
	}
	public sealed class HawkSpawnClock
	{
		public double Elapsed { get; private set; }

		public bool Advance(double seconds, bool harborActive, int livingHawks)
		{
			if (!Finite(seconds) || seconds <= 0.0 || livingHawks < 0)
			{
				throw new ArgumentOutOfRangeException("seconds");
			}
			if (!harborActive || livingHawks >= 1)
			{
				Elapsed = 0.0;
				return false;
			}
			Elapsed += seconds;
			if (Elapsed < 420.0)
			{
				return false;
			}
			Elapsed = 0.0;
			return true;
		}

		private static bool Finite(double value)
		{
			if (!double.IsNaN(value))
			{
				return !double.IsInfinity(value);
			}
			return false;
		}
	}
	public sealed class HawkDiveCycle
	{
		private HawkFlightCurve? warning;

		private HawkFlightCurve? approach;

		private HawkFlightCurve? pullout;

		public HawkStage Stage { get; private set; }

		public double Elapsed { get; private set; }

		public Vector3 DiveOrigin { get; private set; }

		public Vector3 LockedAim { get; private set; }

		public Vector3 DiveEnd { get; private set; }

		public double DiveSeconds { get; private set; }

		public bool StrikeSpent { get; private set; }

		public bool CanWarn
		{
			get
			{
				if (Stage == HawkStage.Circling)
				{
					return Elapsed >= 8.0;
				}
				return false;
			}
		}

		public int DivesStarted { get; private set; }

		public void Warn(Vector3 origin, Vector3 target)
		{
			Warn(origin, target, Vector3.UnitX * 8f);
		}

		public void Warn(Vector3 origin, Vector3 target, Vector3 velocity)
		{
			if (!CanWarn)
			{
				throw new InvalidOperationException("The hawk must finish circling before another warning.");
			}
			if (!Finite(origin) || !Finite(target) || !Finite(velocity) || velocity.LengthSquared() < 1f || velocity.LengthSquared() > 400f || Vector3.DistanceSquared(origin, target) < 1f)
			{
				throw new ArgumentException("A dive needs finite, distinct origin and target positions.");
			}
			LockedAim = target;
			DiveOrigin = origin + velocity * 0.84f + Vector3.UnitY * 1.25f;
			Vector3 vector = Vector3.Normalize(target - DiveOrigin);
			Vector3 value = new Vector3(vector.X, 0f, vector.Z);
			value = ((value.LengthSquared() < 0.001f) ? Vector3.UnitZ : Vector3.Normalize(value));
			DiveEnd = target + value * 2.5f + Vector3.UnitY * 1.5f;
			float num = Vector3.Distance(DiveOrigin, target);
			DiveSeconds = Math.Max(1.1, Math.Min(2.4, (num + 3f) / 15f));
			double num2 = DiveSeconds * 0.78;
			Vector3 vector2 = vector * Math.Min(8f, num / (float)num2);
			Vector3 vector3 = value * Math.Max(8f, Math.Min(16f, num / (float)num2 * 0.75f));
			warning = new HawkFlightCurve(origin, velocity, DiveOrigin, vector2, 1.2);
			approach = new HawkFlightCurve(DiveOrigin, vector2, target, vector3, num2);
			pullout = new HawkFlightCurve(target, vector3, DiveEnd, value * 6f + Vector3.UnitY * 7f, DiveSeconds - num2);
			StrikeSpent = false;
			Enter(HawkStage.Telegraph);
		}

		public void Advance(double seconds, bool targetValid = true)
		{
			if (double.IsNaN(seconds) || double.IsInfinity(seconds) || seconds <= 0.0)
			{
				throw new ArgumentOutOfRangeException("seconds");
			}
			if (!targetValid && (Stage == HawkStage.Telegraph || Stage == HawkStage.Diving))
			{
				Abort();
				return;
			}
			Elapsed += seconds;
			if (Stage == HawkStage.Telegraph && Elapsed >= 1.2)
			{
				DivesStarted++;
				Enter(HawkStage.Diving);
			}
			else if (Stage == HawkStage.Diving && Elapsed >= DiveSeconds)
			{
				Enter(HawkStage.Recovering);
			}
			else if (Stage == HawkStage.Recovering && Elapsed >= 4.0)
			{
				Enter(HawkStage.Circling);
			}
		}

		public HawkFlightPose WarningPose(double elapsed)
		{
			if (Stage != HawkStage.Telegraph || warning == null)
			{
				throw new InvalidOperationException("Warning poses require an active wind-up.");
			}
			return warning.Sample(elapsed);
		}

		public HawkFlightPose DivePose(double elapsed)
		{
			if (Stage != HawkStage.Diving || approach == null || pullout == null)
			{
				throw new InvalidOperationException("Dive poses require an active dive.");
			}
			if (!(elapsed <= approach.Duration))
			{
				return pullout.Sample(elapsed - approach.Duration);
			}
			return approach.Sample(elapsed);
		}

		public Vector3 DivePosition(double elapsed)
		{
			return DivePose(elapsed).Position;
		}

		public bool TryStrike(Vector3 previous, Vector3 next, Vector3 playerCenter)
		{
			if (!Finite(previous) || !Finite(next) || !Finite(playerCenter))
			{
				throw new ArgumentException("Strike positions must be finite.");
			}
			if (Stage != HawkStage.Diving || StrikeSpent)
			{
				return false;
			}
			Vector3 vector = next - previous;
			float num = vector.LengthSquared();
			float num2 = ((num < 1E-10f) ? 0f : Math.Max(0f, Math.Min(1f, Vector3.Dot(playerCenter - previous, vector) / num)));
			if (Vector3.DistanceSquared(playerCenter, previous + vector * num2) > 0.5625f)
			{
				return false;
			}
			StrikeSpent = true;
			return true;
		}

		public void Abort()
		{
			if (Stage != HawkStage.Circling && Stage != HawkStage.Recovering)
			{
				Enter(HawkStage.Recovering);
			}
		}

		private void Enter(HawkStage stage)
		{
			Stage = stage;
			Elapsed = 0.0;
		}

		private static bool Finite(Vector3 value)
		{
			if (Finite(value.X) && Finite(value.Y))
			{
				return Finite(value.Z);
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
}
namespace GamblersReach.Core.Progression
{
	public enum ChapterReward
	{
		None,
		AnglerLure,
		IslandFiveCoordinates
	}
	public sealed class ChapterProgress
	{
		public int Version { get; set; } = 1;

		public bool Initialized { get; set; }

		public bool ExistingIslandFiveAccess { get; set; }

		public bool BarracudaTrophyDelivered { get; set; }

		public bool AnglerLureClaimed { get; set; }

		public bool AnglerTrophyDelivered { get; set; }

		public bool CoordinatesClaimed { get; set; }

		public ChapterReward PendingReward { get; set; }

		public bool CanVisitIslandFive
		{
			get
			{
				if (!ExistingIslandFiveAccess)
				{
					return CoordinatesClaimed;
				}
				return true;
			}
		}

		public void Initialize(int nativeUnlockCount, int continuationIslandId = 4)
		{
			Validate();
			if (nativeUnlockCount < 1 || nativeUnlockCount > 255)
			{
				throw new ArgumentOutOfRangeException("nativeUnlockCount");
			}
			if (!Initialized)
			{
				ExistingIslandFiveAccess = InterludeRoutePolicy.IsUnlocked(continuationIslandId, nativeUnlockCount);
				Initialized = true;
			}
		}

		public void DeliverBarracudaTrophy()
		{
			RequireReady();
			if (PendingReward != ChapterReward.None)
			{
				throw new InvalidOperationException("Take the waiting reward before offering another trophy.");
			}
			BarracudaTrophyDelivered = true;
			PendingReward = ChapterReward.AnglerLure;
		}

		public void DeliverAnglerTrophy()
		{
			RequireReady();
			if (!AnglerLureClaimed || CoordinatesClaimed || PendingReward != ChapterReward.None)
			{
				throw new InvalidOperationException("The angler trophy is not needed at this stage.");
			}
			AnglerTrophyDelivered = true;
			PendingReward = ChapterReward.IslandFiveCoordinates;
		}

		public void Claim(ChapterReward reward)
		{
			RequireReady();
			if (reward == ChapterReward.None || reward != PendingReward)
			{
				throw new InvalidOperationException("That reward is not waiting to be collected.");
			}
			if (reward == ChapterReward.AnglerLure)
			{
				AnglerLureClaimed = true;
			}
			else
			{
				CoordinatesClaimed = true;
			}
			PendingReward = ChapterReward.None;
			Validate();
		}

		public void Validate()
		{
			if (Version != 1 || !Enum.IsDefined(typeof(ChapterReward), PendingReward) || (!Initialized && (ExistingIslandFiveAccess || BarracudaTrophyDelivered || AnglerLureClaimed || AnglerTrophyDelivered || CoordinatesClaimed || PendingReward != ChapterReward.None)) || (AnglerLureClaimed && !BarracudaTrophyDelivered) || (AnglerTrophyDelivered && !AnglerLureClaimed) || (CoordinatesClaimed && !AnglerTrophyDelivered) || (BarracudaTrophyDelivered && !AnglerLureClaimed && PendingReward != ChapterReward.AnglerLure) || (AnglerTrophyDelivered && !CoordinatesClaimed && PendingReward != ChapterReward.IslandFiveCoordinates) || (PendingReward == ChapterReward.AnglerLure && !BarracudaTrophyDelivered) || (PendingReward == ChapterReward.IslandFiveCoordinates && (!AnglerTrophyDelivered || CoordinatesClaimed)))
			{
				throw new ArgumentException("The harbor chapter's trophy and reward state is inconsistent.");
			}
		}

		private void RequireReady()
		{
			Validate();
			if (!Initialized)
			{
				throw new InvalidOperationException("Select a crew save before starting the harbor chapter.");
			}
		}
	}
	public static class HarborSaveRevision
	{
		private const string Release080Plugin = "B82A1AA7035E23D749A682CA415B03FC585DDBC32A4A4640C5D47EDC9D031D44";

		private const string Release080Core = "1AB9465A0B9A43B699DCAA2BF61EBD760B2F9C9C9BCE100ECE8BE268EE765260";

		private const string Release081Plugin = "A697A545B6D2305402175F0291F1706D49762CF7F9DBBC8C4B1EB863858E36DF";

		private const string Release081Core = "4DB633424F0CC228C9EAB0B136C58707873328676F246875CA97F769436B5ABA";

		private const string Release082Plugin = "5A5102CF45FE8D331A2D080B24BC7A51900BFD644F75F80F752BAB2A0EAF61F0";

		private const string Release082Core = "B02C833A811D2CA9327B473CD870FA26E425FB9D7E4BF55DBD135BD2ABD4F274";

		private const string Verified083Plugin = "10E193C337211F02D717B03BC36C9F97BBB93DE4C95886095C246E384236C66C";

		private const string Verified083Core = "3F47EA9DC4A2668B1734376BA66BDCBB2D1F227B670277D13DE7E148E83A806F";

		private const string Final083Plugin = "BCE48FC27C98F680208514B75665A791FC16CBD2C08F821135B4985D19CE219B";

		private const string Final083Core = "FA2A9199DE7B242B93BF9CDC1C34B8BC2801AE8A7B54AC23DA7F70FAD4517F2B";

		private const string Published083Plugin = "2B4DDB34D01147F397367362370178E4B70961FFA6FB2121126164ED3C1DB946";

		private const string Published083Core = "70DE6B29B13E099814C8C64FE808D73B9DB448CF771B3FCDA7C5A6642A2FF4CC";

		public static bool Accept(SavedPack saved, IReadOnlyDictionary<string, string> code)
		{
			if (saved == null || code == null || saved.Key != "gamblers_reach" || code.Count != 2 || !code.TryGetValue("GamblersReach", out string value) || !code.TryGetValue("GamblersReach.Core", out string value2))
			{
				return false;
			}
			return saved.Version switch
			{
				"0.7.0" => saved.Fingerprint == "23737c25e8a8e86f7707f11d58c433a39f7c1217593500db09e41093c79c59c6" && value == "5226473DBAED2950D4C89AD542707E8EE60E82C963081C130A3A22FE636B78C4" && value2 == "6EE04CA3CF88F4ED9342B3A5E263404F96B722F4EBDC61A3668A96C383FEB7FD", 
				"0.7.1" => saved.Fingerprint == "22918057faa48535942230ca43139bef87aecb8982e607c46630b4ccaa5e76ab" && value == "38FBA14506562EA94D8DC9E3D1B8B20557B9786BD40B276C37B07BE15FBA1242" && value2 == "1C684E478FD719ACEFA93AFBE6FA735E25135482780054DCAD5018028CDFD0DE", 
				"0.7.2" => saved.Fingerprint == "ae798b2247b389e535e12004a9cce35eaa0102a9448485e577e5e0df401b9cfa" && value == "FB131E532CEEAAB58274836CADA223CD3A463D5C6DDA0CDAF4BCA26D7F78FF8B" && value2 == "7886CA9C0B52923355305DF503ECB8E71970BA3BF3A54C4CB4A7035862E71272", 
				"0.7.3" => saved.Fingerprint == "68da1823c373e929acfdfe89029c6f0e53cd96db7065f030e8e5d5240acf875c" && value == "6B84E52DABA097A21AAF5A6721A91E9D93163423EDAEF30CACD861A8F74A2C7F" && value2 == "689ED1F4A5C7321B9F78E68F0EE059ED228186B8F47C97FDF58DD1EC6E942C68", 
				"0.8.0" => saved.Fingerprint == "1f102a86294e76cd038a3e49cf09246ecca2648d90d2c4baeabd17e49e760ec3" && value == "B82A1AA7035E23D749A682CA415B03FC585DDBC32A4A4640C5D47EDC9D031D44" && value2 == "1AB9465A0B9A43B699DCAA2BF61EBD760B2F9C9C9BCE100ECE8BE268EE765260", 
				"0.8.1" => saved.Fingerprint == "e9c47b6e0ef99e06d4082fb3204f3724b8d1e45f5ac58b1e4cc594a9413ef0d5" && value == "A697A545B6D2305402175F0291F1706D49762CF7F9DBBC8C4B1EB863858E36DF" && value2 == "4DB633424F0CC228C9EAB0B136C58707873328676F246875CA97F769436B5ABA", 
				"0.8.2" => saved.Fingerprint == "c3c851a52ccd6fe58581e7dce2199935db86f70cc60bcbea2eba416fa171d41c" && value == "5A5102CF45FE8D331A2D080B24BC7A51900BFD644F75F80F752BAB2A0EAF61F0" && value2 == "B02C833A811D2CA9327B473CD870FA26E425FB9D7E4BF55DBD135BD2ABD4F274", 
				"0.8.3" => saved.Fingerprint == "c9fcbdb01ece013997b6192aaeaa5285afdd63c2df8eea89ea801315ae484fa2" && IsVerified083Code(value, value2), 
				_ => false, 
			};
		}

		public static bool AcceptWildlife(SavedPack saved, IReadOnlyDictionary<string, string> code)
		{
			if (saved == null || saved.Key != "gamblers_reach_wildlife" || code == null || code.Count != 2 || !code.TryGetValue("GamblersReach", out string value) || !code.TryGetValue("GamblersReach.Core", out string value2))
			{
				return false;
			}
			return saved.Version switch
			{
				"0.8.0" => saved.Fingerprint == "a7e42f62b2dc0bbaee2ae29d747f9d5d24a1e50939840209a32b452d7a1a5fb2" && value == "B82A1AA7035E23D749A682CA415B03FC585DDBC32A4A4640C5D47EDC9D031D44" && value2 == "1AB9465A0B9A43B699DCAA2BF61EBD760B2F9C9C9BCE100ECE8BE268EE765260", 
				"0.8.1" => saved.Fingerprint == "7b978088f9d3925a2dc2641aa80abcf6cd5d75643c9204f1c22ecb1d40a83287" && value == "A697A545B6D2305402175F0291F1706D49762CF7F9DBBC8C4B1EB863858E36DF" && value2 == "4DB633424F0CC228C9EAB0B136C58707873328676F246875CA97F769436B5ABA", 
				"0.8.2" => saved.Fingerprint == "8b9a83bbd5db323bdfae408d38db3d03cc47a4019b0a9752179db84c738ca2eb" && value == "5A5102CF45FE8D331A2D080B24BC7A51900BFD644F75F80F752BAB2A0EAF61F0" && value2 == "B02C833A811D2CA9327B473CD870FA26E425FB9D7E4BF55DBD135BD2ABD4F274", 
				"0.8.3" => saved.Fingerprint == "9e5f9f2c0588b1a61258770f2692c299fbad35b2c56d1bae0059ef85cc7f339e" && IsVerified083Code(value, value2), 
				_ => false, 
			};
		}

		private static bool IsVerified083Code(string? plugin, string? core)
		{
			if ((!(plugin == "10E193C337211F02D717B03BC36C9F97BBB93DE4C95886095C246E384236C66C") || !(core == "3F47EA9DC4A2668B1734376BA66BDCBB2D1F227B670277D13DE7E148E83A806F")) && (!(plugin == "BCE48FC27C98F680208514B75665A791FC16CBD2C08F821135B4985D19CE219B") || !(core == "FA2A9199DE7B242B93BF9CDC1C34B8BC2801AE8A7B54AC23DA7F70FAD4517F2B")))
			{
				if (plugin == "2B4DDB34D01147F397367362370178E4B70961FFA6FB2121126164ED3C1DB946")
				{
					return core == "70DE6B29B13E099814C8C64FE808D73B9DB448CF771B3FCDA7C5A6642A2FF4CC";
				}
				return false;
			}
			return true;
		}
	}
	public sealed class HuntProgress
	{
		public int Version { get; set; } = 1;

		public bool MiniBossDefeated { get; set; }

		public bool BossDefeated { get; set; }

		public bool TrophyReturned { get; set; }

		public bool RifleClaimed { get; set; }

		public void Validate()
		{
			if (Version != 1 || (BossDefeated && !MiniBossDefeated) || (TrophyReturned && !BossDefeated) || (RifleClaimed && !TrophyReturned))
			{
				throw new ArgumentException("The hunt progress is inconsistent.");
			}
		}

		public bool RecordMiniBoss()
		{
			Validate();
			if (MiniBossDefeated)
			{
				return false;
			}
			MiniBossDefeated = true;
			return true;
		}

		public bool RecordBoss()
		{
			Validate();
			if (!MiniBossDefeated)
			{
				throw new InvalidOperationException("Defeat the Reef Marauder before the Breakwater King.");
			}
			if (BossDefeated)
			{
				return false;
			}
			BossDefeated = true;
			return true;
		}

		public void ReturnTrophy()
		{
			Validate();
			if (!BossDefeated || TrophyReturned)
			{
				throw new InvalidOperationException("The crown is not needed at this stage.");
			}
			TrophyReturned = true;
		}

		public void ClaimRifle()
		{
			Validate();
			if (!TrophyReturned || RifleClaimed)
			{
				throw new InvalidOperationException("The hunt reward is not available.");
			}
			RifleClaimed = true;
		}
	}
	public sealed class VanillaProgress
	{
		public const int FinalVanillaIslandId = 4;

		public int HighestCompletedIslandId { get; }

		public bool HasCompletedGame { get; }

		public VanillaProgress(int highestCompletedIslandId, bool hasCompletedGame = false)
		{
			if (highestCompletedIslandId < -1 || highestCompletedIslandId > 4)
			{
				throw new ArgumentOutOfRangeException("highestCompletedIslandId");
			}
			HighestCompletedIslandId = highestCompletedIslandId;
			HasCompletedGame = hasCompletedGame;
		}
	}
	public enum QuestStage
	{
		Locked,
		Introduction,
		CollectSixSpecies,
		ClaimGearReward,
		PostgameRareCatch,
		Complete,
		AwaitFinale
	}
	public enum QuestNpcRole
	{
		Host,
		Angler,
		Outfitter
	}
	public sealed class QuestDefinition
	{
		public const int RequiredSpeciesCount = 6;

		private readonly ReadOnlyCollection<string> speciesIds;

		public IReadOnlyList<string> CollectionSpeciesIds => speciesIds;

		public string RareSpeciesId { get; }

		public QuestDefinition(IEnumerable<string> collectionSpeciesIds, string rareSpeciesId)
		{
			if (collectionSpeciesIds == null)
			{
				throw new ArgumentNullException("collectionSpeciesIds");
			}
			string[] array = (from id in collectionSpeciesIds.Take(7)
				select Identifiers.Require(id, "collectionSpeciesIds")).ToArray();
			if (array.Length != 6 || array.Distinct<string>(StringComparer.Ordinal).Count() != 6)
			{
				throw new ArgumentException("Exactly six distinct species IDs are required.", "collectionSpeciesIds");
			}
			Array.Sort(array, (IComparer<string>?)StringComparer.Ordinal);
			speciesIds = Array.AsReadOnly(array);
			RareSpeciesId = Identifiers.Require(rareSpeciesId, "rareSpeciesId");
			if (!array.Contains<string>(RareSpeciesId, StringComparer.Ordinal))
			{
				throw new ArgumentException("The rare-catch target must be one of the six collection species.", "rareSpeciesId");
			}
		}
	}
	public sealed class ModProgress
	{
		public int Version => 1;

		public bool IslandUnlocked { get; }

		public bool IntroductionCompleted { get; }

		public IReadOnlyList<string> CollectedSpeciesIds { get; }

		public bool GearRewardClaimed { get; }

		public bool RareCatchQuestCompleted { get; }

		internal ModProgress(bool islandUnlocked, bool introductionCompleted, IEnumerable<string> collectedSpeciesIds, bool gearRewardClaimed, bool rareCatchQuestCompleted)
		{
			IslandUnlocked = islandUnlocked;
			IntroductionCompleted = introductionCompleted;
			CollectedSpeciesIds = Array.AsReadOnly(collectedSpeciesIds.OrderBy<string, string>((string id) => id, StringComparer.Ordinal).ToArray());
			GearRewardClaimed = gearRewardClaimed;
			RareCatchQuestCompleted = rareCatchQuestCompleted;
		}

		public static ModProgress New()
		{
			return new ModProgress(islandUnlocked: false, introductionCompleted: false, Array.Empty<string>(), gearRewardClaimed: false, rareCatchQuestCompleted: false);
		}

		public ModProgressSnapshot CreateSnapshot()
		{
			return new ModProgressSnapshot
			{
				Version = Version,
				IslandUnlocked = IslandUnlocked,
				IntroductionCompleted = IntroductionCompleted,
				CollectedSpeciesIds = CollectedSpeciesIds.ToArray(),
				GearRewardClaimed = GearRewardClaimed,
				RareCatchQuestCompleted = RareCatchQuestCompleted
			};
		}
	}
	public sealed class ModProgressSnapshot
	{
		public const int CurrentVersion = 1;

		public int Version { get; set; } = 1;

		public bool IslandUnlocked { get; set; }

		public bool IntroductionCompleted { get; set; }

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

		public bool GearRewardClaimed { get; set; }

		public bool RareCatchQuestCompleted { get; set; }
	}
	public sealed class QuestProgression
	{
		public QuestDefinition Definition { get; }

		public QuestProgression(QuestDefinition definition)
		{
			Definition = definition ?? throw new ArgumentNullException("definition");
		}

		public static bool IsVanillaEligible(VanillaProgress vanilla)
		{
			if (vanilla == null)
			{
				throw new ArgumentNullException("vanilla");
			}
			if (!vanilla.HasCompletedGame)
			{
				return vanilla.HighestCompletedIslandId >= 3;
			}
			return true;
		}

		public bool CanVisitIsland(VanillaProgress vanilla, ModProgress progress)
		{
			Validate(progress);
			if (!IsVanillaEligible(vanilla))
			{
				return progress.IslandUnlocked;
			}
			return true;
		}

		public ModProgress RefreshEligibility(VanillaProgress vanilla, ModProgress progress)
		{
			if (!CanVisitIsland(vanilla, progress) || progress.IslandUnlocked)
			{
				return progress;
			}
			return Copy(progress, true);
		}

		public QuestStage GetStage(VanillaProgress vanilla, ModProgress progress)
		{
			if (!CanVisitIsland(vanilla, progress))
			{
				return QuestStage.Locked;
			}
			if (!progress.IntroductionCompleted)
			{
				return QuestStage.Introduction;
			}
			if (progress.CollectedSpeciesIds.Count < 6)
			{
				return QuestStage.CollectSixSpecies;
			}
			if (!progress.GearRewardClaimed)
			{
				return QuestStage.ClaimGearReward;
			}
			if (progress.RareCatchQuestCompleted)
			{
				return QuestStage.Complete;
			}
			if (!vanilla.HasCompletedGame)
			{
				return QuestStage.AwaitFinale;
			}
			return QuestStage.PostgameRareCatch;
		}

		public static QuestNpcRole? NpcFor(QuestStage stage)
		{
			switch (stage)
			{
			case QuestStage.Introduction:
				return QuestNpcRole.Host;
			case QuestStage.CollectSixSpecies:
				return QuestNpcRole.Angler;
			case QuestStage.ClaimGearReward:
				return QuestNpcRole.Outfitter;
			case QuestStage.PostgameRareCatch:
				return QuestNpcRole.Angler;
			case QuestStage.Locked:
			case QuestStage.Complete:
			case QuestStage.AwaitFinale:
				return null;
			default:
				throw new ArgumentOutOfRangeException("stage");
			}
		}

		public ModProgress CompleteIntroduction(VanillaProgress vanilla, ModProgress progress)
		{
			RequireStage(vanilla, progress, QuestStage.Introduction);
			return Copy(progress, true, true);
		}

		public ModProgress RecordCatch(VanillaProgress vanilla, ModProgress progress, string speciesId)
		{
			Identifiers.Require(speciesId, "speciesId");
			progress = RefreshEligibility(vanilla, progress);
			if (!progress.IntroductionCompleted || !Definition.CollectionSpeciesIds.Contains<string>(speciesId, StringComparer.Ordinal))
			{
				return progress;
			}
			if (!progress.CollectedSpeciesIds.Contains<string>(speciesId, StringComparer.Ordinal))
			{
				ModProgress progress2 = progress;
				IEnumerable<string> collectedSpeciesIds = progress.CollectedSpeciesIds.Concat(new string[1] { speciesId });
				return Copy(progress2, null, null, collectedSpeciesIds);
			}
			if (vanilla.HasCompletedGame && progress.GearRewardClaimed && !progress.RareCatchQuestCompleted && speciesId == Definition.RareSpeciesId)
			{
				ModProgress progress3 = progress;
				bool? rareCatchQuestCompleted = true;
				return Copy(progress3, null, null, null, null, rareCatchQuestCompleted);
			}
			return progress;
		}

		public ModProgress ClaimGearReward(VanillaProgress vanilla, ModProgress progress)
		{
			RequireStage(vanilla, progress, QuestStage.ClaimGearReward);
			bool? gearRewardClaimed = true;
			return Copy(progress, null, null, null, gearRewardClaimed);
		}

		public ModProgress Restore(ModProgressSnapshot snapshot)
		{
			if (snapshot == null)
			{
				throw new ArgumentNullException("snapshot");
			}
			if (snapshot.Version != 1)
			{
				throw new ArgumentException("Unsupported mod progress version.", "snapshot");
			}
			if (snapshot.CollectedSpeciesIds == null || snapshot.CollectedSpeciesIds.Length > 6)
			{
				throw new ArgumentException("Invalid collection state.", "snapshot");
			}
			string[] collectedSpeciesIds = snapshot.CollectedSpeciesIds;
			for (int i = 0; i < collectedSpeciesIds.Length; i++)
			{
				Identifiers.Require(collectedSpeciesIds[i], "snapshot");
			}
			if (snapshot.CollectedSpeciesIds.Distinct<string>(StringComparer.Ordinal).Count() != snapshot.CollectedSpeciesIds.Length)
			{
				throw new ArgumentException("Duplicate collected species IDs.", "snapshot");
			}
			ModProgress modProgress = new ModProgress(snapshot.IslandUnlocked, snapshot.IntroductionCompleted, snapshot.CollectedSpeciesIds, snapshot.GearRewardClaimed, snapshot.RareCatchQuestCompleted);
			Validate(modProgress);
			return modProgress;
		}

		private void Validate(ModProgress progress)
		{
			if (progress == null)
			{
				throw new ArgumentNullException("progress");
			}
			if ((progress.IntroductionCompleted && !progress.IslandUnlocked) || (progress.CollectedSpeciesIds.Count > 0 && !progress.IntroductionCompleted) || (progress.GearRewardClaimed && progress.CollectedSpeciesIds.Count != 6) || (progress.RareCatchQuestCompleted && !progress.GearRewardClaimed) || progress.CollectedSpeciesIds.Any((string id) => !Definition.CollectionSpeciesIds.Contains<string>(id, StringComparer.Ordinal)))
			{
				throw new ArgumentException("Progress is inconsistent with the quest definition or its prerequisites.", "progress");
			}
		}

		private void RequireStage(VanillaProgress vanilla, ModProgress progress, QuestStage stage)
		{
			QuestStage stage2 = GetStage(vanilla, progress);
			if (stage2 != stage)
			{
				throw new InvalidOperationException($"This operation requires {stage}; the quest is {stage2}.");
			}
		}

		private static ModProgress Copy(ModProgress progress, bool? islandUnlocked = null, bool? introductionCompleted = null, IEnumerable<string>? collectedSpeciesIds = null, bool? gearRewardClaimed = null, bool? rareCatchQuestCompleted = null)
		{
			return new ModProgress(islandUnlocked ?? progress.IslandUnlocked, introductionCompleted ?? progress.IntroductionCompleted, collectedSpeciesIds ?? progress.CollectedSpeciesIds, gearRewardClaimed ?? progress.GearRewardClaimed, rareCatchQuestCompleted ?? progress.RareCatchQuestCompleted);
		}
	}
}
namespace GamblersReach.Core.Content
{
	public sealed class ManifestEntry
	{
		public string Id { get; }

		public string CanonicalData { get; }

		public ManifestEntry(string id, string canonicalData)
		{
			Id = Identifiers.Require(id, "id");
			CanonicalData = canonicalData ?? throw new ArgumentNullException("canonicalData");
		}
	}
	public static class ContentManifest
	{
		public const int FormatVersion = 1;

		public static string ComputeChecksum(IEnumerable<ManifestEntry> entries)
		{
			if (entries == null)
			{
				throw new ArgumentNullException("entries");
			}
			ManifestEntry[] array = entries.ToArray();
			if (array.Any((ManifestEntry entry) => entry == null))
			{
				throw new ArgumentException("A manifest cannot contain null entries.", "entries");
			}
			Array.Sort(array, (ManifestEntry left, ManifestEntry right) => StringComparer.Ordinal.Compare(left.Id, right.Id));
			for (int num = 1; num < array.Length; num++)
			{
				if (array[num - 1].Id == array[num].Id)
				{
					throw new ArgumentException("Duplicate content ID: " + array[num].Id, "entries");
				}
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), leaveOpen: true))
			{
				WriteText(binaryWriter, "GamblersReach.ContentManifest");
				binaryWriter.Write(1);
				binaryWriter.Write(array.Length);
				ManifestEntry[] array2 = array;
				foreach (ManifestEntry manifestEntry in array2)
				{
					WriteText(binaryWriter, manifestEntry.Id);
					WriteText(binaryWriter, manifestEntry.CanonicalData);
				}
			}
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(memoryStream.ToArray())).Replace("-", "").ToLowerInvariant();
		}

		private static void WriteText(BinaryWriter writer, string value)
		{
			byte[] bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetBytes(value);
			writer.Write(bytes.Length);
			writer.Write(bytes);
		}
	}
}
namespace GamblersReach.Core.Blackjack
{
	public enum PlayerAction
	{
		Hit,
		Stand
	}
	public enum BlackjackRoundState
	{
		PlayerTurn,
		Resolved,
		SettlementPending,
		Settled
	}
	public sealed class BlackjackRound
	{
		private readonly CardDeck deck;

		private readonly List<Card> playerCards = new List<Card>();

		private readonly List<Card> dealerCards = new List<Card>();

		private readonly List<PlayerAction> actions = new List<PlayerAction>();

		private readonly ReadOnlyCollection<Card> playerView;

		private readonly ReadOnlyCollection<Card> dealerView;

		private int nextCard;

		public Guid RoundId { get; }

		public ItemWager Wager { get; }

		public BlackjackRoundState State { get; private set; }

		public IReadOnlyList<Card> PlayerCards => playerView;

		public IReadOnlyList<Card> DealerCards => dealerView;

		public Card DealerUpCard => dealerCards[0];

		public HandScore PlayerScore => HandEvaluator.Score(playerCards);

		public HandScore DealerScore => HandEvaluator.Score(dealerCards);

		public int CardsRemaining => deck.Cards.Count - nextCard;

		public BlackjackResult? Result { get; private set; }

		public SettlementInstruction? Settlement { get; private set; }

		public BlackjackRound(Guid roundId, ItemWager wager, CardDeck deck)
		{
			if (roundId == Guid.Empty)
			{
				throw new ArgumentException("A globally unique nonempty round ID is required.", "roundId");
			}
			RoundId = roundId;
			Wager = wager ?? throw new ArgumentNullException("wager");
			this.deck = deck ?? throw new ArgumentNullException("deck");
			playerView = playerCards.AsReadOnly();
			dealerView = dealerCards.AsReadOnly();
			playerCards.Add(Draw());
			dealerCards.Add(Draw());
			playerCards.Add(Draw());
			dealerCards.Add(Draw());
			if (PlayerScore.IsNatural || DealerScore.IsNatural)
			{
				Resolve((PlayerScore.IsNatural && DealerScore.IsNatural) ? BlackjackOutcome.Push : (PlayerScore.IsNatural ? BlackjackOutcome.Natural : BlackjackOutcome.Loss));
			}
		}

		public void Play(PlayerAction action)
		{
			if (action != PlayerAction.Hit && action != PlayerAction.Stand)
			{
				throw new ArgumentOutOfRangeException("action");
			}
			RequireState(BlackjackRoundState.PlayerTurn);
			if (action == PlayerAction.Hit)
			{
				playerCards.Add(Draw());
				if (PlayerScore.IsBust)
				{
					Resolve(BlackjackOutcome.Loss);
				}
				else if (PlayerScore.Total == 21)
				{
					FinishDealer();
				}
			}
			else
			{
				FinishDealer();
			}
			actions.Add(action);
		}

		public SettlementInstruction PrepareSettlement()
		{
			RequireState(BlackjackRoundState.Resolved);
			Settlement = new SettlementInstruction(Wager, Result);
			State = BlackjackRoundState.SettlementPending;
			return Settlement;
		}

		public void AcknowledgeSettlement(Guid settlementId)
		{
			RequireState(BlackjackRoundState.SettlementPending);
			if (settlementId != RoundId)
			{
				throw new ArgumentException("The acknowledgement belongs to a different settlement.", "settlementId");
			}
			State = BlackjackRoundState.Settled;
		}

		public RoundSnapshot CreateSnapshot()
		{
			return new RoundSnapshot
			{
				Version = 1,
				RoundId = RoundId,
				EscrowId = Wager.EscrowId,
				WagerValue = Wager.Value,
				Deck = deck.Cards.Select((Card card) => new CardSnapshot
				{
					Suit = card.Suit,
					Rank = card.Rank
				}).ToArray(),
				Actions = actions.ToArray(),
				State = State
			};
		}

		public static BlackjackRound Restore(RoundSnapshot snapshot)
		{
			if (snapshot == null)
			{
				throw new ArgumentNullException("snapshot");
			}
			if (snapshot.Version != 1)
			{
				throw new ArgumentException("Unsupported round snapshot version.", "snapshot");
			}
			if (snapshot.Deck == null || snapshot.Deck.Length != 52 || snapshot.Deck.Any((CardSnapshot card) => card == null))
			{
				throw new ArgumentException("A complete recovery deck is required.", "snapshot");
			}
			if (snapshot.Actions == null || snapshot.Actions.Length > 48)
			{
				throw new ArgumentException("Invalid action history.", "snapshot");
			}
			CardDeck cardDeck = new CardDeck(snapshot.Deck.Select((CardSnapshot card) => new Card(card.Suit, card.Rank)));
			BlackjackRound blackjackRound = new BlackjackRound(snapshot.RoundId, new ItemWager(snapshot.EscrowId, snapshot.WagerValue), cardDeck);
			try
			{
				PlayerAction[] array = snapshot.Actions;
				foreach (PlayerAction action in array)
				{
					blackjackRound.Play(action);
				}
				switch (snapshot.State)
				{
				case BlackjackRoundState.PlayerTurn:
				case BlackjackRoundState.Resolved:
					if (blackjackRound.State != snapshot.State)
					{
						throw new ArgumentException("Saved state disagrees with the replayed round.", "snapshot");
					}
					break;
				case BlackjackRoundState.SettlementPending:
					blackjackRound.PrepareSettlement();
					break;
				case BlackjackRoundState.Settled:
					blackjackRound.PrepareSettlement();
					blackjackRound.AcknowledgeSettlement(blackjackRound.RoundId);
					break;
				default:
					throw new ArgumentException("Unknown saved round state.", "snapshot");
				}
			}
			catch (InvalidOperationException innerException)
			{
				throw new ArgumentException("The recovery history contains an out-of-turn transition.", "snapshot", innerException);
			}
			return blackjackRound;
		}

		private Card Draw()
		{
			if (nextCard == deck.Cards.Count)
			{
				throw new InvalidOperationException("The deck is exhausted.");
			}
			return deck.Cards[nextCard++];
		}

		private void FinishDealer()
		{
			while (DealerScore.Total < 17)
			{
				dealerCards.Add(Draw());
			}
			Resolve((DealerScore.IsBust || PlayerScore.Total > DealerScore.Total) ? BlackjackOutcome.Win : ((PlayerScore.Total == DealerScore.Total) ? BlackjackOutcome.Push : BlackjackOutcome.Loss));
		}

		private void Resolve(BlackjackOutcome outcome)
		{
			RequireState(BlackjackRoundState.PlayerTurn);
			if (Result != null)
			{
				throw new InvalidOperationException("The round has already resolved.");
			}
			Result = new BlackjackResult(RoundId, outcome, PlayerScore, DealerScore);
			State = BlackjackRoundState.Resolved;
		}

		private void RequireState(BlackjackRoundState required)
		{
			if (State != required)
			{
				throw new InvalidOperationException($"This operation requires {required}; the round is {State}.");
			}
		}
	}
	public sealed class CardSnapshot
	{
		public Suit Suit { get; set; }

		public Rank Rank { get; set; }
	}
	public sealed class RoundSnapshot
	{
		public const int CurrentVersion = 1;

		public int Version { get; set; } = 1;

		public Guid RoundId { get; set; }

		public string EscrowId { get; set; } = string.Empty;

		public float WagerValue { get; set; }

		public CardSnapshot[] Deck { get; set; } = Array.Empty<CardSnapshot>();

		public PlayerAction[] Actions { get; set; } = Array.Empty<PlayerAction>();

		public BlackjackRoundState State { get; set; }
	}
	public sealed class BlackjackTableGate
	{
		public long Revision { get; private set; }

		public bool IsLocked { get; private set; }

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

		public Guid RoundId { get; private set; }

		public void RequireAvailable(long expectedRevision)
		{
			if (IsLocked)
			{
				throw new InvalidOperationException("The blackjack table is in use. Wait for the current hand to finish.");
			}
			if (expectedRevision != Revision)
			{
				throw new InvalidOperationException("The table changed before your deal arrived. Review the table and try again.");
			}
		}

		public void Claim(long expectedRevision, int actorId, Guid roundId)
		{
			RequireAvailable(expectedRevision);
			if (actorId < 0)
			{
				throw new ArgumentOutOfRangeException("actorId");
			}
			if (roundId == Guid.Empty)
			{
				throw new ArgumentException("A hand must have a round identity.", "roundId");
			}
			long revision = checked(Revision + 1);
			ActorId = actorId;
			RoundId = roundId;
			IsLocked = true;
			Revision = revision;
		}

		public void RequireActor(int actorId, Guid roundId)
		{
			if (!IsLocked || actorId != ActorId || roundId != RoundId)
			{
				throw new InvalidOperationException("Only the player who dealt this hand can control it.");
			}
		}

		public void Advance(int actorId, Guid roundId)
		{
			RequireActor(actorId, roundId);
			checked
			{
				Revision++;
			}
		}

		public void Release(int actorId, Guid roundId)
		{
			RequireActor(actorId, roundId);
			long revision = checked(Revision + 1);
			IsLocked = false;
			Revision = revision;
		}

		public void Reset()
		{
			long revision = checked(Revision + 1);
			IsLocked = false;
			ActorId = -1;
			RoundId = Guid.Empty;
			Revision = revision;
		}
	}
	public enum Suit
	{
		Clubs,
		Diamonds,
		Hearts,
		Spades
	}
	public enum Rank
	{
		Ace = 1,
		Two,
		Three,
		Four,
		Five,
		Six,
		Seven,
		Eight,
		Nine,
		Ten,
		Jack,
		Queen,
		King
	}
	public readonly struct Card : IEquatable<Card>
	{
		public Suit Suit { get; }

		public Rank Rank { get; }

		public bool IsValid
		{
			get
			{
				if (Suit >= Suit.Clubs && Suit <= Suit.Spades && Rank >= Rank.Ace)
				{
					return Rank <= Rank.King;
				}
				return false;
			}
		}

		public Card(Suit suit, Rank rank)
		{
			if (suit < Suit.Clubs || suit > Suit.Spades)
			{
				throw new ArgumentOutOfRangeException("suit");
			}
			if (rank < Rank.Ace || rank > Rank.King)
			{
				throw new ArgumentOutOfRangeException("rank");
			}
			Suit = suit;
			Rank = rank;
		}

		public bool Equals(Card other)
		{
			if (Suit == other.Suit)
			{
				return Rank == other.Rank;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is Card other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (int)((int)Suit * 13 + Rank);
		}

		public override string ToString()
		{
			return $"{Rank} of {Suit}";
		}

		public static bool operator ==(Card left, Card right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(Card left, Card right)
		{
			return !left.Equals(right);
		}
	}
	public readonly struct HandScore
	{
		public int Total { get; }

		public bool IsSoft { get; }

		public int CardCount { get; }

		public bool IsBust => Total > 21;

		public bool IsNatural
		{
			get
			{
				if (CardCount == 2)
				{
					return Total == 21;
				}
				return false;
			}
		}

		internal HandScore(int total, bool isSoft, int cardCount)
		{
			Total = total;
			IsSoft = isSoft;
			CardCount = cardCount;
		}
	}
	public static class HandEvaluator
	{
		public static HandScore Score(IEnumerable<Card> cards)
		{
			if (cards == null)
			{
				throw new ArgumentNullException("cards");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (Card card in cards)
			{
				if (!card.IsValid)
				{
					throw new ArgumentException("A hand contains an invalid card.", "cards");
				}
				checked
				{
					num += Math.Min(unchecked((int)card.Rank), 10);
					num3++;
				}
				if (card.Rank == Rank.Ace)
				{
					num2++;
				}
			}
			bool flag = num2 > 0 && num <= 11;
			return new HandScore(flag ? (num + 10) : num, flag, num3);
		}
	}
	public sealed class CardDeck
	{
		private readonly ReadOnlyCollection<Card> cards;

		public IReadOnlyList<Card> Cards => cards;

		public CardDeck(IEnumerable<Card> orderedCards)
		{
			if (orderedCards == null)
			{
				throw new ArgumentNullException("orderedCards");
			}
			Card[] array = orderedCards.Take(53).ToArray();
			if (array.Length != 52 || array.Any((Card card) => !card.IsValid) || array.Distinct().Count() != 52)
			{
				throw new ArgumentException("A deck must contain each of the 52 valid cards exactly once.", "orderedCards");
			}
			cards = Array.AsReadOnly(array);
		}

		public static CardDeck Ordered()
		{
			List<Card> list = new List<Card>(52);
			for (int i = 0; i < 4; i++)
			{
				for (int j = 1; j <= 13; j++)
				{
					list.Add(new Card((Suit)i, (Rank)j));
				}
			}
			return new CardDeck(list);
		}

		public static CardDeck Shuffled(Random random)
		{
			if (random == null)
			{
				throw new ArgumentNullException("random");
			}
			Card[] array = Ordered().Cards.ToArray();
			for (int num = array.Length - 1; num > 0; num--)
			{
				int num2 = random.Next(num + 1);
				if (num2 < 0 || num2 > num)
				{
					throw new ArgumentException("The random source returned an invalid index.", "random");
				}
				ref Card reference = ref array[num];
				ref Card reference2 = ref array[num2];
				Card card = array[num2];
				Card card2 = array[num];
				reference = card;
				reference2 = card2;
			}
			return new CardDeck(array);
		}
	}
	public enum BlackjackOutcome
	{
		Loss,
		Push,
		Win,
		Natural
	}
	public enum SettlementKind
	{
		Forfeit,
		ReturnOriginalItems,
		AwardItems
	}
	public static class PayoutRules
	{
		public const float MaximumWagerValue = 1000000f;

		public const float MaximumReturnMultiplier = 2.5f;

		public static void ValidateWagerValue(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value) || value <= 0f || value > 1000000f)
			{
				throw new ArgumentOutOfRangeException("value", "Wager value must be finite, positive, and at most 1,000,000.");
			}
		}

		public static float MultiplierFor(BlackjackOutcome outcome)
		{
			return outcome switch
			{
				BlackjackOutcome.Loss => 0f, 
				BlackjackOutcome.Push => 1f, 
				BlackjackOutcome.Win => 2f, 
				BlackjackOutcome.Natural => 2.5f, 
				_ => throw new ArgumentOutOfRangeException("outcome"), 
			};
		}

		public static void ValidateReturnMultiplier(float multiplier)
		{
			if (multiplier != 0f && multiplier != 1f && multiplier != 2f && multiplier != 2.5f)
			{
				throw new ArgumentOutOfRangeException("multiplier", "Only the four defined payout multipliers are supported.");
			}
		}

		public static float CalculateReturnValue(float wagerValue, float multiplier)
		{
			ValidateWagerValue(wagerValue);
			ValidateReturnMultiplier(multiplier);
			return wagerValue * multiplier;
		}
	}
	public sealed class ItemWager
	{
		public string EscrowId { get; }

		public float Value { get; }

		public ItemWager(string escrowId, float value)
		{
			EscrowId = Identifiers.Require(escrowId, "escrowId");
			PayoutRules.ValidateWagerValue(value);
			Value = value;
		}
	}
	public sealed class BlackjackResult
	{
		public Guid RoundId { get; }

		public BlackjackOutcome Outcome { get; }

		public HandScore PlayerScore { get; }

		public HandScore DealerScore { get; }

		public float TotalReturnMultiplier => PayoutRules.MultiplierFor(Outcome);

		public bool ReturnOriginalItems => Outcome == BlackjackOutcome.Push;

		internal BlackjackResult(Guid roundId, BlackjackOutcome outcome, HandScore playerScore, HandScore dealerScore)
		{
			RoundId = roundId;
			Outcome = outcome;
			PlayerScore = playerScore;
			DealerScore = dealerScore;
		}
	}
	public sealed class SettlementInstruction
	{
		public Guid SettlementId { get; }

		public string EscrowId { get; }

		public float WagerValue { get; }

		public SettlementKind Kind { get; }

		public float TotalReturnMultiplier { get; }

		public float TotalReturnValue => PayoutRules.CalculateReturnValue(WagerValue, TotalReturnMultiplier);

		internal SettlementInstruction(ItemWager wager, BlackjackResult result)
		{
			SettlementId = result.RoundId;
			EscrowId = wager.EscrowId;
			WagerValue = wager.Value;
			TotalReturnMultiplier = result.TotalReturnMultiplier;
			Kind = ((result.Outcome != BlackjackOutcome.Loss) ? (result.ReturnOriginalItems ? SettlementKind.ReturnOriginalItems : SettlementKind.AwardItems) : SettlementKind.Forfeit);
		}
	}
}

BepInEx/plugins/GamblersReach/GamblersReach.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using GamblersReach.Art;
using GamblersReach.Characters;
using GamblersReach.Content;
using GamblersReach.Core.Blackjack;
using GamblersReach.Core.Progression;
using GamblersReach.Core.Wildlife;
using GamblersReach.Encounters;
using GamblersReach.Gameplay;
using GamblersReach.Interface;
using GamblersReach.Networking;
using GamblersReach.Wildlife;
using GamblersReach.World;
using HarmonyLib;
using HowToFish.ExpansionKit;
using HowToFish.ExpansionKit.Assets;
using HowToFish.ExpansionKit.Packs;
using HowToFish.ExpansionKit.Progression;
using HowToFish.ExpansionKit.Runtime;
using HowToFish.ExpansionKit.Runtime.Commerce;
using HowToFish.ExpansionKit.Runtime.Content;
using HowToFish.ExpansionKit.Runtime.Persistence;
using HowToFish.ExpansionKit.Runtime.Rendering;
using HowToFish.ExpansionKit.Runtime.World;
using HowToFish.ExpansionKit.World;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("GamblersReach.Qa")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("GamblersReach")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.8.4.0")]
[assembly: AssemblyInformationalVersion("0.8.4")]
[assembly: AssemblyProduct("GamblersReach")]
[assembly: AssemblyTitle("GamblersReach")]
[assembly: AssemblyVersion("0.8.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GamblersReach
{
	internal static class ExpansionProfile
	{
		internal static string Folder => ExpansionSaveProfiles.Folder;
	}
	internal sealed class ExpansionRuntime : MonoBehaviour
	{
		private sealed class HarborPackAssets : IPackAssetFactory
		{
			private readonly AuthoredAssets assets;

			internal HarborPackAssets(AuthoredAssets assets)
			{
				this.assets = assets;
			}

			public GameObject Instantiate(string packKey, ArtRecipe art)
			{
				if (packKey != "gamblers_reach" || art.Bundle != "gamblersreach.models")
				{
					throw new InvalidOperationException("The harbor art adapter received another pack's asset.");
				}
				return assets.CreateVisual(Path.GetFileNameWithoutExtension(art.Prefab));
			}
		}

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

			public static Func<ItemDefinition, bool> <>9__65_2;

			public static ExternalLootBinder <>9__65_0;

			public static Func<NativeItemRegistrar, bool> <>9__66_0;

			public static Func<NativeItemRegistrar, bool> <>9__66_1;

			public static Func<NativeLootTable, bool> <>9__66_2;

			public static Func<LoadSceneMode, AsyncOperation?> <>9__67_2;

			internal bool <ConfigurePack>b__65_2(ItemDefinition item)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: 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_000b: Invalid comparison between Unknown and I4
				ItemKind kind = item.Kind;
				if (kind - 3 <= 1)
				{
					return true;
				}
				return false;
			}

			internal NativeLootTable <ConfigurePack>b__65_0(ExternalRecipeContext<LootRecipe> context)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Expected O, but got Unknown
				<>c__DisplayClass65_0 CS$<>8__locals3 = new <>c__DisplayClass65_0
				{
					context = context
				};
				return new NativeLootTable(((PackRecipe)CS$<>8__locals3.context.Recipe).Key, ((IEnumerable<LootDropRecipe>)CS$<>8__locals3.context.Recipe.Drops).Select((Func<LootDropRecipe, LootDrop>)((LootDropRecipe drop) => new LootDrop(CS$<>8__locals3.context.Installer.ResolveItem(ContentReference.Parse(drop.Item)), drop.FixedQuantity, drop.PerPlayerQuantity, ContentReference.Parse(drop.Item)))), (ManualLogSource)null);
			}

			internal bool <ContentInstalled>b__66_0(NativeItemRegistrar registrar)
			{
				return registrar.CollectionId == 48187;
			}

			internal bool <ContentInstalled>b__66_1(NativeItemRegistrar registrar)
			{
				return registrar.CollectionId == 48188;
			}

			internal bool <ContentInstalled>b__66_2(NativeLootTable table)
			{
				return table.Key == "angler_loot";
			}

			internal AsyncOperation? <BindPlatformWorld>b__67_2(LoadSceneMode mode)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return ReachWorld.LoadScene(7, mode);
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass65_0
		{
			public ExternalRecipeContext<LootRecipe> context;

			internal LootDrop <ConfigurePack>b__4(LootDropRecipe drop)
			{
				//IL_0011: 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_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Expected O, but got Unknown
				return new LootDrop(context.Installer.ResolveItem(ContentReference.Parse(drop.Item)), drop.FixedQuantity, drop.PerPlayerQuantity, ContentReference.Parse(drop.Item));
			}
		}

		private bool prepared;

		private AuthoredAssets? authoredAssets;

		private HarborSceneAssets? harborAssets;

		private bool disposed;

		private ChapterQuests? chapterQuests;

		internal static ExpansionRuntime? Instance { get; private set; }

		internal bool Ready
		{
			get
			{
				if (prepared && Object.op_Implicit((Object)(object)ExpansionPlatform.Current))
				{
					return ExpansionPlatform.Current.CoreReady;
				}
				return false;
			}
		}

		internal string Status { get; private set; } = "Preparing the extra island...";

		internal ArtFactory Art { get; private set; }

		internal ContentRegistry Content { get; private set; }

		internal EncounterCatalog Encounters { get; private set; }

		internal HarborLureCatalog Lures { get; private set; }

		internal NpcService Characters { get; private set; }

		internal ReachWorld World { get; private set; }

		internal ReachNetwork Network { get; private set; }

		internal ReachSession Session { get; private set; }

		internal ReachPanel Panel { get; private set; }

		internal NativeWorldServices NativeServices { get; private set; }

		internal IslandArtLayout? ArtLayout { get; private set; }

		internal HawkContent Hawks { get; private set; }

		private void Awake()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			Instance = this;
			Characters = new NpcService();
			NativeServices = new NativeWorldServices();
			World = new ReachWorld(Characters.CaptureTemplates, BuildWorld, SpawnCharacters, ClearSceneServices, NativeServices.Capture);
			World.IslandCreated += SpawnGrass;
			ExpansionPlatform.Current.RegisterExtension(new PackExtension("gamblers_reach", (Func<PackSource>)PrepareSource, (Action<PackContentOptions>)ConfigurePack)
			{
				ContentInstalled = ContentInstalled,
				BindWorld = BindPlatformWorld,
				PrepareGameplay = PrepareGameplay,
				GameplayReady = () => prepared,
				VerifyScene = VerifyPlatformScene,
				AcceptSavedRevision = HarborPackDefinition.AcceptSavedRevision
			});
			Hawks = new HawkContent(this);
		}

		private PackSource PrepareSource()
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			IslandManager val = Object.FindAnyObjectByType<IslandManager>();
			if (!Object.op_Implicit((Object)(object)val))
			{
				throw new InvalidOperationException("The native island manager was not found.");
			}
			World.Configure(val);
			Art = new ArtFactory();
			authoredAssets = new AuthoredAssets();
			harborAssets = new HarborSceneAssets();
			World.UseAuthoredScene(harborAssets.ScenePath, "GamblersReachHarbor");
			Content = new ContentRegistry();
			Content.Begin(authoredAssets.CreateVisual, authoredAssets.CreateVisual);
			Encounters = new EncounterCatalog();
			Encounters.Begin(authoredAssets.CreateVisual);
			return HarborPackDefinition.Create(World.Position);
		}

		private void ConfigurePack(PackContentOptions options)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			options.Assets = (IPackAssetFactory)(object)new HarborPackAssets(authoredAssets ?? throw new InvalidOperationException("The original harbor assets are unavailable."));
			foreach (ContentDescriptor descriptor in Content.Descriptors)
			{
				options.ExternalItems.Add(descriptor.Key, (ExternalItemBinder)((ExternalRecipeContext<ItemRecipe> context, NativeItemRegistrar registrar) => Content.Prepare(context.Recipe, registrar)));
			}
			foreach (ItemDefinition item in HarborDefinition.Value.Items.Where(delegate(ItemDefinition item)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: 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_000b: Invalid comparison between Unknown and I4
				ItemKind kind = item.Kind;
				return kind - 3 <= 1;
			}))
			{
				options.ExternalItems.Add(item.Key, (ExternalItemBinder)((ExternalRecipeContext<ItemRecipe> context, NativeItemRegistrar registrar) => Encounters.Prepare(context.Recipe, registrar)));
			}
			Dictionary<string, ExternalLootBinder> externalLoot = options.ExternalLoot;
			object obj = <>c.<>9__65_0;
			if (obj == null)
			{
				ExternalLootBinder val = (ExternalRecipeContext<LootRecipe> context) => new NativeLootTable(((PackRecipe)context.Recipe).Key, ((IEnumerable<LootDropRecipe>)context.Recipe.Drops).Select((Func<LootDropRecipe, LootDrop>)((LootDropRecipe drop) => new LootDrop(context.Installer.ResolveItem(ContentReference.Parse(drop.Item)), drop.FixedQuantity, drop.PerPlayerQuantity, ContentReference.Parse(drop.Item)))), (ManualLogSource)null);
				<>c.<>9__65_0 = val;
				obj = (object)val;
			}
			externalLoot.Add("angler_loot", (ExternalLootBinder)obj);
		}

		private void ContentInstalled(PackContentInstaller installer)
		{
			Content.Complete(installer.Registrars.Single((NativeItemRegistrar registrar) => registrar.CollectionId == 48187));
			Encounters.Complete(installer.Registrars.Single((NativeItemRegistrar registrar) => registrar.CollectionId == 48188));
			Encounters.BindLoot(installer.Loot.Tables.Single((NativeLootTable table) => table.Key == "angler_loot"));
			Lures = new HarborLureCatalog();
			Lures.Bind(installer.Lures ?? throw new InvalidOperationException("The shared harbor lures did not register."));
			NativeServices.PrepareHarpoonUpgrades(Content.HarpoonUpgrades);
			Network = new ReachNetwork(InstanceFinder.NetworkManager);
			Panel = ((Component)this).gameObject.AddComponent<ReachPanel>();
			Panel.Initialize(SendAction);
			Session = new ReachSession(Content, World, Network, Characters, Panel);
			Session.ChapterChanged += World.RefreshChapterGate;
		}

		private void BindPlatformWorld(ExpansionPlatform platform)
		{
			platform.World.RegisterExternalIsland("gamblers_reach:harbor", (Func<IslandInfo>)(() => World.Location ?? throw new InvalidOperationException("The harbor approach was not configured.")), (Func<bool>)(() => Session.ChapterState.CoordinatesClaimed), (Func<LoadSceneMode, AsyncOperation>)((LoadSceneMode mode) => ReachWorld.LoadScene(7, mode)), (Func<bool>)(() => Session.ChapterState.ExistingIslandFiveAccess));
			World.IslandCreated += delegate
			{
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				if (ArtLayout == null)
				{
					throw new InvalidOperationException("The harbor's authored scene did not bind.");
				}
				platform.World.MountScene("gamblers_reach:harbor", (byte)6, ArtLayout.Root.scene, ArtLayout.Root.transform);
			};
		}

		private void VerifyPlatformScene(MountedIsland scene)
		{
			if (scene.Reference != "gamblers_reach:harbor")
			{
				return;
			}
			NPC[] componentsInChildren = ((Component)scene.Root).GetComponentsInChildren<NPC>(true);
			foreach (NpcDefinition resident in HarborDefinition.Value.Npcs)
			{
				if (componentsInChildren.Count((NPC npc) => npc.ID == resident.Id) != 1)
				{
					throw new InvalidOperationException("An authored harbor NPC failed to mount: " + resident.Key);
				}
			}
			if (((Component)scene.Root).GetComponentsInChildren<Purchasable>(true).Length >= HarborDefinition.Value.Shops.Count)
			{
				return;
			}
			throw new InvalidOperationException("The harbor did not mount all its declared shop services.");
		}

		private IEnumerator PrepareGameplay()
		{
			Status = "Preparing native character and boat behavior...";
			yield return World.Prepare();
			if (!Characters.HasTemplates)
			{
				throw new InvalidOperationException("No compatible native NPC rigs were captured. See the template diagnostics in the log.");
			}
			if (!Characters.HasSaleQuest)
			{
				throw new InvalidOperationException("The native merchant quest was not captured: " + Characters.SaleQuestOrigin);
			}
			if (!NativeServices.IsPrepared)
			{
				throw new InvalidOperationException("Native services, grass or the Reel of Fortune could not be prepared.");
			}
			prepared = true;
			Status = "Gambler's Reach ready. A new route opens after island four.";
			Plugin.Log.LogInfo((object)Status);
		}

		private WorldLayout BuildWorld(Transform parent)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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)
			ArtLayout = (harborAssets ?? throw new InvalidOperationException("The authored harbor is unavailable.")).Bind(((Component)parent).gameObject);
			return new WorldLayout
			{
				PlayerSpawn = ArtLayout.PlayerSpawn + Vector3.up * World.PlayerSpawnClearance,
				BoatSpawn = ArtLayout.BoatSpawn,
				PlayerHeading = ArtLayout.PlayerHeading,
				TablePosition = ArtLayout.TablePosition,
				NpcPositions = new Dictionary<string, Vector3>(StringComparer.Ordinal)
				{
					["tackle"] = ArtLayout.TackleMakerPosition,
					["collector"] = ArtLayout.MarineCollectorPosition,
					["dealer"] = ArtLayout.DealerPosition
				}
			};
		}

		private void OnNpcInteraction(string key)
		{
			SendAction(ReachAction.Talk, key);
		}

		private void SpawnCharacters(Transform parent, WorldLayout layout)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			if (ArtLayout == null)
			{
				throw new InvalidOperationException("The authored island layout is missing.");
			}
			NativeServices.Spawn(parent, Content, ArtLayout.ShopPositions, ArtLayout.ShopHeadings, ArtLayout.SlotMachinePosition, ArtLayout.SlotMachineHeading);
			NativeServices.Cooking.Spawn(parent, ArtLayout.NativeDecorPositions["grill"], ArtLayout.NativeDecorHeadings["grill"]);
			Characters.Spawn(parent, layout.NpcPositions, OnNpcInteraction);
			Characters.Targets["tackle"].localRotation = Quaternion.Euler(0f, ArtLayout.NpcHeadings["tackle_maker"], 0f);
			Characters.Targets["collector"].localRotation = Quaternion.Euler(0f, ArtLayout.NpcHeadings["marine_collector"], 0f);
			Characters.Targets["dealer"].localRotation = Quaternion.Euler(0f, ArtLayout.NpcHeadings["blackjack_dealer"], 0f);
			NPC component = ((Component)Characters.Targets["collector"]).GetComponent<NPC>();
			((Component)component).GetComponent<ReachNpcMarker>().NativeChapter = true;
			chapterQuests = new ChapterQuests(component, Lures.Get("angler_lure"), Encounters.Get(192), Encounters.Get(193));
			Session.BindChapterQuests(chapterQuests);
			NativeBlackjackDisplay display = NativeServices.SpawnBlackjackPresentation(parent, layout.TablePosition, ArtLayout.BlackjackPlayerHeading);
			Panel.BindTable(parent, layout.TablePosition, ArtLayout.BlackjackPlayerHeading, display);
			Session.RefreshDeposit();
		}

		private void SpawnGrass()
		{
			if (ArtLayout == null)
			{
				throw new InvalidOperationException("The active harbor has no authored grass placement surface.");
			}
			NativeServices.Grass.Spawn(ArtLayout.Root.transform);
		}

		private void ClearSceneServices()
		{
			chapterQuests?.Dispose();
			chapterQuests = null;
			Characters.ClearScene();
			NativeServices.ClearScene();
			ArtLayout = null;
		}

		private void SendAction(ReachAction action, string argument)
		{
			try
			{
				Network.Send(action, argument);
			}
			catch (InvalidOperationException ex)
			{
				Plugin.Log.LogWarning((object)ex.Message);
				Panel.ShowError(ex.Message);
			}
		}

		private void Update()
		{
			if (Network != null)
			{
				Network.Tick();
			}
			if (Session != null)
			{
				Session.Tick();
			}
		}

		private void OnGUI()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready && MainMenuManager.IsInMenu)
			{
				GUI.Box(new Rect(16f, (float)(Screen.height - 80), (float)Math.Min(600, Screen.width - 32), 60f), "Gambler's Reach\n" + Status);
			}
		}

		private void OnDestroy()
		{
			if (!disposed)
			{
				disposed = true;
				Plugin.Log.LogInfo((object)"Disposing the expansion runtime.");
				prepared = false;
				if (World != null)
				{
					World.IslandCreated -= SpawnGrass;
				}
				if (Session != null && World != null)
				{
					Session.ChapterChanged -= World.RefreshChapterGate;
				}
				chapterQuests?.Dispose();
				Session?.Dispose();
				Network?.Dispose();
				World?.Dispose();
				Characters?.Dispose();
				NativeServices?.Dispose();
				Lures?.Dispose();
				Encounters?.Dispose();
				Content?.Dispose();
				authoredAssets?.Dispose();
				harborAssets?.Dispose();
				Art?.Dispose();
				Instance = null;
			}
		}
	}
	[BepInPlugin("friends.howtofish.gamblersreach", "Gambler's Reach", "0.8.4")]
	[BepInDependency("howtofish.expansionkit.runtime", "0.7.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "friends.howtofish.gamblersreach";

		public const string Name = "Gambler's Reach";

		public const string Version = "0.8.4";

		internal static readonly Guid SupportedGame = new Guid("91a1729d-8ab8-4d3b-afcf-c6edfa435491");

		private Harmony? patches;

		internal static ManualLogSource Log { get; private set; } = null;

		internal static Plugin Instance { get; private set; } = null;

		private void Awake()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Guid moduleVersionId = typeof(Item).Assembly.ManifestModule.ModuleVersionId;
			if (moduleVersionId != SupportedGame)
			{
				string text = $"Unsupported How to Fish build ({moduleVersionId}). Expected game 1.0.12. No expansion content was loaded.";
				Log.LogError((object)text);
				throw new NotSupportedException(text);
			}
			patches = new Harmony("friends.howtofish.gamblersreach");
			patches.PatchAll(typeof(Plugin).Assembly);
			RegisterContent();
			Log.LogInfo((object)("Gambler's Reach 0.8.4 loaded on Unity " + Application.unityVersion + "; game assembly verified."));
		}

		private void RegisterContent()
		{
			((Component)this).gameObject.AddComponent<ExpansionRuntime>();
		}
	}
}
namespace GamblersReach.World
{
	internal sealed class BlackjackDeposit : MonoBehaviour
	{
		internal const int MaximumItems = 12;

		private BoxCollider region;

		internal static BlackjackDeposit? Current { get; private set; }

		internal Vector3 Position => ((Component)region).transform.TransformPoint(region.center);

		internal bool Ready
		{
			get
			{
				if (Object.op_Implicit((Object)(object)region))
				{
					return ((Component)region).gameObject.activeInHierarchy;
				}
				return false;
			}
		}

		internal void Configure(BoxCollider volume)
		{
			if (!Object.op_Implicit((Object)(object)volume) || !((Collider)volume).isTrigger)
			{
				throw new ArgumentException("The blackjack deposit needs a dedicated trigger volume.", "volume");
			}
			if (Object.op_Implicit((Object)(object)Current) && (Object)(object)Current != (Object)(object)this)
			{
				throw new InvalidOperationException("Only one shared blackjack deposit may be active.");
			}
			int num = LayerMask.NameToLayer("Ignore Raycast");
			if (num < 0)
			{
				throw new InvalidOperationException("The wager sensor's non-interactive layer is unavailable.");
			}
			((Component)volume).gameObject.layer = num;
			((Component)volume).gameObject.tag = "Untagged";
			region = volume;
			Current = this;
		}

		internal Item[] ItemsSnapshot()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return Array.Empty<Item>();
			}
			Physics.SyncTransforms();
			Vector3 lossyScale = ((Component)region).transform.lossyScale;
			Vector3 val = Vector3.Scale(region.size * 0.5f, new Vector3(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z)));
			Collider[] array = Physics.OverlapBox(Position, val, ((Component)region).transform.rotation, LayerMask.op_Implicit(GameInfo.ItemLayer), (QueryTriggerInteraction)2);
			HashSet<Item> hashSet = new HashSet<Item>();
			Collider[] array2 = array;
			foreach (Collider val2 in array2)
			{
				Item value = null;
				if (!ItemManager.Items.TryGetValue(((Component)val2).transform, out value) && Object.op_Implicit((Object)(object)val2.attachedRigidbody))
				{
					ItemManager.Items.TryGetValue(((Component)val2.attachedRigidbody).transform, out value);
				}
				if (Object.op_Implicit((Object)(object)value))
				{
					hashSet.Add(value);
				}
			}
			return hashSet.OrderBy((Item item) => ((NetworkBehaviour)item).ObjectId).ToArray();
		}

		private void OnDestroy()
		{
			if ((Object)(object)Current == (Object)(object)this)
			{
				Current = null;
			}
		}
	}
	internal enum CumulativeShopKind
	{
		Weapon,
		Melee,
		Explosive,
		Sharpening,
		Rod,
		Utility,
		Lure,
		Pocket,
		Motor
	}
	internal sealed class CumulativeShopOffer
	{
		internal string Key { get; }

		internal string Reference => "native_" + Key.Substring("legacy_".Length);

		internal CumulativeShopKind Kind { get; }

		internal byte? NativeItemId { get; }

		internal byte? NativeLureIndex { get; }

		internal byte? NativeTier { get; }

		internal string NativeName { get; }

		internal string? SourceName { get; }

		internal int? FixedPrice { get; }

		internal CumulativeShopOffer(string key, CumulativeShopKind kind, byte? nativeItemId, string nativeName, int? fixedPrice, byte? nativeLureIndex = null, byte? nativeTier = null, string? sourceName = null)
		{
			Key = key;
			Kind = kind;
			NativeItemId = nativeItemId;
			NativeName = nativeName;
			FixedPrice = fixedPrice;
			NativeLureIndex = nativeLureIndex;
			NativeTier = nativeTier;
			SourceName = sourceName;
		}
	}
	internal readonly struct CumulativeShopPlacement
	{
		internal Vector3 Surface { get; }

		internal float Heading { get; }

		internal CumulativeShopPlacement(Vector3 surface, float heading)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Surface = surface;
			Heading = heading;
		}
	}
	internal readonly struct CumulativeShopFootprint
	{
		internal float Width { get; }

		internal float Height { get; }

		internal float Depth { get; }

		internal CumulativeShopFootprint(Vector3 size)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Width = size.x;
			Height = size.y;
			Depth = size.z;
		}

		public override string ToString()
		{
			return $"{Width:0.00}w x {Depth:0.00}d x {Height:0.00}h m";
		}
	}
	internal sealed class CumulativeHarborShops : IDisposable
	{
		private sealed class Template
		{
			internal Purchasable Stand;

			internal Quaternion Tilt;

			internal CumulativeShopFootprint Footprint;

			internal string Source = "";
		}

		private static readonly CumulativeShopOffer[] Island4CarryForward;

		internal static readonly IReadOnlyList<string> ExistingUpgradeCoverage;

		private readonly GameObject nursery;

		private readonly Dictionary<string, Item> nativeItems = new Dictionary<string, Item>(StringComparer.Ordinal);

		private readonly Dictionary<string, BaitInfo> nativeLures = new Dictionary<string, BaitInfo>(StringComparer.Ordinal);

		private readonly Dictionary<string, Template> templates = new Dictionary<string, Template>(StringComparer.Ordinal);

		private readonly Dictionary<string, Purchasable> stands = new Dictionary<string, Purchasable>(StringComparer.Ordinal);

		private readonly Dictionary<string, float> headings = new Dictionary<string, float>(StringComparer.Ordinal);

		private GameObject? spawnedRoot;

		private bool disposed;

		internal IReadOnlyList<CumulativeShopOffer> Offers => Array.AsReadOnly(Island4CarryForward);

		internal IReadOnlyList<string> RequiredMarkerKeys => Array.AsReadOnly(Island4CarryForward.Select((CumulativeShopOffer offer) => offer.Key).ToArray());

		internal IReadOnlyDictionary<string, Purchasable> Stands => stands;

		internal IReadOnlyDictionary<string, string> Sources => new ReadOnlyDictionary<string, string>(templates.ToDictionary<KeyValuePair<string, Template>, string, string>((KeyValuePair<string, Template> pair) => pair.Key, (KeyValuePair<string, Template> pair) => pair.Value.Source, StringComparer.Ordinal));

		internal IReadOnlyDictionary<string, CumulativeShopFootprint> Footprints => new ReadOnlyDictionary<string, CumulativeShopFootprint>(templates.ToDictionary<KeyValuePair<string, Template>, string, CumulativeShopFootprint>((KeyValuePair<string, Template> pair) => pair.Key, (KeyValuePair<string, Template> pair) => pair.Value.Footprint, StringComparer.Ordinal));

		internal IReadOnlyList<string> Missing
		{
			get
			{
				Template value;
				return (from offer in Island4CarryForward
					where !templates.TryGetValue(offer.Key, out value) || !Object.op_Implicit((Object)(object)value.Stand)
					select offer.Key).ToArray();
			}
		}

		internal bool IsPrepared
		{
			get
			{
				if (!disposed)
				{
					return Missing.Count == 0;
				}
				return false;
			}
		}

		internal CumulativeHarborShops()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			ValidateCatalogShape();
			nursery = new GameObject("Inactive Island4 cumulative shop templates");
			nursery.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)nursery);
		}

		internal object DescribeTemplates()
		{
			return new
			{
				Footprints = Enumerable.ToDictionary(Footprints, (KeyValuePair<string, CumulativeShopFootprint> pair) => pair.Key, (KeyValuePair<string, CumulativeShopFootprint> pair) => new
				{
					pair.Value.Width,
					pair.Value.Depth,
					pair.Value.Height
				}, StringComparer.Ordinal),
				Sources = Sources,
				Offers = Offers.Select((CumulativeShopOffer offer) => new
				{
					Key = offer.Key,
					Kind = offer.Kind.ToString(),
					NativeItemId = offer.NativeItemId,
					NativeLureIndex = offer.NativeLureIndex,
					NativeTier = offer.NativeTier,
					NativeName = offer.NativeName,
					FixedPrice = offer.FixedPrice
				}).ToArray()
			};
		}

		internal static IReadOnlyList<CumulativeHarborShops> FindSpawned(Transform parent)
		{
			if (!Object.op_Implicit((Object)(object)parent))
			{
				throw new ArgumentNullException("parent");
			}
			return (from binding in ((Component)parent).GetComponentsInChildren<CumulativeShopBinding>(true)
				where binding.Shops != null && !binding.Shops.disposed
				select binding.Shops).Distinct().ToArray();
		}

		internal void Capture(Scene scene)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			ThrowIfDisposed();
			if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded)
			{
				throw new ArgumentException("Cumulative shop capture requires a loaded native scene.", "scene");
			}
			if (!string.Equals(((Scene)(ref scene)).name, "Island4", StringComparison.Ordinal) || IsPrepared)
			{
				return;
			}
			if (templates.Count != 0)
			{
				throw new InvalidOperationException("The native Island4 cumulative shops were captured more than once.");
			}
			ResolveNativeAssets();
			Purchasable[] source = ((Scene)(ref scene)).GetRootGameObjects().SelectMany((GameObject root) => root.GetComponentsInChildren<Purchasable>(true)).ToArray();
			bool flag = false;
			try
			{
				CumulativeShopOffer[] island4CarryForward = Island4CarryForward;
				foreach (CumulativeShopOffer offer in island4CarryForward)
				{
					Purchasable[] array = source.Where((Purchasable candidate) => Matches(candidate, offer) && (offer.SourceName == null || ((Object)((Component)candidate).gameObject).name == offer.SourceName)).ToArray();
					if (array.Length != 1)
					{
						throw new InvalidOperationException($"Native Island4 must contain exactly one {offer.Key} display; found {array.Length}.");
					}
					CaptureTemplate(scene, offer, array[0]);
				}
				ValidateNativeCatalog();
				flag = true;
			}
			finally
			{
				if (!flag)
				{
					foreach (Template value in templates.Values)
					{
						if (Object.op_Implicit((Object)(object)value.Stand))
						{
							Object.Destroy((Object)(object)((Component)value.Stand).gameObject);
						}
					}
					templates.Clear();
					nativeItems.Clear();
					nativeLures.Clear();
				}
			}
		}

		internal void Spawn(Transform parent, IReadOnlyDictionary<string, CumulativeShopPlacement> placements)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			ThrowIfDisposed();
			if (!IsPrepared)
			{
				throw new InvalidOperationException("Missing verified Island4 cumulative shops: " + string.Join(", ", Missing));
			}
			if (!Object.op_Implicit((Object)(object)parent) || placements == null)
			{
				throw new ArgumentException("Cumulative shops require a parent and placement catalog.");
			}
			if (Object.op_Implicit((Object)(object)spawnedRoot) || stands.Values.Any((Purchasable stand) => Object.op_Implicit((Object)(object)stand)))
			{
				throw new InvalidOperationException("Clear cumulative shops before rebuilding them.");
			}
			CumulativeShopOffer[] island4CarryForward = Island4CarryForward;
			foreach (CumulativeShopOffer cumulativeShopOffer in island4CarryForward)
			{
				if (!placements.TryGetValue(cumulativeShopOffer.Key, out var value) || !Finite(value.Surface) || !NativeContentAccess.Finite(value.Heading))
				{
					throw new InvalidOperationException("Missing or invalid cumulative shop marker: " + cumulativeShopOffer.Key);
				}
			}
			ValidateNativeCatalog();
			spawnedRoot = new GameObject("Island4 cumulative harbor shops");
			spawnedRoot.SetActive(false);
			spawnedRoot.transform.SetParent(parent, false);
			spawnedRoot.AddComponent<CumulativeShopBinding>().Shops = this;
			bool flag = false;
			try
			{
				island4CarryForward = Island4CarryForward;
				foreach (CumulativeShopOffer cumulativeShopOffer2 in island4CarryForward)
				{
					Template template = templates[cumulativeShopOffer2.Key];
					Purchasable component = NativeSceneMeshes.Clone(((Component)template.Stand).gameObject, spawnedRoot.transform).GetComponent<Purchasable>();
					CumulativeShopPlacement cumulativeShopPlacement = placements[cumulativeShopOffer2.Key];
					((Object)component).name = "Native Island4 " + cumulativeShopOffer2.Key;
					((Component)component).transform.localPosition = cumulativeShopPlacement.Surface;
					((Component)component).transform.localRotation = Quaternion.Euler(0f, cumulativeShopPlacement.Heading, 0f) * template.Tilt;
					ValidateReferences(component);
					if (!Matches(component, cumulativeShopOffer2))
					{
						throw new InvalidOperationException("A placed cumulative shop lost its native identity: " + cumulativeShopOffer2.Key);
					}
					SeatOnSurface(component, spawnedRoot.transform, cumulativeShopPlacement.Surface, cumulativeShopOffer2.Kind == CumulativeShopKind.Motor);
					stands.Add(cumulativeShopOffer2.Key, component);
					headings.Add(cumulativeShopOffer2.Key, cumulativeShopPlacement.Heading);
					((Component)component).gameObject.SetActive(true);
				}
				spawnedRoot.SetActive(true);
				flag = true;
			}
			finally
			{
				if (!flag)
				{
					Clear();
				}
			}
		}

		internal Vector3 ApproachDirection(string key)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			ThrowIfDisposed();
			if (!Object.op_Implicit((Object)(object)spawnedRoot) || !headings.TryGetValue(key, out var value))
			{
				throw new InvalidOperationException("The cumulative shop is not spawned: " + key);
			}
			Vector3 val = Vector3.ProjectOnPlane(spawnedRoot.transform.TransformDirection(Quaternion.Euler(0f, value, 0f) * Vector3.forward), Vector3.up);
			return ((Vector3)(ref val)).normalized;
		}

		internal void ValidateLiveStand(CumulativeShopOffer offer)
		{
			ValidateNativeCatalog();
			if (!Island4CarryForward.Contains(offer) || !stands.TryGetValue(offer.Key, out Purchasable value) || !Object.op_Implicit((Object)(object)value) || !((Behaviour)value).isActiveAndEnabled || !Matches(value, offer))
			{
				throw new InvalidOperationException("The cumulative native display is missing or inactive: " + offer.Key);
			}
			ValidateReferences(value);
			if (NativeContentAccess.Get<bool>(value, "_isFree"))
			{
				throw new InvalidOperationException("A cumulative native display became free: " + offer.Key);
			}
			int? fixedPrice = offer.FixedPrice;
			if (fixedPrice.HasValue)
			{
				int valueOrDefault = fixedPrice.GetValueOrDefault();
				if (NativeContentAccess.Get<int>(value, "_customCost") != valueOrDefault)
				{
					throw new InvalidOperationException("A cumulative native display has the wrong price: " + offer.Key);
				}
			}
			if (offer.Kind == CumulativeShopKind.Sharpening && NativeContentAccess.Get<byte>(value, "_maxSharpnessUpgrade") != 12)
			{
				throw new InvalidOperationException("The sharpening stand no longer has Island4's tier-12 cap.");
			}
			if (offer.Kind == CumulativeShopKind.Pocket && NativeContentAccess.Get<byte>(value, "_maxSlot") != offer.NativeTier)
			{
				throw new InvalidOperationException("The pocket stand no longer has Island4's three-slot cap.");
			}
			if (offer.Kind == CumulativeShopKind.Motor && NativeContentAccess.Get<byte>(value, "_motorIndex") != offer.NativeTier)
			{
				throw new InvalidOperationException("A motor stand no longer has its native boat tier: " + offer.Key);
			}
		}

		internal void ValidateNativeCatalog()
		{
			ThrowIfDisposed();
			if (nativeItems.Count != Island4CarryForward.Count((CumulativeShopOffer offer) => offer.NativeItemId.HasValue))
			{
				throw new InvalidOperationException("The native cumulative item catalog is incomplete.");
			}
			if (nativeLures.Count != Island4CarryForward.Count((CumulativeShopOffer offer) => offer.NativeLureIndex.HasValue))
			{
				throw new InvalidOperationException("The native cumulative lure catalog is incomplete.");
			}
			foreach (CumulativeShopOffer item in Island4CarryForward.Where((CumulativeShopOffer offer) => offer.NativeItemId.HasValue))
			{
				Item val = nativeItems[item.Key];
				if ((Object)(object)GameInfo.IDToItem(item.NativeItemId.Value) != (Object)(object)val || ((Object)val).name != item.NativeName || val.Cost != item.FixedPrice)
				{
					throw new InvalidOperationException("Native item identity changed after capture: " + item.Key);
				}
				RequireItemFamily(val, item);
			}
			foreach (CumulativeShopOffer item2 in Island4CarryForward.Where((CumulativeShopOffer offer) => offer.NativeLureIndex.HasValue))
			{
				BaitInfo val2 = nativeLures[item2.Key];
				byte? nativeLureIndex = item2.NativeLureIndex;
				if (nativeLureIndex.HasValue)
				{
					byte valueOrDefault = nativeLureIndex.GetValueOrDefault();
					if (valueOrDefault < GameInfo.AllBaits.Count && !((Object)(object)GameInfo.AllBaits[valueOrDefault] != (Object)(object)val2) && GameInfo.GetIndexOfBait(val2) == valueOrDefault && !(((Object)val2).name != item2.NativeName) && val2.Cost == item2.FixedPrice)
					{
						RequireLure(val2, item2);
						continue;
					}
				}
				throw new InvalidOperationException("Native lure identity changed after capture: " + item2.Key);
			}
		}

		internal void Clear()
		{
			if (Object.op_Implicit((Object)(object)spawnedRoot))
			{
				spawnedRoot.GetComponent<CumulativeShopBinding>().Shops = null;
				spawnedRoot.SetActive(false);
				Object.Destroy((Object)(object)spawnedRoot);
			}
			spawnedRoot = null;
			stands.Clear();
			headings.Clear();
		}

		private void ResolveNativeAssets()
		{
			HashSet<Item> hashSet = new HashSet<Item>(Resources.LoadAll<Item>("Items"));
			HashSet<BaitInfo> hashSet2 = new HashSet<BaitInfo>(Resources.LoadAll<BaitInfo>("Baits"));
			if (hashSet.Count == 0 || hashSet2.Count == 0)
			{
				throw new InvalidOperationException("Initialize native GameInfo before capturing cumulative shops.");
			}
			foreach (CumulativeShopOffer item in Island4CarryForward.Where((CumulativeShopOffer offer) => offer.NativeItemId.HasValue))
			{
				Item val = GameInfo.IDToItem(item.NativeItemId.Value);
				if (!Object.op_Implicit((Object)(object)val) || !hashSet.Contains(val) || ((Object)val).name != item.NativeName || val.Cost != item.FixedPrice || val.IsQuestItem || val is DeadPlayer || (val is Creature && (item.Key != "legacy_beer" || ((object)val).GetType() != typeof(Creature))))
				{
					throw new InvalidOperationException($"Native item {item.NativeItemId} differs from '{item.NativeName}' at ${item.FixedPrice}: " + $"name={((val != null) ? ((Object)val).name : null)}, type={((object)val)?.GetType().Name}, cost={((val != null) ? new int?(val.Cost) : ((int?)null))}, quest={((val != null) ? new bool?(val.IsQuestItem) : ((bool?)null))}, " + $"resource={Object.op_Implicit((Object)(object)val) && hashSet.Contains(val)}, creature={val is Creature}, player={val is DeadPlayer}.");
				}
				RequireItemFamily(val, item);
				nativeItems.Add(item.Key, val);
			}
			foreach (CumulativeShopOffer item2 in Island4CarryForward.Where((CumulativeShopOffer offer) => offer.NativeLureIndex.HasValue))
			{
				byte value = item2.NativeLureIndex.Value;
				if (value >= GameInfo.AllBaits.Count)
				{
					throw new InvalidOperationException("Native lure index is unavailable: " + item2.Key);
				}
				BaitInfo val2 = GameInfo.AllBaits[value];
				if (!Object.op_Implicit((Object)(object)val2) || !hashSet2.Contains(val2) || GameInfo.GetIndexOfBait(val2) != value || ((Object)val2).name != item2.NativeName || val2.Cost != item2.FixedPrice)
				{
					throw new InvalidOperationException($"Native lure {value} is not Island4 stock '{item2.NativeName}' at ${item2.FixedPrice}.");
				}
				RequireLure(val2, item2);
				nativeLures.Add(item2.Key, val2);
			}
		}

		private void CaptureTemplate(Scene scene, CumulativeShopOffer offer, Purchasable source)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00af: 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)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			ValidateReferences(source);
			if (NativeContentAccess.Get<bool>(source, "_isFree") || !NativeContentAccess.Get<bool>(source, "_customCanBuy"))
			{
				throw new InvalidOperationException("Island4 source is not an enabled paid purchase: " + offer.Key);
			}
			Purchasable component = NativeSceneMeshes.Clone(((Component)source).gameObject, nursery.transform).GetComponent<Purchasable>();
			bool flag = false;
			try
			{
				((Object)component).name = "Native Island4 template " + offer.Key;
				((Component)component).transform.localPosition = Vector3.zero;
				((Component)component).transform.localScale = ((Component)source).transform.lossyScale;
				Quaternion val = Quaternion.Inverse(Quaternion.Euler(0f, ((Component)source).transform.eulerAngles.y, 0f)) * ((Component)source).transform.rotation;
				((Component)component).transform.localRotation = val;
				NativeContentAccess.Set(component, "_isHovering", false);
				NativeContentAccess.Set(component, "_customCanBuy", true);
				ValidateReferences(component);
				if (!Matches(component, offer))
				{
					throw new InvalidOperationException("Cloning changed the Island4 purchase identity: " + offer.Key);
				}
				Bounds val2 = VisibleBounds(component, nursery.transform);
				templates.Add(offer.Key, new Template
				{
					Stand = component,
					Tilt = val,
					Footprint = new CumulativeShopFootprint(((Bounds)(ref val2)).size),
					Source = ((Scene)(ref scene)).name + ":" + Hierarchy(((Component)source).transform)
				});
				flag = true;
			}
			finally
			{
				if (!flag && Object.op_Implicit((Object)(object)component))
				{
					Object.Destroy((Object)(object)((Component)component).gameObject);
				}
			}
		}

		private bool Matches(Purchasable stand, CumulativeShopOffer offer)
		{
			return offer.Kind switch
			{
				CumulativeShopKind.Sharpening => ((object)stand).GetType() == typeof(SharpnessPurchasable) && NativeContentAccess.Get<byte>(stand, "_maxSharpnessUpgrade") == 12, 
				CumulativeShopKind.Lure => ((object)stand).GetType() == typeof(BaitPurchasable) && (Object)(object)NativeContentAccess.Optional<BaitInfo>(stand, "_bait") == (Object)(object)nativeLures[offer.Key], 
				CumulativeShopKind.Pocket => ((object)stand).GetType() == typeof(SlotPurchasable) && NativeContentAccess.Get<byte>(stand, "_maxSlot") == offer.NativeTier, 
				CumulativeShopKind.Motor => ((object)stand).GetType() == typeof(MotorPurchasable) && NativeContentAccess.Get<byte>(stand, "_motorIndex") == offer.NativeTier && NativeContentAccess.Get<int>(stand, "_customCost") == offer.FixedPrice, 
				_ => ((object)stand).GetType() == typeof(ItemPurchasable) && (Object)(object)NativeContentAccess.Optional<Item>(stand, "_itemToPurchase") == (Object)(object)nativeItems[offer.Key], 
			};
		}

		private static void RequireItemFamily(Item item, CumulativeShopOffer offer)
		{
			bool flag;
			switch (offer.Kind)
			{
			case CumulativeShopKind.Weapon:
				flag = item is Weapon;
				break;
			case CumulativeShopKind.Melee:
				flag = item is Melee;
				break;
			case CumulativeShopKind.Explosive:
				flag = item is Explosive;
				break;
			case CumulativeShopKind.Rod:
				flag = ((offer.Key == "legacy_cast_rod") ? (item is FishingRodCast) : (offer.Key == "legacy_crab_rod" && item is FishingRodCrab));
				break;
			case CumulativeShopKind.Utility:
			{
				bool flag2 = !(item is Weapon) && !(item is Melee) && !(item is FishingRod) && !(item is Explosive);
				if (flag2)
				{
					flag2 = offer.Key switch
					{
						"legacy_radio" => item is Radio, 
						"legacy_disc" => item is Disc, 
						"legacy_map" => item is Map, 
						"legacy_beer" => ((object)item).GetType() == typeof(Creature), 
						"legacy_badball" => true, 
						_ => false, 
					};
				}
				flag = flag2;
				break;
			}
			default:
				flag = false;
				break;
			}
			if (!flag)
			{
				throw new InvalidOperationException($"Native item {offer.NativeName} does not match {offer.Kind}.");
			}
		}

		private static void RequireLure(BaitInfo lure, CumulativeShopOffer offer)
		{
			if (offer.Kind != CumulativeShopKind.Lure || !Object.op_Implicit((Object)(object)lure.Mesh) || !Object.op_Implicit((Object)(object)lure.MeshForNpc) || lure.ItemWeights == null || lure.ItemWeights.Count == 0 || !lure.ItemWeights.Any((ItemInfoWeight entry) => entry != null && entry.Weight > 0f && Object.op_Implicit((Object)(object)entry.Fishable) && entry.Fishable.ItemToSpawn is Creature))
			{
				throw new InvalidOperationException("Native lure " + offer.NativeName + " has no usable native catch table.");
			}
		}

		private static void ValidateCatalogShape()
		{
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			if (Island4CarryForward.Length != 25 || Island4CarryForward.Select((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Key).Distinct<string>(StringComparer.Ordinal).Count() != 25 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Weapon) != 4 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Melee) != 2 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Explosive) != 1 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Sharpening) != 1 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Rod) != 2 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Utility) != 5 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Lure) != 7 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Pocket) != 1 || Island4CarryForward.Count((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.Kind == CumulativeShopKind.Motor) != 2 || Island4CarryForward.Any((CumulativeShopOffer cumulativeShopOffer) => cumulativeShopOffer.NativeName.Contains("Scientific") || cumulativeShopOffer.NativeName == "Coconut"))
			{
				throw new InvalidOperationException("The Island4 cumulative shop catalog is malformed.");
			}
			CumulativeShopOffer[] island4CarryForward = Island4CarryForward;
			foreach (CumulativeShopOffer offer in island4CarryForward)
			{
				ShopDefinition val = HarborDefinition.Value.Shops.Single((ShopDefinition shop) => shop.Key == offer.Key);
				string text;
				switch (offer.Kind)
				{
				case CumulativeShopKind.Weapon:
				case CumulativeShopKind.Melee:
				case CumulativeShopKind.Explosive:
				case CumulativeShopKind.Rod:
				case CumulativeShopKind.Utility:
					text = "NativeItem";
					break;
				case CumulativeShopKind.Lure:
					text = "NativeLure";
					break;
				case CumulativeShopKind.Sharpening:
					text = "Sharpening";
					break;
				case CumulativeShopKind.Pocket:
					text = "Pocket";
					break;
				case CumulativeShopKind.Motor:
					text = "Motor";
					break;
				default:
					throw new InvalidOperationException("Unknown cumulative definition kind: " + offer.Kind);
				}
				string text2 = text;
				int num2 = ((int?)offer.NativeItemId) ?? ((int?)offer.NativeLureIndex) ?? (-1);
				string text3 = ((num2 >= 0) ? offer.NativeName : "");
				if (((object)val.Kind/*cast due to .constrained prefix*/).ToString() != text2 || val.Reference != offer.Reference || val.NativeId != num2 || val.NativeName != text3)
				{
					throw new InvalidOperationException("The SDK definition differs from the verified native shop: " + offer.Key);
				}
			}
		}

		private static void ValidateReferences(Purchasable stand)
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			Collider val = NativeContentAccess.Get<Collider>(stand, "_interactCol");
			GameObject[] array = NativeContentAccess.Get<GameObject[]>(stand, "_modelsToOutline");
			if (!Object.op_Implicit((Object)(object)val) || !val.enabled || !((Component)val).transform.IsChildOf(((Component)stand).transform) || !((Component)val).CompareTag("Interactable") || !Object.op_Implicit((Object)(object)((Interactable)stand).TextTarget) || !((Interactable)stand).TextTarget.IsChildOf(((Component)stand).transform) || array.Length == 0 || array.Any((GameObject model) => !Object.op_Implicit((Object)(object)model) || !model.transform.IsChildOf(((Component)stand).transform)) || ((Component)stand).GetComponentsInChildren<Purchasable>(true).Length != 1 || ((Component)stand).GetComponentsInChildren<NetworkBehaviour>(true).Length != 0 || ((Component)stand).GetComponentsInChildren<NPC>(true).Length != 0)
			{
				throw new InvalidOperationException("Native cumulative shop has invalid or external references: " + ((Object)stand).name);
			}
			VisibleBounds(stand, ((Component)stand).transform);
		}

		private static void SeatOnSurface(Purchasable stand, Transform parent, Vector3 surface, bool centerFootprint)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_0026: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			Bounds val = VisibleBounds(stand, parent);
			Transform transform = ((Component)stand).transform;
			transform.localPosition += new Vector3(centerFootprint ? (surface.x - ((Bounds)(ref val)).center.x) : 0f, surface.y - ((Bounds)(ref val)).min.y, centerFootprint ? (surface.z - ((Bounds)(ref val)).center.z) : 0f);
		}

		private static Bounds VisibleBounds(Purchasable stand, Transform relativeTo)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			MeshRenderer val3 = default(MeshRenderer);
			MeshFilter[] array = (from filter in ((Component)stand).GetComponentsInChildren<MeshFilter>(true)
				where Object.op_Implicit((Object)(object)filter.sharedMesh) && ActiveBelow(((Component)filter).transform, ((Component)stand).transform) && ((Component)filter).TryGetComponent<MeshRenderer>(ref val3) && ((Renderer)val3).enabled
				select filter).ToArray();
			if (array.Length == 0)
			{
				throw new InvalidOperationException("Native cumulative shop has no visible owned geometry: " + ((Object)stand).name);
			}
			Bounds? val = null;
			MeshFilter[] array2 = array;
			foreach (MeshFilter val2 in array2)
			{
				Bounds value = NativeSceneMeshes.TransformBounds(val2.sharedMesh.bounds, relativeTo.worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix);
				if (!Finite(((Bounds)(ref value)).min) || !Finite(((Bounds)(ref value)).max))
				{
					throw new InvalidOperationException("Native cumulative shop has invalid geometry bounds: " + ((Object)stand).name);
				}
				if (val.HasValue)
				{
					Bounds valueOrDefault = val.GetValueOrDefault();
					((Bounds)(ref valueOrDefault)).Encapsulate(((Bounds)(ref value)).min);
					((Bounds)(ref valueOrDefault)).Encapsulate(((Bounds)(ref value)).max);
					val = valueOrDefault;
				}
				else
				{
					val = value;
				}
			}
			return val.Value;
		}

		private static bool ActiveBelow(Transform node, Transform root)
		{
			while ((Object)(object)node != (Object)(object)root)
			{
				if (!Object.op_Implicit((Object)(object)node) || !((Component)node).gameObject.activeSelf)
				{
					return false;
				}
				node = node.parent;
			}
			return true;
		}

		private static string Hierarchy(Transform node)
		{
			List<string> list = new List<string>();
			while (Object.op_Implicit((Object)(object)node))
			{
				list.Add(((Object)node).name);
				node = node.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (NativeContentAccess.Finite(value.x) && NativeContentAccess.Finite(value.y))
			{
				return NativeContentAccess.Finite(value.z);
			}
			return false;
		}

		private void ThrowIfDisposed()
		{
			if (disposed)
			{
				throw new ObjectDisposedException("CumulativeHarborShops");
			}
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			Clear();
			disposed = true;
			foreach (Template value in templates.Values)
			{
				if (Object.op_Implicit((Object)(object)value.Stand))
				{
					Object.Destroy((Object)(object)((Component)value.Stand).gameObject);
				}
			}
			templates.Clear();
			nativeItems.Clear();
			Object.Destroy((Object)(object)nursery);
		}

		static CumulativeHarborShops()
		{
			CumulativeShopOffer[] obj = new CumulativeShopOffer[25]
			{
				new CumulativeShopOffer("legacy_brass_knuckles", CumulativeShopKind.Melee, (byte)57, "Brass Knuckles", 24),
				new CumulativeShopOffer("legacy_pistol", CumulativeShopKind.Weapon, (byte)66, "Pistol", 50),
				new CumulativeShopOffer("legacy_shotgun", CumulativeShopKind.Weapon, (byte)68, "Shotgun", 150),
				new CumulativeShopOffer("legacy_smg", CumulativeShopKind.Weapon, (byte)69, "Smg", 650),
				new CumulativeShopOffer("legacy_sniper_rifle", CumulativeShopKind.Weapon, (byte)70, "Sniper Rifle", 3800),
				new CumulativeShopOffer("legacy_knife", CumulativeShopKind.Melee, (byte)63, "Knife", 45),
				new CumulativeShopOffer("legacy_dynamite", CumulativeShopKind.Explosive, (byte)60, "Dynamite", 25),
				new CumulativeShopOffer("legacy_sharpening", CumulativeShopKind.Sharpening, null, "Sharpness (Purchasable)", null, null, null, "Sharpness (Purchasable)"),
				new CumulativeShopOffer("legacy_cast_rod", CumulativeShopKind.Rod, (byte)61, "Fishing Rod", 3),
				new CumulativeShopOffer("legacy_crab_rod", CumulativeShopKind.Rod, (byte)59, "Crab Fishing Rod", 3),
				new CumulativeShopOffer("legacy_badball", CumulativeShopKind.Utility, (byte)55, "Badball", 3),
				new CumulativeShopOffer("legacy_beer", CumulativeShopKind.Utility, (byte)56, "Beer", 12),
				new CumulativeShopOffer("legacy_radio", CumulativeShopKind.Utility, (byte)67, "Radio", 10),
				new CumulativeShopOffer("legacy_disc", CumulativeShopKind.Utility, (byte)62, "Disc", 10),
				new CumulativeShopOffer("legacy_map", CumulativeShopKind.Utility, (byte)65, "Map", 10),
				new CumulativeShopOffer("legacy_hotdog_bait", CumulativeShopKind.Lure, null, "HotDog", 1, (byte)4),
				new CumulativeShopOffer("legacy_beginner_lure", CumulativeShopKind.Lure, null, "Beginner Lure", 3, (byte)6),
				new CumulativeShopOffer("legacy_standard_lure", CumulativeShopKind.Lure, null, "Standard Lure", 15, (byte)8),
				new CumulativeShopOffer("legacy_professional_lure", CumulativeShopKind.Lure, null, "Professional Lure", 50, (byte)10),
				new CumulativeShopOffer("legacy_beginner_boss_lure", CumulativeShopKind.Lure, null, "Beginner Boss Lure", 40, (byte)9),
				new CumulativeShopOffer("legacy_standard_boss_lure", CumulativeShopKind.Lure, null, "Standard Boss Lure", 280, (byte)12),
				new CumulativeShopOffer("legacy_professional_boss_lure", CumulativeShopKind.Lure, null, "Professional Boss Lure", 1200, (byte)15),
				null,
				null,
				null
			};
			byte? nativeTier = (byte)3;
			obj[22] = new CumulativeShopOffer("legacy_pocket_slot", CumulativeShopKind.Pocket, null, "Slot3 (Purchasable)", null, null, nativeTier, "Slot3 (Purchasable)");
			int? fixedPrice = 230;
			nativeTier = (byte)1;
			obj[23] = new CumulativeShopOffer("legacy_big_motor", CumulativeShopKind.Motor, null, "BigMotor (Purchasable)", fixedPrice, null, nativeTier, "BigMotor (Purchasable)");
			int? fixedPrice2 = 860;
			nativeTier = (byte)2;
			obj[24] = new CumulativeShopOffer("legacy_dual_motors", CumulativeShopKind.Motor, null, "DualMotors (Purchasable)", fixedPrice2, null, nativeTier, "DualMotors (Purchasable)");
			Island4CarryForward = obj;
			ExistingUpgradeCoverage = Array.AsReadOnly(new string[7] { "harpoon_red_dot_sight", "harpoon_sniper_scope", "harpoon_compensator", "harpoon_suppressor", "harpoon_laser_sight", "harpoon_extended_mag", "harpoon_ammunition" });
		}
	}
	internal sealed class CumulativeShopBinding : MonoBehaviour
	{
		internal CumulativeHarborShops? Shops { get; set; }
	}
	internal readonly struct HarpoonUpgradePlacement
	{
		internal Vector3 Surface { get; }

		internal float Heading { get; }

		internal HarpoonUpgradePlacement(Vector3 surface, float heading)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Surface = surface;
			Heading = heading;
		}
	}
	internal sealed class HarpoonUpgradeShops : IDisposable
	{
		private const string AmmunitionPolicy = "gamblers_reach:ammunition";

		private readonly HarpoonUpgradeCatalog catalog;

		private readonly IDisposable ammunitionPolicy;

		private readonly GameObject nursery;

		private readonly Dictionary<AttachmentInfo, AttachmentPurchasable> attachments = new Dictionary<AttachmentInfo, AttachmentPurchasable>();

		private readonly Dictionary<string, Purchasable> stands = new Dictionary<string, Purchasable>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> sources = new Dictionary<string, string>(StringComparer.Ordinal);

		private BulletPurchasable? ammunition;

		private bool disposed;

		internal IReadOnlyDictionary<string, Purchasable> Stands => stands;

		internal IReadOnlyDictionary<string, string> Sources => sources;

		internal IReadOnlyList<string> MissingFamilies => (from offer in catalog.Offers
			where Object.op_Implicit((Object)(object)offer.Information) && !attachments.ContainsKey(offer.Information)
			select offer.Key).Concat(Object.op_Implicit((Object)(object)ammunition) ? ((IEnumerable<string>)Array.Empty<string>()) : ((IEnumerable<string>)new string[1] { "harpoon_ammunition" })).ToArray();

		internal bool IsPrepared
		{
			get
			{
				if (!disposed)
				{
					return MissingFamilies.Count == 0;
				}
				return false;
			}
		}

		internal HarpoonUpgradeShops(HarpoonUpgradeCatalog catalog)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			this.catalog = catalog ?? throw new ArgumentNullException("catalog");
			nursery = new GameObject("Reach inactive native harpoon upgrade templates");
			nursery.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)nursery);
			ammunitionPolicy = AmmunitionPurchasePolicies.Register("gamblers_reach:ammunition", (Func<Weapon, byte?>)MaximumAmmunitionTier);
		}

		private static byte? MaximumAmmunitionTier(Weapon weapon)
		{
			ReachWorld? current = ReachWorld.Current;
			if (current == null || !current.IsActive)
			{
				return null;
			}
			if (((Item)weapon).ID == 188)
			{
				return (byte)12;
			}
			if (((Item)weapon).ID >= 86)
			{
				return null;
			}
			ReachSession? current2 = ReachSession.Current;
			return (byte)((current2 != null && current2.CanVisitIslandFive) ? 12u : 9u);
		}

		internal void Capture(Scene scene)
		{
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			ThrowIfDisposed();
			if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded)
			{
				throw new ArgumentException("Harpoon upgrade capture requires a loaded native scene.", "scene");
			}
			Purchasable[] array = ((Scene)(ref scene)).GetRootGameObjects().SelectMany((GameObject root) => root.GetComponentsInChildren<Purchasable>(true)).ToArray();
			foreach (Purchasable val in array)
			{
				AttachmentPurchasable val2 = (AttachmentPurchasable)(object)((val is AttachmentPurchasable) ? val : null);
				if (val2 != null)
				{
					AttachmentInfo information = NativeContentAccess.Get<AttachmentInfo>(val2, "_info");
					HarpoonUpgradeOffer harpoonUpgradeOffer = catalog.Offers.SingleOrDefault((HarpoonUpgradeOffer harpoonUpgradeOffer2) => (Object)(object)harpoonUpgradeOffer2.Information == (Object)(object)information);
					if (harpoonUpgradeOffer != null && !attachments.ContainsKey(information))
					{
						HarpoonUpgradeCatalog.RequireNativeIdentity(information);
						AttachmentPurchasable value = (AttachmentPurchasable)CloneOwned(val);
						attachments.Add(information, value);
						sources.Add(harpoonUpgradeOffer.Key, ((Scene)(ref scene)).name + ":" + ((Object)val).name);
					}
				}
				else if (val is BulletPurchasable && !Object.op_Implicit((Object)(object)ammunition))
				{
					ammunition = (BulletPurchasable)CloneOwned(val);
					NativeContentAccess.Set(ammunition, "_maxBulletUpgrade", (byte)9);
					sources.Add("harpoon_ammunition", ((Scene)(ref scene)).name + ":" + ((Object)val).name);
				}
			}
		}

		internal void Spawn(Transform parent, Weapon target, IReadOnlyDictionary<string, HarpoonUpgradePlacement> layout)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			ThrowIfDisposed();
			if (!IsPrepared)
			{
				throw new InvalidOperationException("Missing native harpoon purchasable families: " + string.Join(", ", MissingFamilies) + ". Capture native Island4 or Island5 before spawning.");
			}
			if (!Object.op_Implicit((Object)(object)parent) || !Object.op_Implicit((Object)(object)target) || ((Item)target).ID != 188)
			{
				throw new ArgumentException("Harpoon upgrade stands require an authored parent and the registered weapon-188 prefab.");
			}
			if (stands.Values.Any((Purchasable val3) => Object.op_Implicit((Object)(object)val3)))
			{
				throw new InvalidOperationException("Harpoon upgrade stands have already been spawned; call ClearSpawned before rebuilding.");
			}
			foreach (string standKey in catalog.StandKeys)
			{
				if (!layout.TryGetValue(standKey, out var value) || !Finite(value.Surface) || !NativeContentAccess.Finite(value.Heading))
				{
					throw new InvalidOperationException("Missing or invalid harpoon shelf marker: " + standKey);
				}
			}
			stands.Clear();
			bool flag = false;
			try
			{
				foreach (string key in catalog.StandKeys)
				{
					HarpoonUpgradeOffer harpoonUpgradeOffer = catalog.Offers.First((HarpoonUpgradeOffer harpoonUpgradeOffer2) => harpoonUpgradeOffer2.StandKey == key);
					Purchasable val = Object.Instantiate<Purchasable>((Purchasable)(Object.op_Implicit((Object)(object)harpoonUpgradeOffer.Information) ? ((object)attachments[harpoonUpgradeOffer.Information]) : ((object)ammunition)), parent, false);
					stands.Add(key, val);
					((Object)val).name = "Reach native " + key;
					HarpoonUpgradePlacement harpoonUpgradePlacement = layout[key];
					((Component)val).transform.localPosition = harpoonUpgradePlacement.Surface;
					((Component)val).transform.localRotation = Quaternion.Euler(0f, harpoonUpgradePlacement.Heading, 0f);
					ValidateOwnedReferences(val);
					SeatOnSurface(val, parent, harpoonUpgradePlacement.Surface.y);
					BulletPurchasable val2 = (BulletPurchasable)(object)((val is BulletPurchasable) ? val : null);
					if (val2 != null)
					{
						AmmunitionPurchasePolicies.Bind(val2, "gamblers_reach:ammunition");
					}
					((Component)val).gameObject.SetActive(true);
				}
				flag = true;
			}
			finally
			{
				if (!flag)
				{
					ClearSpawned();
				}
			}
		}

		internal void ClearSpawned()
		{
			foreach (Purchasable value in stands.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
			stands.Clear();
		}

		private Purchasable CloneOwned(Purchasable source)
		{
			ValidateOwnedReferences(source);
			Purchasable component = NativeSceneMeshes.Clone(((Component)source).gameObject, nursery.transform).GetComponent<Purchasable>();
			NativeContentAccess.Set(component, "_isFree", false);
			NativeContentAccess.Set(component, "_customCanBuy", true);
			NativeContentAccess.Set(component, "_customCost", 0);
			NativeContentAccess.Set(component, "_isHovering", false);
			ValidateOwnedReferences(component);
			return component;
		}

		private static void ValidateOwnedReferences(Purchasable shop)
		{
			Collider val = NativeContentAccess.Get<Collider>(shop, "_interactCol");
			GameObject[] array = NativeContentAccess.Get<GameObject[]>(shop, "_modelsToOutline");
			if (!Object.op_Implicit((Object)(object)val) || !((Component)val).transform.IsChildOf(((Component)shop).transform) || !((Component)val).CompareTag("Interactable") || !Object.op_Implicit((Object)(object)((Interactable)shop).TextTarget) || !((Interactable)shop).TextTarget.IsChildOf(((Component)shop).transform) || array.Length == 0 || array.Any((GameObject model) => !Object.op_Implicit((Object)(object)model) || !model.transform.IsChildOf(((Component)shop).transform)))
			{
				throw new InvalidOperationException("Native upgrade stand has external or invalid interaction/card/outline references: " + ((Object)shop).name);
			}
			if (((Component)shop).GetComponentsInChildren<Purchasable>(true).Length != 1 || ((Component)shop).GetComponentsInChildren<NetworkBehaviour>(true).Length != 0)
			{
				throw new InvalidOperationException("Expected one non-networked native purchase display; actual purchases use Server's native RPCs.");
			}
		}

		private static void SeatOnSurface(Purchasable stand, Transform parent, float height)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			MeshFilter[] array = (from filter in ((Component)stand).GetComponentsInChildren<MeshFilter>(true)
				where ActiveBelow(((Component)filter).transform, ((Component)stand).transform)
				select filter).ToArray();
			if (array.Length == 0)
			{
				throw new InvalidOperationException("Native upgrade shelf display contains no visible geometry: " + ((Object)stand).name);
			}
			float num = array.Min(delegate(MeshFilter filter)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_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_002e: Unknown result type (might be due to invalid IL or missing references)
				Bounds val = TransformBounds(filter.sharedMesh.bounds, parent.worldToLocalMatrix * ((Component)filter).transform.localToWorldMatrix);
				return ((Bounds)(ref val)).min.y;
			});
			Transform transform = ((Component)stand).transform;
			transform.localPosition += Vector3.up * (height - num);
		}

		private static bool ActiveBelow(Transform child, Transform root)
		{
			Transform val = child;
			while ((Object)(object)val != (Object)(object)root)
			{
				if (!((Component)val).gameObject.activeSelf)
				{
					return false;
				}
				val = val.parent;
			}
			return true;
		}

		private static Bounds TransformBounds(Bounds bounds, Matrix4x4 matrix)
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			Bounds result = default(Bounds);
			((Bounds)(ref result))..ctor(((Matrix4x4)(ref matrix)).MultiplyPoint3x4(((Bounds)(ref bounds)).center), Vector3.zero);
			for (int i = 0; i < 8; i++)
			{
				((Bounds)(ref result)).Encapsulate(((Matrix4x4)(ref matrix)).MultiplyPoint3x4(((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((i & 1) != 0) ? 1 : (-1)), (float)(((i & 2) != 0) ? 1 : (-1)), (float)(((i & 4) != 0) ? 1 : (-1))))));
			}
			return result;
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (NativeContentAccess.Finite(value.x) && NativeContentAccess.Finite(value.y))
			{
				return NativeContentAccess.Finite(value.z);
			}
			return false;
		}

		private void ThrowIfDisposed()
		{
			if (disposed)
			{
				throw new ObjectDisposedException("HarpoonUpgradeShops");
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				ClearSpawned();
				ammunitionPolicy.Dispose();
				Object.Destroy((Object)(object)nursery);
			}
		}
	}
	internal sealed class NativeCasinoPresentation : IDisposable
	{
		private readonly GameObject nursery;

		private GameObject? canvasTemplate;

		private GameObject? highlightTemplate;

		private Vector3 canvasPosition;

		private Quaternion canvasRotation;

		private Vector3 highlightPosition;

		private Quaternion highlightRotation;

		private Color winColor;

		private float pulse;

		internal bool Ready
		{
			get
			{
				if (Object.op_Implicit((Object)(object)canvasTemplate))
				{
					return Object.op_Implicit((Object)(object)highlightTemplate);
				}
				return false;
			}
		}

		internal NativeCasinoPresentation()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			nursery = new GameObject("Inactive native casino presentation");
			nursery.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)nursery);
		}

		internal void Capture(Scene scene)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Expected O, but got Unknown
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				return;
			}
			LocalCasino val = ((Scene)(ref scene)).GetRootGameObjects().SelectMany((GameObject root) => root.GetComponentsInChildren<LocalCasino>(true)).FirstOrDefault();
			if (Object.op_Implicit((Object)(object)val))
			{
				Transform val2 = NativeContentAccess.Get<Transform>(val, "_rouletteTableHolder");
				TextMeshProUGUI val3 = NativeContentAccess.Get<TextMeshProUGUI>(val, "_curWorthText");
				CasinoBox componentInChildren = ((Component)val2).GetComponentInChildren<CasinoBox>(true);
				if (!Object.op_Implicit((Object)(object)((Graphic)val3).canvas) || !Object.op_Implicit((Object)(object)componentInChildren))
				{
					throw new InvalidOperationException("The native roulette presentation is incomplete.");
				}
				BoxCollider val4 = NativeContentAccess.Get<BoxCollider>(componentInChildren, "_col");
				MeshFilter component = ((Component)componentInChildren).GetComponent<MeshFilter>();
				MeshRenderer component2 = ((Component)componentInChildren).GetComponent<MeshRenderer>();
				if (!Object.op_Implicit((Object)(object)val4) || !Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)component.sharedMesh) || component.sharedMesh.vertexCount != 24)
				{
					throw new InvalidOperationException("The native roulette wager highlight is not the verified standalone cube.");
				}
				Quaternion val5 = Quaternion.Euler(0f, 90f, 0f);
				canvasPosition = val5 * val2.InverseTransformPoint(((Component)((Graphic)val3).canvas).transform.position);
				canvasRotation = val5 * Quaternion.Inverse(val2.rotation) * ((Component)((Graphic)val3).canvas).transform.rotation;
				highlightPosition = val5 * val2.InverseTransformPoint(((Component)componentInChildren).transform.position);
				highlightRotation = val5 * Quaternion.Inverse(val2.rotation) * ((Component)componentInChildren).transform.rotation;
				canvasTemplate = Object.Instantiate<GameObject>(((Component)((Graphic)val3).canvas).gameObject, nursery.transform, false);
				canvasTemplate.SetActive(false);
				LocalizeStringEvent[] componentsInChildren = canvasTemplate.GetComponentsInChildren<LocalizeStringEvent>(true);
				for (int num = 0; num < componentsInChildren.Length; num++)
				{
					((Behaviour)componentsInChildren[num]).enabled = false;
				}
				highlightTemplate = new GameObject("Native tabletop wager highlight", new Type[3]
				{
					typeof(MeshFilter),
					typeof(MeshRenderer),
					typeof(BoxCollider)
				});
				highlightTemplate.transform.SetParent(nursery.transform, false);
				highlightTemplate.transform.localScale = Vector3.Scale(((Component)componentInChildren).transform.lossyScale, new Vector3(1f / val2.lossyScale.x, 1f / val2.lossyScale.y, 1f / val2.lossyScale.z));
				highlightTemplate.GetComponent<MeshFilter>().sharedMesh = component.sharedMesh;
				((Renderer)highlightTemplate.GetComponent<MeshRenderer>()).sharedMaterials = ((Renderer)component2).sharedMaterials;
				BoxCollider component3 = highlightTemplate.GetComponent<BoxCollider>();
				component3.size = val4.size;
				component3.center = val4.center;
				((Collider)component3).isTrigger = true;
				((Collider)component3).enabled = false;
				winColor = NativeContentAccess.Get<Color>(val, "_wonColor");
				pulse = NativeContentAccess.Get<float>(val, "_scaleMultiplier");
				Plugin.Log.LogInfo((object)("Captured native roulette typography, floating amount and tabletop highlight from " + ((Scene)(ref scene)).name + "."));
			}
		}

		internal NativeBlackjackDisplay Spawn(Transform parent, Vector3 tableSurface, float heading)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				throw new InvalidOperationException("Native roulette presentation was not captured before island creation.");
			}
			GameObject val = new GameObject("Blackjack native presentation");
			val.SetActive(false);
			val.transform.SetParent(parent, false);
			val.transform.localPosition = tableSurface - Vector3.up;
			val.transform.localRotation = Quaternion.Euler(0f, heading, 0f);
			GameObject obj = Object.Instantiate<GameObject>(canvasTemplate, val.transform, false);
			obj.transform.localPosition = canvasPosition;
			obj.transform.localRotation = canvasRotation;
			GameObject val2 = Object.Instantiate<GameObject>(highlightTemplate, val.transform, false);
			((Object)val2).name = "Blackjack deposited fish volume";
			val2.transform.localPosition = highlightPosition;
			val2.transform.localRotation = highlightRotation;
			FitHighlight(parent, val.transform, val2.transform);
			BoxCollider component = val2.GetComponent<BoxCollider>();
			val2.AddComponent<BlackjackDeposit>().Configure(component);
			TextMeshProUGUI[] componentsInChildren = obj.GetComponentsInChildren<TextMeshProUGUI>(true);
			NativeBlackjackDisplay nativeBlackjackDisplay = obj.AddComponent<NativeBlackjackDisplay>();
			nativeBlackjackDisplay.Configure(componentsInChildren.Single((TextMeshProUGUI text) => ((Object)text).name == "CurWorthText"), componentsInChildren.Single((TextMeshProUGUI text) => ((Object)text).name == "PlaceBetsText"), winColor, pulse);
			obj.SetActive(true);
			val.SetActive(true);
			return nativeBlackjackDisplay;
		}

		private static void FitHighlight(Transform island, Transform table, Transform highlight)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			Bounds val = default(Bounds);
			bool flag = false;
			MeshFilter[] componentsInChildren = ((Component)island).GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val2 in componentsInChildren)
			{
				MeshRenderer component = ((Component)val2).GetComponent<MeshRenderer>();
				if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)val2.sharedMesh))
				{
					continue;
				}
				Material[] sharedMaterials = ((Renderer)component).sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if (!Object.op_Implicit((Object)(object)sharedMaterials[j]) || !((Object)sharedMaterials[j]).name.StartsWith("felt_green", StringComparison.Ordinal))
					{
						continue;
					}
					Mesh sharedMesh = val2.sharedMesh;
					Matrix4x4 val3 = table.worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix;
					Vector3[] vertices = sharedMesh.vertices;
					foreach (int item in sharedMesh.GetTriangles(j).Distinct())
					{
						Vector3 val4 = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(vertices[item]);
						if (!flag)
						{
							val = new Bounds(val4, Vector3.zero);
							flag = true;
						}
						else
						{
							((Bounds)(ref val)).Encapsulate(val4);
						}
					}
				}
			}
			if (!flag || ((Bounds)(ref val)).size.x < 1f || ((Bounds)(ref val)).size.z < 0.5f || ((Bounds)(ref val)).size.y > 0.1f)
			{
				throw new InvalidOperationException("The authored blackjack felt has no valid surface bounds.");
			}
			Mesh sharedMesh2 = ((Component)highlight).GetComponent<MeshFilter>().sharedMesh;
			Bounds val5 = NativeSceneMeshes.TransformBounds(sharedMesh2.bounds, Matrix4x4.TRS(Vector3.zero, highlight.localRotation, highlight.localScale));
			float num = Mathf.Min(new float[3]
			{
				1f,
				(((Bounds)(ref val)).size.x - 0.03f) / ((Bounds)(ref val5)).size.x,
				(((Bounds)(ref val)).size.z - 0.03f) / ((Bounds)(ref val5)).size.z
			});
			Vector3 localScale = highlight.localScale;
			highlight.localScale = new Vector3(localScale.x * num, localScale.y, localScale.z * num);
			val5 = NativeSceneMeshes.TransformBounds(sharedMesh2.bounds, Matrix4x4.TRS(Vector3.zero, highlight.localRotation, highlight.localScale));
			Vector3 localPosition = highlight.localPosition;
			localPosition.x = Mathf.Clamp(localPosition.x, ((Bounds)(ref val)).min.x + 0.015f - ((Bounds)(ref val5)).min.x, ((Bounds)(ref val)).max.x - 0.015f - ((Bounds)(ref val5)).max.x);
			localPosition.z = Mathf.Clamp(localPosition.z, ((Bounds)(ref val)).min.z + 0.015f - ((Bounds)(ref val5)).min.z, ((Bounds)(ref val)).max.z - 0.015f - ((Bounds)(ref val5)).max.z);
			localPosition.y = ((Bounds)(ref val)).max.y + 0.005f - ((Bounds)(ref val5)).min.y;
			highlight.localPosition = localPosition;
		}

		public void Dispose()
		{
			Object.Destroy((Object)(object)nursery);
		}
	}
	internal sealed class NativeBlackjackDisplay : MonoBehaviour
	{
		private TextMeshProUGUI worth;

		private TextMeshProUGUI prompt;

		private Color winColor;

		private float pulse;

		private double displayed;

		private long target;

		private string lastRound = "";

		private bool wasLocked;

		private float pulseRemaining;

		internal long TargetValue => target;

		internal void Configure(TextMeshProUGUI amount, TextMeshProUGUI label, Color winnerColor, float nativePulse)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			worth = amount;
			prompt = label;
			winColor = winnerColor;
			pulse = nativePulse;
			((TMP_Text)worth).text = "$0";
			((Graphic)worth).color = Color.white;
			((TMP_Text)prompt).text = "Place items here";
		}

		internal void Show(PanelView state)
		{
			//IL_0093: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			bool num = wasLocked && !state.TableLocked && state.Round == lastRound;
			target = state.DepositValue;
			if (num)
			{
				bool flag = state.PayoutMultiplier > 1f;
				((Graphic)worth).color = (flag ? winColor : Color.white);
				pulseRemaining = (flag ? 0.5f : 0f);
			}
			else if (state.TableLocked || state.Round != lastRound)
			{
				((Graphic)worth).color = Color.white;
			}
			string text = state.ActingPlayerName.Replace("<", "").Replace(">", "");
			if (text.Length > 24)
			{
				text = text.Substring(0, 24);
			}
			((TMP_Text)prompt).text = ((state.DepositMessage.Length > 0) ? state.DepositMessage : (state.TableLocked ? (text + " is playing") : ((state.Round.Length == 0) ? "Place items here" : ((state.DepositItemCount == 0) ? (state.Outcome + "\nPlace another bet") : (state.Outcome + "\nCollect or DEAL again")))));
			lastRound = state.Round;
			wasLocked = state.TableLocked;
		}

		private void Update()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)worth))
			{
				double num = (double)target - displayed;
				displayed = ((Math.Abs(num) < 1.0) ? ((double)target) : (displayed + num * (1.0 - Math.Exp(-12f * Time.unscaledDeltaTime))));
				((TMP_Text)worth).text = "$" + Math.Round(displayed);
				if (pulseRemaining > 0f)
				{
					pulseRemaining = Mathf.Max(0f, pulseRemaining - Time.unscaledDeltaTime);
					((TMP_Text)worth).transform.localScale = Vector3.one * (1f + pulse * Mathf.Sin(pulseRemaining * MathF.PI * 2f));
				}
				else
				{
					((TMP_Text)worth).transform.localScale = Vector3.one;
				}
			}
		}
	}
	internal readonly struct NativeGrassTuning
	{
		internal readonly float MeshSize;

		internal readonly float Density;

		internal readonly float PositionRandomness;

		internal readonly float RotationRandomness;

		internal readonly Vector2 ScaleRange;

		internal float Step => MeshSize / Density;

		internal NativeGrassTuning(float meshSize, float density, float positionRandomness, float rotationRandomness, Vector2 scaleRange)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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)
			if (meshSize <= 0f || density <= 0f || positionRandomness < 0f || positionRandomness > 1f || rotationRandomness < 0f || rotationRandomness > 360f || scaleRange.x <= 0f || scaleRange.y < scaleRange.x)
			{
				throw new InvalidOperationException($"The native grass decoration tuning is invalid: size {meshSize}, density {density}, " + $"jitter {positionRandomness}, roll {rotationRandomness}, scale {scaleRange}.");
			}
			MeshSize = meshSize;
			Density = density;
			PositionRandomness = positionRandomness;
			RotationRandomness = rotationRandomness;
			ScaleRange = scaleRange;
		}

		public override string ToString()
		{
			return $"size {MeshSize}, density {Density} ({Step:0.###}m grid), jitter {PositionRandomness}, roll +/-{RotationRandomness} degrees, scale {ScaleRange.x}-{ScaleRange.y}";
		}
	}
	internal sealed class GrassPlacementStats
	{
		internal int Candidates;

		internal int OffGrass;

		internal int Steep;

		internal int Low;

		internal int Edge;

		internal int Covered;

		internal int Blocked;

		internal int Thinned;

		internal int Accepted;

		public override string ToString()
		{
			return $"{Accepted} accepted of {Candidates} candidates (off grass {OffGrass}, steep {Steep}, low {Low}, path/edge {Edge}, " + $"covered {Covered}, blocked {Blocked}, thinned {Thinned})";
		}
	}
	internal sealed class NativeGrassPlacement
	{
		internal const float MinNormalY = 0.78f;

		internal co