Decompiled source of RamRoyalFlush v0.2.21

BepInEx/plugins/RamRoyalFlush/RamRoyalFlush.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using SoftReferenceableAssets;
using UnityEngine;
using UnityEngine.Audio;

[assembly: AssemblyFileVersion("0.2.21.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.2.20.0")]
namespace RamRoyalFlush;

internal sealed class AudioSequenceRules
{
	private bool requested;

	private bool waitForCompletion;

	private bool expired;

	internal bool CompletionSeen { get; private set; }

	internal bool Played { get; private set; }

	internal void Complete()
	{
		CompletionSeen = true;
	}

	internal void Request(bool fartEnabled)
	{
		if (!requested)
		{
			requested = true;
			waitForCompletion = fartEnabled;
		}
	}

	internal bool TryStart(bool fartActive, bool paused)
	{
		if (expired || Played || !requested || paused || fartActive || (waitForCompletion && !CompletionSeen))
		{
			return false;
		}
		Played = true;
		return true;
	}

	internal void Expire()
	{
		expired = true;
	}
}
public sealed class CompletionAudio : MonoBehaviour
{
	internal enum Readiness
	{
		Waiting,
		Ready,
		Failed,
		Cancelled
	}

	private static readonly Dictionary<string, CompletionAudio> pending = new Dictionary<string, CompletionAudio>();

	private readonly AudioSequenceRules order = new AudioSequenceRules();

	private string key;

	private AudioSource fart;

	private double fartEnd;

	private double drainSeconds;

	private double stoppedAt = -1.0;

	private int lastSample;

	private bool finishedLogged;

	private double expires;

	private Player player;

	private GameObject presentation;

	private bool localBarrier;

	private bool expectedFart;

	private bool observedPlaying;

	private bool cancelled;

	private bool interruptedPlayback;

	internal static void BeginLocal(string key, bool fartEnabled)
	{
		CompletionAudio completionAudio = Get(key);
		completionAudio.localBarrier = true;
		completionAudio.expectedFart = fartEnabled;
	}

	internal static bool WantsFart(string key, bool configured)
	{
		CompletionAudio completionAudio = Get(key);
		if (!completionAudio.cancelled && (completionAudio.localBarrier ? completionAudio.expectedFart : configured))
		{
			return !completionAudio.order.Played;
		}
		return false;
	}

	internal static bool IsCancelled(string key)
	{
		if (pending.TryGetValue(key, out var value) && Object.op_Implicit((Object)(object)value))
		{
			return value.cancelled;
		}
		return false;
	}

	internal static void BindPresentation(string key, GameObject go)
	{
		CompletionAudio completionAudio = Get(key);
		if (completionAudio.cancelled)
		{
			Object.Destroy((Object)(object)go);
		}
		else
		{
			completionAudio.presentation = go;
		}
	}

	internal static Readiness Status(string key)
	{
		if (!pending.TryGetValue(key, out var value) || !Object.op_Implicit((Object)(object)value))
		{
			return Readiness.Failed;
		}
		if (value.cancelled)
		{
			return Readiness.Cancelled;
		}
		if (AudioListener.pause)
		{
			return Readiness.Waiting;
		}
		if (!value.expectedFart)
		{
			return Readiness.Ready;
		}
		if (!value.order.CompletionSeen)
		{
			return Readiness.Waiting;
		}
		if (!Object.op_Implicit((Object)(object)value.fart))
		{
			return Readiness.Failed;
		}
		return value.PlaybackStatus();
	}

	private Readiness PlaybackStatus()
	{
		double dspTime = AudioSettings.dspTime;
		bool flag = Object.op_Implicit((Object)(object)fart) && fart.isPlaying;
		if (Object.op_Implicit((Object)(object)fart))
		{
			lastSample = Math.Max(lastSample, fart.timeSamples);
		}
		if (observedPlaying && !flag && dspTime < fartEnd - Math.Max(0.02, drainSeconds))
		{
			interruptedPlayback = true;
		}
		if (interruptedPlayback)
		{
			return Readiness.Failed;
		}
		observedPlaying |= flag;
		if (flag || dspTime < fartEnd)
		{
			return Readiness.Waiting;
		}
		if (!observedPlaying)
		{
			return Readiness.Failed;
		}
		if (stoppedAt < 0.0)
		{
			stoppedAt = dspTime;
		}
		if (dspTime < Math.Max(fartEnd, stoppedAt) + drainSeconds)
		{
			return Readiness.Waiting;
		}
		if (!finishedLogged)
		{
			finishedLogged = true;
			Plugin.Log.LogInfo((object)("RRFlush fart naturally finished: key=" + key + "; dsp=" + dspTime + "; clipEndDSP=" + fartEnd + "; stoppedDSP=" + stoppedAt + "; outputDrainSeconds=" + drainSeconds + "; lastObservedSample=" + lastSample + "; playing=False; seated exit may now begin."));
		}
		return Readiness.Ready;
	}

	internal static void Cancel(string key)
	{
		if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
		{
			CompletionAudio completionAudio = Get(key);
			completionAudio.cancelled = true;
			completionAudio.order.Expire();
			if (Object.op_Implicit((Object)(object)completionAudio.fart))
			{
				completionAudio.fart.Stop();
			}
			if (Object.op_Implicit((Object)(object)completionAudio.presentation))
			{
				Object.Destroy((Object)(object)completionAudio.presentation);
			}
		}
	}

	internal static string Key(int seat, long token)
	{
		return seat + ":" + token;
	}

	private static CompletionAudio Get(string key)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		if (pending.TryGetValue(key, out var value) && Object.op_Implicit((Object)(object)value))
		{
			return value;
		}
		GameObject val = new GameObject("RamRoyalFlush_audio_sequence");
		value = val.AddComponent<CompletionAudio>();
		value.key = key;
		value.expires = AudioSettings.dspTime + 10.0;
		pending[key] = value;
		return value;
	}

	internal static bool ScreamStarted(string key)
	{
		if (pending.TryGetValue(key, out var value) && Object.op_Implicit((Object)(object)value))
		{
			return value.order.Played;
		}
		return false;
	}

	internal static void Complete(string key, AudioSource source)
	{
		if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated())
		{
			return;
		}
		CompletionAudio completionAudio = Get(key);
		if (completionAudio.cancelled || completionAudio.order.CompletionSeen)
		{
			return;
		}
		completionAudio.fart = source;
		completionAudio.observedPlaying = Object.op_Implicit((Object)(object)source) && source.isPlaying;
		if (Object.op_Implicit((Object)(object)source) && Object.op_Implicit((Object)(object)source.clip))
		{
			int num = default(int);
			int num2 = default(int);
			AudioSettings.GetDSPBufferSize(ref num, ref num2);
			int outputSampleRate = AudioSettings.outputSampleRate;
			if (num < 0 || num2 < 0 || outputSampleRate <= 0)
			{
				completionAudio.interruptedPlayback = true;
				Plugin.Log.LogWarning((object)"Audio output timing unavailable; completion cannot verify full playback; food/launch withheld.");
			}
			else
			{
				completionAudio.drainSeconds = (double)num * (double)num2 / (double)outputSampleRate;
			}
			completionAudio.fartEnd = AudioSettings.dspTime + (double)source.clip.samples / (double)source.clip.frequency / Math.Max(0.01, source.pitch);
			completionAudio.lastSample = source.timeSamples;
			completionAudio.expires = Math.Max(completionAudio.expires, completionAudio.fartEnd + completionAudio.drainSeconds + 5.0);
		}
		completionAudio.order.Complete();
		Plugin.Log.LogInfo((object)("RRFlush fart cue: key=" + key + "; started=" + Object.op_Implicit((Object)(object)source) + "; startDSP=" + AudioSettings.dspTime + "; endDSP=" + completionAudio.fartEnd + "; outputDrainSeconds=" + completionAudio.drainSeconds + "; firstSample=" + completionAudio.lastSample));
		completionAudio.Tick();
	}

	internal static void Request(string key, Player player, bool fartEnabled)
	{
		if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
		{
			CompletionAudio completionAudio = Get(key);
			completionAudio.player = player;
			completionAudio.order.Request(completionAudio.localBarrier ? completionAudio.expectedFart : fartEnabled);
			completionAudio.Tick();
		}
	}

	private void Update()
	{
		Tick();
	}

	internal void Tick()
	{
		if (AudioListener.pause)
		{
			return;
		}
		if (Object.op_Implicit((Object)(object)fart) && fart.isPlaying)
		{
			observedPlaying = true;
		}
		if (AudioSettings.dspTime >= expires)
		{
			if (Object.op_Implicit((Object)(object)player) && !order.Played)
			{
				Plugin.Log.LogWarning((object)("RRLaunch scream skipped: completion audio not verified before timeout; flight unchanged. key=" + key));
			}
			Close();
			return;
		}
		bool fartActive = Object.op_Implicit((Object)(object)fart) && (fart.isPlaying || AudioSettings.dspTime < fartEnd || PlaybackStatus() == Readiness.Waiting);
		if (!Object.op_Implicit((Object)(object)player) || !order.TryStart(fartActive, paused: false))
		{
			return;
		}
		try
		{
			Plugin.Log.LogInfo((object)("RRLaunch scream cue: key=" + key + "; dsp=" + AudioSettings.dspTime + "; fartEnded=True"));
			LaunchScream.Play(player);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Launch scream unavailable; flight unchanged: " + ex));
		}
	}

	private void Close()
	{
		order.Expire();
		if (key != null && pending.TryGetValue(key, out var value) && (Object)(object)value == (Object)(object)this)
		{
			pending.Remove(key);
		}
		Object.Destroy((Object)(object)((Component)this).gameObject);
	}

	private void OnDestroy()
	{
		order.Expire();
		if (key != null && pending.TryGetValue(key, out var value) && (Object)(object)value == (Object)(object)this)
		{
			pending.Remove(key);
		}
	}

	internal static void Dispose()
	{
		foreach (CompletionAudio item in new List<CompletionAudio>(pending.Values))
		{
			if (Object.op_Implicit((Object)(object)item))
			{
				item.Close();
			}
		}
		pending.Clear();
	}
}
internal static class CompletionPresentation
{
	private const string CloudShaderID = "0f5a6e59b3eb19f3ba0b7e7955445af0";

	private const string CloudShaderName = "Particles/Standard Unlit2";

	private static AudioClip fart;

	private static Material gas;

	private static Texture2D texture;

	private static int active;

	private static bool mixerWarning;

	private static SoftReference<Shader> cloudShader;

	private static bool shaderHeld;

	internal static void Play(Vector3 position, bool surprise, string audioKey)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Expected O, but got Unknown
		//IL_00e7: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		AudioSource played = null;
		try
		{
			if (CompletionAudio.IsCancelled(audioKey) || (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated()) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || active >= 4 || Vector3.Distance(((Component)Player.m_localPlayer).transform.position, position) > 30f)
			{
				return;
			}
			bool sound = CompletionAudio.WantsFart(audioKey, Plugin.FartSound.Value);
			if (sound || Plugin.GreenCloud.Value)
			{
				GameObject go = new GameObject("RamRoyalFlush_completion");
				CompletionAudio.BindPresentation(audioKey, go);
				go.transform.position = position + Vector3.up * 0.45f;
				active++;
				go.AddComponent<PresentationLifetime>();
				Object.Destroy((Object)(object)go, Mathf.Max(5f, 9.398436f));
				EffectPlayback.Run(sound, Plugin.GreenCloud.Value, delegate
				{
					played = Sound(go);
					CompletionAudio.Complete(audioKey, played);
				}, delegate
				{
					go.AddComponent<ThreeGasBursts>().Initialize(surprise, sound, audioKey);
				}, delegate(string effect, Exception ex)
				{
					Plugin.Log.LogWarning((object)(effect + " failed; other effect remains independent; gameplay already completed: " + ex));
				});
				if (surprise)
				{
					Plugin.Log.LogInfo((object)"RRFlush Easter egg rolled (5%); original SHITTER'S FULL! voice asset pending.");
				}
			}
		}
		finally
		{
			CompletionAudio.Complete(audioKey, played);
		}
	}

	private static AudioSource Sound(GameObject go)
	{
		AudioMixer val = (Object.op_Implicit((Object)(object)AudioMan.instance) ? AudioMan.instance.m_masterMixer : null);
		AudioMixerGroup val2 = null;
		if (Object.op_Implicit((Object)(object)val))
		{
			AudioMixerGroup[] array = val.FindMatchingGroups("");
			foreach (AudioMixerGroup val3 in array)
			{
				if (Object.op_Implicit((Object)(object)val3) && string.Equals(((Object)val3).name, "SFX", StringComparison.OrdinalIgnoreCase))
				{
					val2 = val3;
					break;
				}
			}
		}
		if (!Object.op_Implicit((Object)(object)val2))
		{
			if (!mixerWarning)
			{
				mixerWarning = true;
				Plugin.Log.LogWarning((object)"SFX mixer group unavailable: fart audio skipped, no unrouted fallback.");
			}
			return null;
		}
		if (!Object.op_Implicit((Object)(object)fart))
		{
			float[] array2 = FartRecording.Create();
			AudioClip val4 = AudioClip.Create("RamRoyalFlush_Justin_recording", array2.Length, 1, 44100, false);
			MethodInfo method = typeof(AudioClip).GetMethod("SetData", new Type[2]
			{
				typeof(float[]),
				typeof(int)
			});
			try
			{
				if (method == null || !(bool)method.Invoke(val4, new object[2] { array2, 0 }))
				{
					throw new InvalidOperationException("Could not load recorded fart PCM through verified float[] API.");
				}
				fart = val4;
			}
			catch
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					Object.Destroy((Object)(object)val4);
				}
				throw;
			}
		}
		AudioSource val5 = go.AddComponent<AudioSource>();
		val5.playOnAwake = false;
		val5.clip = fart;
		val5.outputAudioMixerGroup = val2;
		val5.volume = 1f;
		val5.spatialBlend = 1f;
		val5.dopplerLevel = 0f;
		val5.minDistance = 1.5f;
		val5.maxDistance = 22f;
		val5.rolloffMode = (AudioRolloffMode)1;
		val5.timeSamples = 0;
		val5.loop = false;
		val5.Play();
		return val5;
	}

	internal static ParticleSystem Cloud(GameObject go, bool surprise)
	{
		//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0301: Unknown result type (might be due to invalid IL or missing references)
		//IL_032b: Unknown result type (might be due to invalid IL or missing references)
		//IL_034b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_036b: Unknown result type (might be due to invalid IL or missing references)
		//IL_038b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0390: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Expected O, but got Unknown
		//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03de: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0403: Unknown result type (might be due to invalid IL or missing references)
		//IL_040d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0414: Expected O, but got Unknown
		//IL_0435: Unknown result type (might be due to invalid IL or missing references)
		//IL_043f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0444: Unknown result type (might be due to invalid IL or missing references)
		//IL_0460: Unknown result type (might be due to invalid IL or missing references)
		//IL_046a: Unknown result type (might be due to invalid IL or missing references)
		//IL_046f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0490: Unknown result type (might be due to invalid IL or missing references)
		//IL_0495: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f6: Expected O, but got Unknown
		//IL_01cd: 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_0286: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)gas))
		{
			AssetID val = default(AssetID);
			if (!AssetID.TryParse("0f5a6e59b3eb19f3ba0b7e7955445af0", ref val))
			{
				throw new InvalidOperationException("Invalid verified cloud shader ID.");
			}
			if (!shaderHeld)
			{
				cloudShader = new SoftReference<Shader>(val);
				shaderHeld = true;
				cloudShader.Load();
			}
			Shader asset = cloudShader.Asset;
			if (!Object.op_Implicit((Object)(object)asset) || ((Object)asset).name != "Particles/Standard Unlit2" || !asset.isSupported)
			{
				cloudShader.Release();
				shaderHeld = false;
				throw new InvalidOperationException("Verified Valheim cloud shader unavailable/unsupported; expected Particles/Standard Unlit2, actual=" + (Object.op_Implicit((Object)(object)asset) ? ((Object)asset).name : "null"));
			}
			gas = new Material(asset);
			if (gas.HasProperty("_Mode"))
			{
				gas.SetFloat("_Mode", 2f);
			}
			gas.SetOverrideTag("RenderType", "Transparent");
			gas.DisableKeyword("_ALPHATEST_ON");
			gas.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			gas.DisableKeyword("_ALPHAMODULATE_ON");
			gas.EnableKeyword("_ALPHABLEND_ON");
			if (gas.HasProperty("_SrcBlend"))
			{
				gas.SetInt("_SrcBlend", 5);
			}
			if (gas.HasProperty("_DstBlend"))
			{
				gas.SetInt("_DstBlend", 10);
			}
			if (gas.HasProperty("_BlendOp"))
			{
				gas.SetInt("_BlendOp", 0);
			}
			if (gas.HasProperty("_ZWrite"))
			{
				gas.SetInt("_ZWrite", 0);
			}
			if (gas.HasProperty("_Color"))
			{
				gas.SetColor("_Color", Color.white);
			}
			gas.renderQueue = 3000;
			texture = new Texture2D(32, 32, (TextureFormat)4, false);
			Color[] array = (Color[])(object)new Color[1024];
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					float num = Mathf.Sqrt(Mathf.Pow(((float)j - 15.5f) / 15.5f, 2f) + Mathf.Pow(((float)i - 15.5f) / 15.5f, 2f));
					ref Color reference = ref array[i * 32 + j];
					reference = new Color(1f, 1f, 1f, Mathf.Pow(Mathf.Max(0f, 1f - num), 2f));
				}
			}
			texture.SetPixels(array);
			texture.Apply();
			gas.mainTexture = (Texture)(object)texture;
			Plugin.Log.LogInfo((object)("RRFlush cloud shader resolved: " + ((Object)asset).name + "; alpha blending configured."));
		}
		ParticleSystem val2 = go.AddComponent<ParticleSystem>();
		val2.Stop(true, (ParticleSystemStopBehavior)0);
		MainModule main = val2.main;
		((MainModule)(ref main)).playOnAwake = false;
		((MainModule)(ref main)).loop = false;
		((MainModule)(ref main)).duration = 0.4f;
		((MainModule)(ref main)).startLifetime = new MinMaxCurve(1.7f, 3.1f);
		((MainModule)(ref main)).startSpeed = new MinMaxCurve(0.6f, surprise ? 3.3f : 1.65f);
		((MainModule)(ref main)).startSize = new MinMaxCurve(0.975f, surprise ? 3.9f : 1.95f);
		((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.26f, 0.65f, 0.06f, 0.62f));
		((MainModule)(ref main)).maxParticles = (surprise ? 270 : 108);
		((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1;
		((MainModule)(ref main)).gravityModifier = MinMaxCurve.op_Implicit(-0.0375f);
		EmissionModule emission = val2.emission;
		((EmissionModule)(ref emission)).enabled = false;
		ShapeModule shape = val2.shape;
		((ShapeModule)(ref shape)).enabled = true;
		((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
		((ShapeModule)(ref shape)).radius = 0.33f;
		ColorOverLifetimeModule colorOverLifetime = val2.colorOverLifetime;
		((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true;
		Gradient val3 = new Gradient();
		val3.SetKeys((GradientColorKey[])(object)new GradientColorKey[2]
		{
			new GradientColorKey(new Color(0.26f, 0.65f, 0.06f), 0f),
			new GradientColorKey(new Color(0.15f, 0.4f, 0.04f), 1f)
		}, (GradientAlphaKey[])(object)new GradientAlphaKey[3]
		{
			new GradientAlphaKey(0.65f, 0f),
			new GradientAlphaKey(0.4f, 0.45f),
			new GradientAlphaKey(0f, 1f)
		});
		((ColorOverLifetimeModule)(ref colorOverLifetime)).color = MinMaxGradient.op_Implicit(val3);
		((Renderer)((Component)val2).GetComponent<ParticleSystemRenderer>()).sharedMaterial = gas;
		val2.Play();
		val2.Emit(surprise ? 90 : 36);
		go.AddComponent<CloudSpawnCheck>().Initialize(val2);
		return val2;
	}

	internal static void Released()
	{
		active = Math.Max(0, active - 1);
	}

	internal static void Dispose()
	{
		if (Object.op_Implicit((Object)(object)fart))
		{
			Object.Destroy((Object)(object)fart);
		}
		if (Object.op_Implicit((Object)(object)gas))
		{
			Object.Destroy((Object)(object)gas);
		}
		if (Object.op_Implicit((Object)(object)texture))
		{
			Object.Destroy((Object)(object)texture);
		}
		if (shaderHeld)
		{
			cloudShader.Release();
			shaderHeld = false;
		}
	}
}
public sealed class PresentationLifetime : MonoBehaviour
{
	private void OnDestroy()
	{
		CompletionPresentation.Released();
	}
}
public sealed class ThreeGasBursts : MonoBehaviour
{
	private GasBurstSchedule schedule;

	private ParticleSystem particles;

	private bool surprise;

	private string key;

	internal void Initialize(bool large, bool sound, string audioKey)
	{
		surprise = large;
		key = audioKey;
		schedule = new GasBurstSchedule(AudioSettings.dspTime, sound);
		Update();
	}

	private void Update()
	{
		if (CompletionAudio.IsCancelled(key))
		{
			Object.Destroy((Object)(object)this);
		}
		else
		{
			if (AudioListener.pause)
			{
				return;
			}
			int num = schedule.Poll(AudioSettings.dspTime);
			if (num == 0)
			{
				return;
			}
			try
			{
				if (num == 1)
				{
					particles = CompletionPresentation.Cloud(((Component)this).gameObject, surprise);
				}
				else if (Object.op_Implicit((Object)(object)particles))
				{
					particles.Emit(surprise ? 90 : 36);
					((Component)this).gameObject.AddComponent<CloudSpawnCheck>().Initialize(particles);
				}
				Plugin.Log.LogInfo((object)("RRFlush gas burst=" + num + "; key=" + key + "; dsp=" + AudioSettings.dspTime));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Gas burst unavailable; audio/exit independent: " + ex));
				Object.Destroy((Object)(object)this);
			}
			if (num == 3)
			{
				Object.Destroy((Object)(object)this);
			}
		}
	}
}
public sealed class CloudSpawnCheck : MonoBehaviour
{
	private ParticleSystem particles;

	private int frames;

	internal void Initialize(ParticleSystem value)
	{
		particles = value;
	}

	private void LateUpdate()
	{
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)particles))
		{
			Object.Destroy((Object)(object)this);
		}
		else if (particles.particleCount != 0 || ++frames >= 4)
		{
			ParticleSystemRenderer component = ((Component)particles).GetComponent<ParticleSystemRenderer>();
			bool flag = particles.particleCount > 0 && Object.op_Implicit((Object)(object)component) && ((Renderer)component).enabled && Object.op_Implicit((Object)(object)((Renderer)component).sharedMaterial) && Object.op_Implicit((Object)(object)((Renderer)component).sharedMaterial.shader) && ((Renderer)component).sharedMaterial.shader.isSupported;
			string text = "RRFlush cloud spawn check: particles=" + particles.particleCount + "; rendererReady=" + flag + "; position=" + ((Component)this).transform.position;
			if (flag)
			{
				Plugin.Log.LogInfo((object)text);
			}
			else
			{
				Plugin.Log.LogWarning((object)(text + "; cloud runtime verification FAILED."));
			}
			Object.Destroy((Object)(object)this);
		}
	}
}
internal static class ConfigRetirement
{
	internal static bool Apply(ConfigFile config)
	{
		string configFilePath = config.ConfigFilePath;
		if (!File.Exists(configFilePath))
		{
			return false;
		}
		byte[] array = File.ReadAllBytes(configFilePath);
		int num = 0;
		Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
		if (Prefix(array, new byte[4] { 255, 254, 0, 0 }))
		{
			num = 4;
			encoding = new UTF32Encoding(bigEndian: false, byteOrderMark: false, throwOnInvalidCharacters: true);
		}
		else if (Prefix(array, new byte[4] { 0, 0, 254, 255 }))
		{
			num = 4;
			encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: false, throwOnInvalidCharacters: true);
		}
		else if (Prefix(array, new byte[2] { 255, 254 }))
		{
			num = 2;
			encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: true);
		}
		else if (Prefix(array, new byte[2] { 254, 255 }))
		{
			num = 2;
			encoding = new UnicodeEncoding(bigEndian: true, byteOrderMark: false, throwOnInvalidBytes: true);
		}
		else if (Prefix(array, new byte[3] { 239, 187, 191 }))
		{
			num = 3;
		}
		string input = encoding.GetString(array, num, array.Length - num);
		StringBuilder stringBuilder = new StringBuilder();
		bool flag = false;
		bool flag2 = false;
		foreach (Match item in Regex.Matches(input, "[^\\r\\n]*(?:\\r\\n|\\n|\\r|$)"))
		{
			if (item.Length != 0)
			{
				string text = item.Value.Trim();
				if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
				{
					string a = text.Substring(1, text.Length - 2).Trim();
					flag = string.Equals(a, "Crafting", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Gameplay", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Presentation", StringComparison.OrdinalIgnoreCase);
					flag2 = flag2 || flag;
				}
				if (!flag)
				{
					stringBuilder.Append(item.Value);
				}
			}
		}
		if (!flag2)
		{
			return false;
		}
		byte[] bytes = encoding.GetBytes(stringBuilder.ToString());
		byte[] array2 = new byte[num + bytes.Length];
		Array.Copy(array, array2, num);
		Array.Copy(bytes, 0, array2, num, bytes.Length);
		string text2 = Hash(array);
		string text3 = Path.Combine(Path.GetDirectoryName(configFilePath), "RamRoyalFlush-ConfigBackups");
		Directory.CreateDirectory(text3);
		string text4 = Path.Combine(text3, "legacy-" + text2.Substring(0, 16) + ".bak");
		if (!File.Exists(text4))
		{
			using FileStream fileStream = new FileStream(text4, FileMode.CreateNew, FileAccess.Write, FileShare.None);
			fileStream.Write(array, 0, array.Length);
			fileStream.Flush(flushToDisk: true);
		}
		if (Hash(File.ReadAllBytes(text4)) != text2)
		{
			throw new IOException("Legacy config backup verification failed; original retained.");
		}
		string text5 = configFilePath + ".migration-" + Guid.NewGuid().ToString("N") + ".tmp";
		try
		{
			using (FileStream fileStream2 = new FileStream(text5, FileMode.CreateNew, FileAccess.Write, FileShare.None))
			{
				fileStream2.Write(array2, 0, array2.Length);
				fileStream2.Flush(flushToDisk: true);
			}
			if (Hash(File.ReadAllBytes(configFilePath)) != text2)
			{
				throw new IOException("Config changed during migration; replacement cancelled.");
			}
			File.Replace(text5, configFilePath, null);
			if (Hash(File.ReadAllBytes(configFilePath)) != Hash(array2))
			{
				throw new IOException("Migrated config verification failed; verified backup retained at " + text4);
			}
		}
		finally
		{
			if (File.Exists(text5))
			{
				File.Delete(text5);
			}
		}
		return true;
	}

	private static bool Prefix(byte[] data, byte[] prefix)
	{
		if (data.Length < prefix.Length)
		{
			return false;
		}
		for (int i = 0; i < prefix.Length; i++)
		{
			if (data[i] != prefix[i])
			{
				return false;
			}
		}
		return true;
	}

	private static string Hash(byte[] data)
	{
		using SHA256 sHA = SHA256.Create();
		return BitConverter.ToString(sHA.ComputeHash(data)).Replace("-", "");
	}
}
[HarmonyPatch(typeof(Player), "AttachStop")]
internal static class DismountSynchronization
{
	[HarmonyPrefix]
	private static void Prefix(Player __instance, out ExitMonitor __state)
	{
		__state = (((Object)(object)__instance == (Object)(object)Player.m_localPlayer) ? ((Component)__instance).GetComponent<ExitMonitor>() : null);
		if (Object.op_Implicit((Object)(object)__state) && !__state.BeforeNativeDetach(__instance))
		{
			__state = null;
		}
	}

	[HarmonyPostfix]
	private static void Postfix(Player __instance, ExitMonitor __state)
	{
		if (Object.op_Implicit((Object)(object)__state))
		{
			__state.AfterNativeDetach(__instance);
		}
	}
}
internal static class EffectPlayback
{
	internal static void Run(bool sound, bool cloud, Action playSound, Action playCloud, Action<string, Exception> report)
	{
		if (sound)
		{
			try
			{
				playSound();
			}
			catch (Exception arg)
			{
				report("Fart audio", arg);
			}
		}
		if (cloud)
		{
			try
			{
				playCloud();
			}
			catch (Exception arg2)
			{
				report("Green cloud", arg2);
			}
		}
	}
}
internal sealed class EffectsConfiguration : IDisposable
{
	private readonly ConfigFile config;

	private bool migrating;

	private bool disposed;

	internal readonly ConfigEntry<bool> FartSound;

	internal readonly ConfigEntry<bool> GreenCloud;

	internal readonly ConfigEntry<bool> FlightDamage;

	internal readonly ConfigEntry<float> EjectionForce;

	internal readonly ConfigEntry<float> LaunchAngle;

	internal EffectsConfiguration(ConfigFile file)
	{
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Expected O, but got Unknown
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		config = file;
		bool saveOnConfigSet = config.SaveOnConfigSet;
		config.SaveOnConfigSet = false;
		try
		{
			if (ConfigRetirement.Apply(config))
			{
				config.Reload();
			}
			FartSound = config.Bind<bool>("Effects", "FartSound", true, "Fart Sound: spatial main fart and Victory Toot.");
			GreenCloud = config.Bind<bool>("Effects", "GreenCloud", true, "Green Cloud: three synchronized main bursts and small victory puff.");
			FlightDamage = config.Bind<bool>("Effects", "FlightDamage", true, "Flight Damage: ON keeps Royal Flush impact and native landing damage. OFF suppresses only this launch's custom impacts and native fall hit; other damage remains. Sampled when launching.");
			EjectionForce = config.Bind<float>("Effects", "EjectionForce", 0f, new ConfigDescription("Ejection Force: 0 disables launch. Smooth upper-end boost preserves gentle low power; 100 requests 290.576 m/s (650 mph). Native collisions/fall damage apply.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[0]));
			LaunchAngle = config.Bind<float>("Effects", "LaunchAngle", 13f, new ConfigDescription("Launch Angle: 10-90 degrees above horizontal throne-forward; default 13.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 90f), new object[0]));
			config.Save();
			config.ConfigReloaded += OnReload;
		}
		finally
		{
			config.SaveOnConfigSet = saveOnConfigSet;
		}
	}

	private void OnReload(object sender, EventArgs args)
	{
		if (disposed || migrating)
		{
			return;
		}
		migrating = true;
		bool saveOnConfigSet = config.SaveOnConfigSet;
		config.SaveOnConfigSet = false;
		try
		{
			if (ConfigRetirement.Apply(config))
			{
				config.Reload();
				config.Save();
			}
		}
		finally
		{
			config.SaveOnConfigSet = saveOnConfigSet;
			migrating = false;
		}
	}

	public void Dispose()
	{
		if (!disposed)
		{
			disposed = true;
			config.ConfigReloaded -= OnReload;
		}
	}
}
internal static class EjectionGeometry
{
	internal static float Angle(float angle)
	{
		if (!float.IsNaN(angle) && !float.IsInfinity(angle))
		{
			return Math.Max(10f, Math.Min(90f, angle));
		}
		return 13f;
	}

	internal static float Speed(float force)
	{
		force = PresentationRules.Force(force);
		double num = (double)force / 100.0;
		double num2 = 290.576 / (100.0 * Math.Sqrt(2.0)) - 1.0;
		return (float)((double)force * Math.Sqrt(2.0) * (1.0 + num2 * num * num * num * num));
	}

	internal static Vector3 Velocity(Vector3 forward, float force, float angle = 13f)
	{
		//IL_0062: 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_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_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		force = PresentationRules.Force(force);
		forward.y = 0f;
		if (force == 0f || float.IsNaN(forward.x) || float.IsNaN(forward.z) || float.IsInfinity(forward.x) || float.IsInfinity(forward.z) || ((Vector3)(ref forward)).sqrMagnitude < 0.0001f)
		{
			return Vector3.zero;
		}
		double num = (double)Angle(angle) * Math.PI / 180.0;
		float num2 = Speed(force);
		return ((Vector3)(ref forward)).normalized * (num2 * (float)Math.Cos(num)) + Vector3.up * (num2 * (float)Math.Sin(num));
	}
}
internal static class ExitGeometry
{
	internal const float Clearance = 0.05f;

	internal const float MaximumReach = 2.5f;

	internal const float FloorTolerance = 0.65f;

	internal static float FrontDistance(Vector3 anchor, Vector3 forward, Vector3 center, Vector3 extents, float radius)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//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)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		float val = Vector3.Dot(center - anchor, forward) + Math.Abs(forward.x) * extents.x + Math.Abs(forward.y) * extents.y + Math.Abs(forward.z) * extents.z;
		return Math.Max(0.5f, val) + radius + 0.15f;
	}

	internal static bool CapsuleAtFloor(Vector3 floor, Vector3 normal, float referenceY, Vector3 centerOffset, float height, float radius, out Vector3 pivot, out Vector3 bottom, out Vector3 top)
	{
		//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_000c: 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_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: 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_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: 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)
		//IL_00eb: 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)
		pivot = (bottom = (top = Vector3.zero));
		if (!Finite(floor) || !Finite(normal) || !Finite(centerOffset) || !Finite(referenceY) || !Finite(height) || !Finite(radius) || radius <= 0f || height < 2f * radius || normal.y < 0.75f || Math.Abs(floor.y - referenceY) > 0.65f)
		{
			return false;
		}
		Vector3 val = floor + Vector3.up * (height * 0.5f + 0.05f);
		pivot = val - centerOffset;
		float num = height * 0.5f - radius;
		bottom = val - Vector3.up * num;
		top = val + Vector3.up * num;
		return true;
	}

	internal static bool InReach(Vector3 anchor, Vector3 destination)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		if (!Finite(anchor) || !Finite(destination))
		{
			return false;
		}
		float num = destination.x - anchor.x;
		float num2 = destination.z - anchor.z;
		return num * num + num2 * num2 <= 6.25f;
	}

	private static bool Finite(Vector3 v)
	{
		if (Finite(v.x) && Finite(v.y))
		{
			return Finite(v.z);
		}
		return false;
	}

	private static bool Finite(float v)
	{
		if (!float.IsNaN(v))
		{
			return !float.IsInfinity(v);
		}
		return false;
	}
}
public sealed class ExitMonitor : MonoBehaviour
{
	private Player player;

	private RoyalFlushSeat seat;

	private Transform attachment;

	private Action release;

	private long token;

	private bool requested;

	private bool completion;

	private bool detached;

	private bool done;

	private bool resolved;

	private Vector3 planned;

	private float nextRetry;

	private float started;

	private int fixedFrames;

	private int lateFrames;

	private ExitOutcome outcome;

	private string contacts = "none";

	private float contactTime = -1f;

	internal void Begin(Player p, RoyalFlushSeat s, long id, Vector3 offset, Action onRelease)
	{
		//IL_0030: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		player = p;
		seat = s;
		attachment = ((Chair)s).m_attachPoint;
		token = id;
		release = onRelease;
		planned = attachment.TransformPoint(offset);
		Trace("before-attach");
	}

	internal void Attached()
	{
		Trace("after-attach");
	}

	internal void RequestExit(bool successful)
	{
		if (done || resolved)
		{
			return;
		}
		if (requested)
		{
			completion = false;
			if (outcome != null)
			{
				outcome.Cancel();
			}
		}
		else
		{
			requested = true;
			completion = successful;
			TryDetach();
		}
	}

	private void TryDetach()
	{
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer)
		{
			Finish(commit: false);
			return;
		}
		if (!((Character)player).IsAttached() || (Object)(object)player.GetAttachPoint() != (Object)(object)attachment)
		{
			completion = false;
			ObserveDetach();
			return;
		}
		if (!Object.op_Implicit((Object)(object)seat) || !ExitPlacement.RefreshCapturedOffset(player, seat))
		{
			completion = false;
			Trace("exit-blocked-food-preserved");
			((Character)player).Message((MessageType)2, "Exit blocked. Food preserved. Clear the front; Ctrl/jump still cancel.", 0, (Sprite)null, false);
			nextRetry = Time.unscaledTime + 0.25f;
			return;
		}
		if (!ExitPlacement.ReadCapturedOffset(player, out var offset))
		{
			completion = false;
			return;
		}
		planned = attachment.TransformPoint(offset);
		Trace("before-native-detach");
		((Character)player).AttachStop();
		if (((Character)player).IsAttached() && (Object)(object)player.GetAttachPoint() == (Object)(object)attachment)
		{
			completion = false;
			nextRetry = Time.unscaledTime + 0.25f;
		}
		else
		{
			ObserveDetach();
		}
	}

	internal bool BeforeNativeDetach(Player p)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		if (done || detached || !Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)player || (Object)(object)p != (Object)(object)Player.m_localPlayer || !((Character)p).IsAttached() || !Object.op_Implicit((Object)(object)attachment) || (Object)(object)p.GetAttachPoint() != (Object)(object)attachment)
		{
			return false;
		}
		if (!ExitPlacement.ReadCapturedOffset(p, out var offset))
		{
			return false;
		}
		planned = attachment.TransformPoint(offset);
		if (!requested)
		{
			completion = false;
			requested = true;
		}
		return true;
	}

	internal void AfterNativeDetach(Player p)
	{
		if (!done && !detached && Object.op_Implicit((Object)(object)p) && !((Object)(object)p != (Object)(object)player) && !((Object)(object)p != (Object)(object)Player.m_localPlayer) && !((Character)p).IsAttached() && !Object.op_Implicit((Object)(object)p.GetAttachPoint()) && !((Character)p).IsDead() && !((Character)p).IsTeleporting())
		{
			Trace("after-native-detach");
			Physics.SyncTransforms();
			Trace("after-dismount-sync");
			ObserveDetach();
		}
	}

	private void ObserveDetach()
	{
		if (!detached)
		{
			detached = true;
			started = Time.unscaledTime;
			outcome = new ExitOutcome(started, completion);
			fixedFrames = (lateFrames = 0);
			Trace("observation-start");
		}
	}

	private void Update()
	{
		if (done)
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || ((Character)player).IsTeleporting())
		{
			if (!resolved)
			{
				Finish(commit: false);
			}
			Cleanup();
			return;
		}
		if (resolved && Time.unscaledTime - started >= 3f)
		{
			Trace("observation-end");
			Cleanup();
			return;
		}
		if (!detached && (!((Character)player).IsAttached() || (Object)(object)player.GetAttachPoint() != (Object)(object)attachment))
		{
			completion = false;
			requested = true;
			Trace("native-cancellation-observed");
			ObserveDetach();
		}
		if (requested && !detached && Time.unscaledTime >= nextRetry)
		{
			if (Object.op_Implicit((Object)(object)seat) && ExitPlacement.TryFind(player, seat, out var _))
			{
				TryDetach();
			}
			nextRetry = Time.unscaledTime + 0.25f;
		}
		if (detached && !resolved)
		{
			Sample(fixedSample: false);
		}
	}

	private void FixedUpdate()
	{
		if (!done && Object.op_Implicit((Object)(object)player) && detached)
		{
			fixedFrames++;
			if (fixedFrames == 1 || fixedFrames == 2 || fixedFrames == 4 || fixedFrames == 8 || fixedFrames == 16 || fixedFrames == 32 || fixedFrames == 64 || fixedFrames == 128)
			{
				Trace("fixed-" + fixedFrames);
			}
			if (!resolved && fixedFrames > 1)
			{
				Sample(fixedSample: true);
			}
		}
	}

	private void LateUpdate()
	{
		if (!done && Object.op_Implicit((Object)(object)player) && detached)
		{
			lateFrames++;
			if (lateFrames == 1 || lateFrames == 2 || lateFrames == 4 || lateFrames == 8 || lateFrames == 16 || lateFrames == 32 || lateFrames == 64 || lateFrames == 128)
			{
				Trace("late-" + lateFrames);
			}
		}
	}

	private void Sample(bool fixedSample)
	{
		//IL_007b: 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_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)seat) || !seat.CompletionAuthorized())
		{
			outcome.Cancel();
		}
		Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
		bool attached = ((Character)player).IsAttached() || Object.op_Implicit((Object)(object)player.GetAttachPoint());
		string detail;
		bool flag = ExitPlacement.StandingClear(player, out detail);
		bool flag2 = Object.op_Implicit((Object)(object)component) && component.useGravity && Vector3.Distance(((Component)player).transform.position, planned) < 0.2f && Vector3.Distance(component.position, ((Component)player).transform.position) < 0.1f && ExitPlacement.ColliderAligned(player);
		switch (outcome.Sample(Time.unscaledTime, !((Character)player).IsDead() && !((Character)player).IsTeleporting(), attached, flag && flag2, fixedSample))
		{
		case ExitOutcome.Result.Commit:
			Finish(commit: true);
			break;
		case ExitOutcome.Result.Preserve:
			Finish(commit: false);
			break;
		}
	}

	private void OnCollisionStay(Collision collision)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if (!done && Object.op_Implicit((Object)(object)player))
		{
			StringBuilder stringBuilder = new StringBuilder();
			ContactPoint[] array = collision.contacts;
			for (int i = 0; i < array.Length && i < 4; i++)
			{
				stringBuilder.Append(Object.op_Implicit((Object)(object)((ContactPoint)(ref array[i])).otherCollider) ? ((Object)((ContactPoint)(ref array[i])).otherCollider).name : "null").Append(':').Append(V(((ContactPoint)(ref array[i])).normal))
					.Append(" sep=")
					.Append(((ContactPoint)(ref array[i])).separation.ToString("F3", CultureInfo.InvariantCulture))
					.Append(';');
			}
			contacts = stringBuilder.ToString();
			contactTime = Time.unscaledTime;
		}
	}

	private void Trace(string stage)
	{
		//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_0041: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: 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_012b: 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_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0305: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)player))
		{
			Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
			CapsuleCollider collider = ((Character)player).GetCollider();
			string detail;
			bool flag = ExitPlacement.StandingClear(player, out detail);
			Vector3 v = (Object.op_Implicit((Object)(object)attachment) ? attachment.InverseTransformPoint(((Component)player).transform.position) : Vector3.zero);
			ExitPlacement.ReadCapturedOffset(player, out var offset);
			ManualLogSource log = Plugin.Log;
			object[] array = new object[46]
			{
				"RRExit token=",
				token,
				" stage=",
				stage,
				" t=",
				Time.unscaledTime.ToString("F3", CultureInfo.InvariantCulture),
				" plan=",
				V(planned),
				" transform=",
				V(((Component)player).transform.position),
				" local=",
				V(v),
				" capturedOffset=",
				V(offset),
				" attach=",
				Object.op_Implicit((Object)(object)attachment) ? V(attachment.position) : "missing",
				" forward=",
				V(((Component)player).transform.forward),
				" body=",
				Object.op_Implicit((Object)(object)component) ? V(component.position) : "missing",
				" velocity=",
				Object.op_Implicit((Object)(object)component) ? V(component.linearVelocity) : "missing",
				" gravity=",
				Object.op_Implicit((Object)(object)component) && component.useGravity,
				" bodyRotation=",
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null
			};
			object obj;
			if (!Object.op_Implicit((Object)(object)component))
			{
				obj = "missing";
			}
			else
			{
				Quaternion rotation = component.rotation;
				obj = V(((Quaternion)(ref rotation)).eulerAngles);
			}
			array[25] = obj;
			array[26] = " interpolation=";
			array[27] = (Object.op_Implicit((Object)(object)component) ? ((object)component.interpolation).ToString() : "missing");
			array[28] = " autoSync=";
			array[29] = Physics.autoSyncTransforms;
			array[30] = " attached=";
			array[31] = ((Character)player).IsAttached();
			array[32] = " samePoint=";
			array[33] = (Object)(object)player.GetAttachPoint() == (Object)(object)attachment;
			array[34] = " sitting=";
			array[35] = ((Character)player).IsSitting();
			array[36] = " colliderCenter=";
			object obj2;
			if (!Object.op_Implicit((Object)(object)collider))
			{
				obj2 = "missing";
			}
			else
			{
				Bounds bounds = ((Collider)collider).bounds;
				obj2 = V(((Bounds)(ref bounds)).center);
			}
			array[37] = obj2;
			array[38] = " standingClear=";
			array[39] = flag;
			array[40] = " overlaps=";
			array[41] = detail;
			array[42] = " contacts=";
			array[43] = contacts;
			array[44] = " contactAge=";
			array[45] = ((contactTime < 0f) ? "none" : (Time.unscaledTime - contactTime).ToString("F3", CultureInfo.InvariantCulture));
			log.LogInfo((object)string.Concat(array));
		}
	}

	private static string V(Vector3 v)
	{
		return string.Format(CultureInfo.InvariantCulture, "({0:F3},{1:F3},{2:F3})", v.x, v.y, v.z);
	}

	private void Finish(bool commit)
	{
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		if (done || resolved)
		{
			return;
		}
		resolved = true;
		Trace(commit ? "verified-exit-commit" : "exit-food-preserved");
		try
		{
			if (commit)
			{
				int count = player.GetFoods().Count;
				Plugin.ClearLocalFood(player);
				if (count <= 0 || player.GetFoods().Count != 0 || !Object.op_Implicit((Object)(object)seat) || !seat.CompletionCommitted(token))
				{
					return;
				}
				try
				{
					float force = SafeEjection.TryLaunch(player, ((Chair)seat).m_attachPoint.forward);
					if (PresentationRules.DangerousLaunch(force))
					{
						seat.NotifyLaunchScream(token, ((Character)player).GetZDOID(), force);
					}
					return;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Launch or scream notification failed; verified food/dismount not retried: " + ex));
					return;
				}
			}
			if (Object.op_Implicit((Object)(object)seat))
			{
				seat.AbortCompletion(token);
			}
			if (Object.op_Implicit((Object)(object)player))
			{
				((Character)player).Message((MessageType)2, "Interaction cancelled or exit unverified. Food preserved.", 0, (Sprite)null, false);
			}
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogError((object)("Food completion failed; no automatic retry: " + ex2));
		}
		finally
		{
			if (!detached)
			{
				Cleanup();
			}
		}
	}

	private void Cleanup()
	{
		if (!done)
		{
			done = true;
			if (release != null)
			{
				release();
			}
			Object.Destroy((Object)(object)this);
		}
	}

	private void OnDestroy()
	{
		if (!done)
		{
			done = true;
			if (release != null)
			{
				release();
			}
		}
	}
}
internal sealed class ExitOutcome
{
	internal enum Result
	{
		Wait,
		Commit,
		Preserve
	}

	private readonly double started;

	private bool authorized;

	private bool finished;

	private int consecutive;

	internal ExitOutcome(double now, bool completion)
	{
		started = now;
		authorized = completion;
	}

	internal void Cancel()
	{
		authorized = false;
	}

	internal Result Sample(double now, bool valid, bool attached, bool clear, bool fixedSample)
	{
		if (finished)
		{
			return Result.Wait;
		}
		if (!valid || double.IsNaN(now) || double.IsInfinity(now) || now < started || now - started > 3.0)
		{
			finished = true;
			return Result.Preserve;
		}
		if (attached || !clear)
		{
			consecutive = 0;
		}
		else if (fixedSample)
		{
			consecutive++;
		}
		if (!attached && clear && consecutive >= 3 && now - started >= 0.15)
		{
			finished = true;
			if (!authorized)
			{
				return Result.Preserve;
			}
			return Result.Commit;
		}
		return Result.Wait;
	}
}
internal static class ExitPlacement
{
	private static readonly FieldInfo CapturedOffset = typeof(Player).GetField("m_detachOffset", BindingFlags.Instance | BindingFlags.NonPublic);

	internal static bool ApiAvailable
	{
		get
		{
			if (CapturedOffset != null && !CapturedOffset.IsStatic)
			{
				return CapturedOffset.FieldType == typeof(Vector3);
			}
			return false;
		}
	}

	internal static bool TryFind(Player player, RoyalFlushSeat seat, out Vector3 offset)
	{
		//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_005c: 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_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: 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_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: 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_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_022f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_023d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: Unknown result type (might be due to invalid IL or missing references)
		//IL_024b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0266: Unknown result type (might be due to invalid IL or missing references)
		//IL_0282: Unknown result type (might be due to invalid IL or missing references)
		//IL_0284: Unknown result type (might be due to invalid IL or missing references)
		//IL_02be: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0304: Unknown result type (might be due to invalid IL or missing references)
		//IL_0306: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03db: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0401: Unknown result type (might be due to invalid IL or missing references)
		//IL_0403: Unknown result type (might be due to invalid IL or missing references)
		//IL_040f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0411: Unknown result type (might be due to invalid IL or missing references)
		//IL_046b: Unknown result type (might be due to invalid IL or missing references)
		//IL_046d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0472: Unknown result type (might be due to invalid IL or missing references)
		offset = Vector3.zero;
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)seat) || !Object.op_Implicit((Object)(object)((Chair)seat).m_attachPoint) || !ApiAvailable)
		{
			return false;
		}
		CapsuleCollider collider = ((Character)player).GetCollider();
		if (!Object.op_Implicit((Object)(object)collider) || collider.direction != 1 || Vector3.Dot(((Component)collider).transform.up, Vector3.up) < 0.99f || Vector3.Dot(((Chair)seat).m_attachPoint.up, Vector3.up) < 0.99f)
		{
			return false;
		}
		Vector3 lossyScale = ((Component)collider).transform.lossyScale;
		float num = collider.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
		float height = Mathf.Max(collider.height * Mathf.Abs(lossyScale.y), num * 2f);
		Vector3 val = ((Component)collider).transform.TransformPoint(collider.center) - ((Component)player).transform.position;
		Vector3 centerOffset = ((Chair)seat).m_attachPoint.rotation * Quaternion.Inverse(((Component)player).transform.rotation) * val;
		Vector3 val2 = Vector3.ProjectOnPlane(((Chair)seat).m_attachPoint.forward, Vector3.up);
		Vector3 normalized = ((Vector3)(ref val2)).normalized;
		if (((Vector3)(ref normalized)).sqrMagnitude < 0.99f)
		{
			return false;
		}
		Vector3 val3 = Vector3.Cross(Vector3.up, normalized);
		Vector3 position = ((Chair)seat).m_attachPoint.position;
		float num2 = 0.5f + num + 0.15f;
		Collider[] componentsInChildren = ((Component)seat).GetComponentsInChildren<Collider>();
		foreach (Collider val4 in componentsInChildren)
		{
			if (val4.enabled && !val4.isTrigger)
			{
				Bounds bounds = val4.bounds;
				num2 = Mathf.Max(num2, ExitGeometry.FrontDistance(position, normalized, ((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents, num));
			}
		}
		float[] array = new float[3] { 0f, 0.45f, -0.45f };
		Vector3 val5 = default(Vector3);
		for (int j = 0; j < 2; j++)
		{
			float[] array2 = array;
			foreach (float num3 in array2)
			{
				Vector3 destination = position + normalized * (num2 + (float)j * 0.3f) + val3 * num3;
				if (!ExitGeometry.InReach(position, destination))
				{
					continue;
				}
				((Vector3)(ref val5))..ctor(destination.x, ((Component)seat).transform.position.y + 0.6f, destination.z);
				RaycastHit[] array3 = Physics.RaycastAll(val5, Vector3.down, 1.25f, -5, (QueryTriggerInteraction)1);
				Array.Sort(array3, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				RaycastHit val6 = default(RaycastHit);
				RaycastHit[] array4 = array3;
				for (int num4 = 0; num4 < array4.Length; num4++)
				{
					RaycastHit val7 = array4[num4];
					if (Object.op_Implicit((Object)(object)((RaycastHit)(ref val7)).collider) && !((Component)((RaycastHit)(ref val7)).collider).transform.IsChildOf(((Component)player).transform))
					{
						val6 = val7;
						break;
					}
				}
				if (!Object.op_Implicit((Object)(object)((RaycastHit)(ref val6)).collider) || ((Component)((RaycastHit)(ref val6)).collider).transform.IsChildOf(((Component)seat).transform) || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent<Character>()) || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent<ItemDrop>()) || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent<Chair>()) || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent<Container>()) || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val6)).collider).GetComponentInParent<CraftingStation>()) || (Object.op_Implicit((Object)(object)((RaycastHit)(ref val6)).rigidbody) && !((RaycastHit)(ref val6)).rigidbody.isKinematic) || !ExitGeometry.CapsuleAtFloor(((RaycastHit)(ref val6)).point, ((RaycastHit)(ref val6)).normal, ((Component)seat).transform.position.y, centerOffset, height, num, out var pivot, out var bottom, out var top) || !ExitGeometry.InReach(position, pivot))
				{
					continue;
				}
				bool flag = false;
				Collider[] array5 = Physics.OverlapCapsule(bottom, top, num + 0.02f, -5, (QueryTriggerInteraction)1);
				foreach (Collider val8 in array5)
				{
					if (Object.op_Implicit((Object)(object)val8) && !((Component)val8).transform.IsChildOf(((Component)player).transform))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					offset = ((Chair)seat).m_attachPoint.InverseTransformPoint(pivot);
					return true;
				}
			}
		}
		return false;
	}

	internal static bool RefreshCapturedOffset(Player player, RoyalFlushSeat seat)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsAttached() || (Object)(object)player.GetAttachPoint() != (Object)(object)((Chair)seat).m_attachPoint)
		{
			return false;
		}
		if (!TryFind(player, seat, out var offset))
		{
			return false;
		}
		CapturedOffset.SetValue(player, offset);
		return true;
	}

	internal static bool ReadCapturedOffset(Player player, out Vector3 offset)
	{
		//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_0028: 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)
		offset = Vector3.zero;
		if (!Object.op_Implicit((Object)(object)player) || !ApiAvailable)
		{
			return false;
		}
		offset = (Vector3)CapturedOffset.GetValue(player);
		return true;
	}

	internal static bool ColliderAligned(Player player)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: 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_003c: Unknown result type (might be due to invalid IL or missing references)
		CapsuleCollider val = (Object.op_Implicit((Object)(object)player) ? ((Character)player).GetCollider() : null);
		if (Object.op_Implicit((Object)(object)val) && ((Collider)val).enabled)
		{
			Bounds bounds = ((Collider)val).bounds;
			return Vector3.Distance(((Bounds)(ref bounds)).center, ((Component)val).transform.TransformPoint(val.center)) < 0.1f;
		}
		return false;
	}

	internal static bool StandingClear(Player player, out string detail)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: 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_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		detail = "none";
		CapsuleCollider val = (Object.op_Implicit((Object)(object)player) ? ((Character)player).GetCollider() : null);
		if (!Object.op_Implicit((Object)(object)val) || !((Collider)val).enabled || val.direction != 1)
		{
			detail = "invalid-capsule";
			return false;
		}
		Vector3 lossyScale = ((Component)val).transform.lossyScale;
		float num = val.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
		float num2 = Mathf.Max(val.height * Mathf.Abs(lossyScale.y), num * 2f);
		Vector3 val2 = ((Component)val).transform.TransformPoint(val.center);
		Vector3 val3 = ((Component)val).transform.up * (num2 * 0.5f - num);
		bool flag = true;
		Collider[] array = Physics.OverlapCapsule(val2 - val3, val2 + val3, num, -5, (QueryTriggerInteraction)1);
		Vector3 val5 = default(Vector3);
		float num3 = default(float);
		foreach (Collider val4 in array)
		{
			if (Object.op_Implicit((Object)(object)val4) && !((Component)val4).transform.IsChildOf(((Component)player).transform) && Physics.ComputePenetration((Collider)(object)val, ((Component)val).transform.position, ((Component)val).transform.rotation, val4, ((Component)val4).transform.position, ((Component)val4).transform.rotation, ref val5, ref num3) && !(num3 <= 0.015f))
			{
				flag = false;
				if (detail.Length < 700)
				{
					object obj = detail;
					detail = string.Concat(obj, ";", ((Object)val4).name, "#", ((Object)val4).GetInstanceID(), " depth=", num3.ToString("F3", CultureInfo.InvariantCulture));
				}
			}
		}
		Vector3 val6 = val2 - Vector3.up * (num2 * 0.5f);
		bool flag2 = false;
		RaycastHit[] array2 = Physics.RaycastAll(val6 + Vector3.up * 0.1f, Vector3.down, 0.25f, -5, (QueryTriggerInteraction)1);
		for (int j = 0; j < array2.Length; j++)
		{
			RaycastHit val7 = array2[j];
			if (Object.op_Implicit((Object)(object)((RaycastHit)(ref val7)).collider) && !((Component)((RaycastHit)(ref val7)).collider).transform.IsChildOf(((Component)player).transform) && !(((RaycastHit)(ref val7)).normal.y < 0.75f) && !Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val7)).collider).GetComponentInParent<Character>()) && !Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val7)).collider).GetComponentInParent<Chair>()) && !Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val7)).collider).GetComponentInParent<ItemDrop>()) && (!Object.op_Implicit((Object)(object)((RaycastHit)(ref val7)).rigidbody) || ((RaycastHit)(ref val7)).rigidbody.isKinematic))
			{
				flag2 = true;
				break;
			}
		}
		if (!flag2)
		{
			detail += ";unsupported";
		}
		if (flag)
		{
			return flag2;
		}
		return false;
	}
}
internal static class FartRecording
{
	internal const int Rate = 44100;

	internal const int Frames = 266736;

	internal const int LeadingSilentFrames = 0;

	internal static float[] Create()
	{
		using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("RamRoyalFlush.JustinFart");
		if (stream == null || stream.Length != 1066944)
		{
			throw new InvalidDataException("Embedded Justin fart PCM missing or invalid.");
		}
		using BinaryReader binaryReader = new BinaryReader(stream);
		float[] array = new float[266736];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = binaryReader.ReadSingle();
			if (float.IsNaN(array[i]) || float.IsInfinity(array[i]) || Math.Abs(array[i]) > 0.921f)
			{
				throw new InvalidDataException("Embedded recorded PCM exceeds approved headroom.");
			}
		}
		return array;
	}
}
internal static class FatalImpactPresentation
{
	private static AudioClip clip;

	private static Material blood;

	private static Texture2D texture;

	private static SoftReference<Shader> shaderRef;

	private static bool shaderHeld;

	private static readonly List<GameObject> live = new List<GameObject>();

	internal static void Play(Vector3 point)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated())
		{
			return;
		}
		GameObject val = new GameObject("RamRoyalFlush_fatal_impact");
		val.transform.position = point;
		live.Add(val);
		val.AddComponent<FatalImpactLifetime>();
		LaunchScream.StopForFatalImpact();
		AudioSource val2 = null;
		ParticleSystem val3 = null;
		try
		{
			val2 = Sound(val);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Fatal splat unavailable; gore remains independent: " + ex));
		}
		try
		{
			val3 = Blood(val);
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogError((object)("Fatal gore unavailable; splat remains independent: " + ex2));
		}
		try
		{
			if (Object.op_Implicit((Object)(object)val2))
			{
				val2.Play();
			}
		}
		catch (Exception ex3)
		{
			Plugin.Log.LogError((object)("Fatal splat playback failed: " + ex3));
		}
		try
		{
			if (Object.op_Implicit((Object)(object)val3))
			{
				val3.Play();
				val3.Emit(110);
				val.AddComponent<FatalImpactSpawnCheck>().Initialize(val3);
			}
		}
		catch (Exception ex4)
		{
			Plugin.Log.LogError((object)("Fatal gore emission failed: " + ex4));
		}
		Object.Destroy((Object)(object)val, 3f);
		Plugin.Log.LogInfo((object)string.Concat("RRFatalImpact sound/gore triggered together at=", point, "; standalone lifetime3s; no gain; client-local."));
	}

	private static AudioSource Sound(GameObject root)
	{
		AudioMixer val = (Object.op_Implicit((Object)(object)AudioMan.instance) ? AudioMan.instance.m_masterMixer : null);
		AudioMixerGroup val2 = null;
		if (Object.op_Implicit((Object)(object)val))
		{
			AudioMixerGroup[] array = val.FindMatchingGroups("");
			foreach (AudioMixerGroup val3 in array)
			{
				if (Object.op_Implicit((Object)(object)val3) && string.Equals(((Object)val3).name, "SFX", StringComparison.OrdinalIgnoreCase))
				{
					val2 = val3;
					break;
				}
			}
		}
		if (!Object.op_Implicit((Object)(object)val2))
		{
			throw new InvalidOperationException("SFX mixer unavailable; no unrouted fallback.");
		}
		if (!Object.op_Implicit((Object)(object)clip))
		{
			float[] array2 = FatalImpactRecording.Create();
			AudioClip val4 = AudioClip.Create("RamRoyalFlush_blood_splats_1", array2.Length, 1, 44100, false);
			try
			{
				MethodInfo method = typeof(AudioClip).GetMethod("SetData", new Type[2]
				{
					typeof(float[]),
					typeof(int)
				});
				if (method == null || !(bool)method.Invoke(val4, new object[2] { array2, 0 }))
				{
					throw new InvalidOperationException("Fatal PCM upload failed.");
				}
				clip = val4;
			}
			catch
			{
				Object.Destroy((Object)(object)val4);
				throw;
			}
		}
		AudioSource val5 = root.AddComponent<AudioSource>();
		val5.playOnAwake = false;
		val5.loop = false;
		val5.clip = clip;
		val5.outputAudioMixerGroup = val2;
		val5.volume = 1f;
		val5.pitch = 1f;
		val5.dopplerLevel = 0f;
		val5.spatialBlend = 0f;
		return val5;
	}

	private static ParticleSystem Blood(GameObject root)
	{
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f8: 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_032e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0333: Unknown result type (might be due to invalid IL or missing references)
		//IL_0344: Unknown result type (might be due to invalid IL or missing references)
		//IL_0361: Unknown result type (might be due to invalid IL or missing references)
		//IL_0366: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_038b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Expected O, but got Unknown
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Expected O, but got Unknown
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)blood))
		{
			AssetID val = default(AssetID);
			if (!AssetID.TryParse("0f5a6e59b3eb19f3ba0b7e7955445af0", ref val))
			{
				throw new InvalidOperationException("Blood shader ID invalid.");
			}
			if (!shaderHeld)
			{
				shaderRef = new SoftReference<Shader>(val);
				shaderHeld = true;
				shaderRef.Load();
			}
			Shader asset = shaderRef.Asset;
			if (!Object.op_Implicit((Object)(object)asset) || ((Object)asset).name != "Particles/Standard Unlit2" || !asset.isSupported)
			{
				throw new InvalidOperationException("Verified particle shader unavailable.");
			}
			blood = new Material(asset);
			blood.SetOverrideTag("RenderType", "Transparent");
			blood.DisableKeyword("_ALPHATEST_ON");
			blood.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			blood.DisableKeyword("_ALPHAMODULATE_ON");
			blood.EnableKeyword("_ALPHABLEND_ON");
			if (blood.HasProperty("_Mode"))
			{
				blood.SetFloat("_Mode", 2f);
			}
			if (blood.HasProperty("_SrcBlend"))
			{
				blood.SetInt("_SrcBlend", 5);
			}
			if (blood.HasProperty("_DstBlend"))
			{
				blood.SetInt("_DstBlend", 10);
			}
			if (blood.HasProperty("_BlendOp"))
			{
				blood.SetInt("_BlendOp", 0);
			}
			if (blood.HasProperty("_ZWrite"))
			{
				blood.SetInt("_ZWrite", 0);
			}
			if (blood.HasProperty("_Color"))
			{
				blood.SetColor("_Color", Color.white);
			}
			blood.renderQueue = 3000;
			texture = new Texture2D(32, 32, (TextureFormat)4, false);
			Color[] array = (Color[])(object)new Color[1024];
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					float num = Mathf.Sqrt(Mathf.Pow(((float)j - 15.5f) / 15.5f, 2f) + Mathf.Pow(((float)i - 15.5f) / 15.5f, 2f));
					ref Color reference = ref array[i * 32 + j];
					reference = new Color(1f, 1f, 1f, Mathf.Pow(Mathf.Max(0f, 1f - num), 2f));
				}
			}
			texture.SetPixels(array);
			texture.Apply();
			blood.mainTexture = (Texture)(object)texture;
		}
		ParticleSystem val2 = root.AddComponent<ParticleSystem>();
		val2.Stop(true, (ParticleSystemStopBehavior)0);
		MainModule main = val2.main;
		((MainModule)(ref main)).playOnAwake = false;
		((MainModule)(ref main)).loop = false;
		((MainModule)(ref main)).duration = 0.1f;
		((MainModule)(ref main)).startLifetime = new MinMaxCurve(0.4f, 1.2f);
		((MainModule)(ref main)).startSize = new MinMaxCurve(0.1f, 0.45f);
		((MainModule)(ref main)).startSpeed = new MinMaxCurve(3f, 9f);
		((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.75f, 0.015f, 0.01f, 0.95f));
		((MainModule)(ref main)).gravityModifier = MinMaxCurve.op_Implicit(0.8f);
		((MainModule)(ref main)).maxParticles = 110;
		((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1;
		ShapeModule shape = val2.shape;
		((ShapeModule)(ref shape)).enabled = true;
		((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
		((ShapeModule)(ref shape)).radius = 0.3f;
		EmissionModule emission = val2.emission;
		((EmissionModule)(ref emission)).enabled = false;
		((Renderer)((Component)val2).GetComponent<ParticleSystemRenderer>()).sharedMaterial = blood;
		return val2;
	}

	internal static void Released(GameObject go)
	{
		live.Remove(go);
	}

	internal static void Dispose()
	{
		FatalImpactReceipt.Dispose();
		GameObject[] array = live.ToArray();
		foreach (GameObject val in array)
		{
			if (Object.op_Implicit((Object)(object)val))
			{
				Object.Destroy((Object)(object)val);
			}
		}
		live.Clear();
		if (Object.op_Implicit((Object)(object)clip))
		{
			Object.Destroy((Object)(object)clip);
		}
		if (Object.op_Implicit((Object)(object)blood))
		{
			Object.Destroy((Object)(object)blood);
		}
		if (Object.op_Implicit((Object)(object)texture))
		{
			Object.Destroy((Object)(object)texture);
		}
		if (shaderHeld)
		{
			shaderRef.Release();
			shaderHeld = false;
		}
	}
}
public sealed class FatalImpactLifetime : MonoBehaviour
{
	private void OnDestroy()
	{
		FatalImpactPresentation.Released(((Component)this).gameObject);
	}
}
public sealed class FatalImpactSpawnCheck : MonoBehaviour
{
	private ParticleSystem particles;

	private int frames;

	internal void Initialize(ParticleSystem value)
	{
		particles = value;
	}

	private void LateUpdate()
	{
		if (!Object.op_Implicit((Object)(object)particles) || particles.particleCount != 0 || ++frames >= 4)
		{
			ParticleSystemRenderer val = (Object.op_Implicit((Object)(object)particles) ? ((Component)particles).GetComponent<ParticleSystemRenderer>() : null);
			if (Object.op_Implicit((Object)(object)particles) && particles.particleCount > 0 && Object.op_Implicit((Object)(object)val) && ((Renderer)val).enabled && Object.op_Implicit((Object)(object)((Renderer)val).sharedMaterial) && Object.op_Implicit((Object)(object)((Renderer)val).sharedMaterial.shader) && ((Renderer)val).sharedMaterial.shader.isSupported)
			{
				Plugin.Log.LogInfo((object)("RRFatalImpact gore renderer ready; particles=" + particles.particleCount));
			}
			else
			{
				Plugin.Log.LogWarning((object)"RRFatalImpact gore runtime verification FAILED.");
			}
			Object.Destroy((Object)(object)this);
		}
	}
}
public sealed class FatalImpactReceipt : MonoBehaviour
{
	private static readonly List<FatalImpactReceipt> pending = new List<FatalImpactReceipt>();

	private Player player;

	private Action callback;

	private Vector3 point;

	private float before;

	private float expires;

	private bool requestOpen;

	private bool armed;

	private bool finished;

	private string cause = "collision";

	internal static FatalImpactReceipt Begin(Player p, Vector3 point, float before)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Expected O, but got Unknown
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		FatalImpactReceipt[] array = pending.ToArray();
		foreach (FatalImpactReceipt fatalImpactReceipt in array)
		{
			if (Object.op_Implicit((Object)(object)fatalImpactReceipt) && (Object)(object)fatalImpactReceipt.player == (Object)(object)p && !fatalImpactReceipt.finished)
			{
				if (fatalImpactReceipt.requestOpen || (fatalImpactReceipt.armed && ((Character)p).GetHealth() <= 0f))
				{
					return null;
				}
				fatalImpactReceipt.Close();
			}
		}
		GameObject val = new GameObject("RamRoyalFlush_fatal_receipt");
		FatalImpactReceipt fatalImpactReceipt2 = val.AddComponent<FatalImpactReceipt>();
		fatalImpactReceipt2.player = p;
		fatalImpactReceipt2.point = point;
		fatalImpactReceipt2.before = before;
		fatalImpactReceipt2.requestOpen = true;
		fatalImpactReceipt2.expires = Time.unscaledTime + 3f;
		fatalImpactReceipt2.callback = fatalImpactReceipt2.OnNativeDeath;
		((Character)p).m_onDeath = (Action)Delegate.Combine(((Character)p).m_onDeath, fatalImpactReceipt2.callback);
		pending.Add(fatalImpactReceipt2);
		return fatalImpactReceipt2;
	}

	internal static FatalImpactReceipt BeginNativeFall(Player p, Vector3 point, float before)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		FatalImpactReceipt fatalImpactReceipt = Begin(p, point, before);
		if (Object.op_Implicit((Object)(object)fatalImpactReceipt))
		{
			fatalImpactReceipt.cause = "native fall";
		}
		return fatalImpactReceipt;
	}

	internal void EndRequest()
	{
		if (!finished)
		{
			requestOpen = false;
			try
			{
				armed = Object.op_Implicit((Object)(object)player) && before > 0f && ((Character)player).GetHealth() <= 0f;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Fatal impact confirmation unavailable: " + ex));
			}
			if (!armed)
			{
				Close();
			}
		}
	}

	private void OnNativeDeath()
	{
		//IL_0078: 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)
		if (finished)
		{
			return;
		}
		try
		{
			if (Object.op_Implicit((Object)(object)player) && ((Character)player).IsDead() && before > 0f && (requestOpen || armed))
			{
				finished = true;
				Plugin.Log.LogInfo((object)("RRFatalImpact confirmed by native death callback; cause=" + cause + "; position=" + point));
				try
				{
					FatalImpactPresentation.Play(point);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Confirmed fatal presentation failed: " + ex));
				}
				Close();
			}
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogError((object)("Fatal death observer failed without changing death: " + ex2));
			Close();
		}
	}

	private void Update()
	{
		if (!finished)
		{
			if (!requestOpen && Object.op_Implicit((Object)(object)player) && ((Character)player).GetHealth() > 0f)
			{
				Close();
			}
			else if (!Object.op_Implicit((Object)(object)player) || Time.unscaledTime >= expires)
			{
				Plugin.Log.LogWarning((object)"Fatal impact not confirmed by native death; no speculative splat/gore.");
				Close();
			}
		}
	}

	private void Close()
	{
		finished = true;
		if (!object.ReferenceEquals(player, null) && callback != null)
		{
			Player obj = player;
			((Character)obj).m_onDeath = (Action)Delegate.Remove(((Character)obj).m_onDeath, callback);
			callback = null;
		}
		pending.Remove(this);
		Object.Destroy((Object)(object)((Component)this).gameObject);
	}

	private void OnDestroy()
	{
		if (!object.ReferenceEquals(player, null) && callback != null)
		{
			Player obj = player;
			((Character)obj).m_onDeath = (Action)Delegate.Remove(((Character)obj).m_onDeath, callback);
			callback = null;
		}
		pending.Remove(this);
	}

	internal static void Dispose()
	{
		FatalImpactReceipt[] array = pending.ToArray();
		foreach (FatalImpactReceipt fatalImpactReceipt in array)
		{
			if (Object.op_Implicit((Object)(object)fatalImpactReceipt))
			{
				fatalImpactReceipt.Close();
			}
		}
		pending.Clear();
	}
}
internal static class FatalImpactRecording
{
	internal const int Frames = 40421;

	internal const int Rate = 44100;

	internal static float[] Create()
	{
		using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("RamRoyalFlush.FatalImpact");
		if (stream == null || stream.Length != 161684)
		{
			throw new InvalidDataException("Fatal splat PCM missing/invalid.");
		}
		using BinaryReader binaryReader = new BinaryReader(stream);
		float[] array = new float[40421];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = binaryReader.ReadSingle();
			if (float.IsNaN(array[i]) || float.IsInfinity(array[i]) || Math.Abs(array[i]) > 1f)
			{
				throw new InvalidDataException("Fatal splat PCM invalid/clipped.");
			}
		}
		return array;
	}
}
internal static class FixedGameplay
{
	internal const int WoodCost = 10;

	internal const int FineWoodCost = 2;

	internal const float InteractionDurationSeconds = 4f;

	internal const bool CompletionMessagesEnabled = true;

	internal const string CompletionMessages = "You feel strangely lighter. Odin looks away in disgust.|You have made an offering to the All-Farter.|The gods have witnessed your shame.|Valhalla trembles. Your bowels are empty.|A great burden has been lifted.|You feel empty inside.";
}
internal sealed class InteractionSession
{
	internal enum Phase
	{
		Idle,
		Pending,
		Seated,
		Completed,
		Cancelled
	}

	internal enum Result
	{
		None,
		Cancel,
		Complete
	}

	private double requested;

	private double started;

	private double lastTick;

	private double duration;

	internal Phase State { get; private set; }

	internal long Token { get; private set; }

	internal bool WaitingForSeating { get; private set; }

	internal bool Request(long token, double now)
	{
		if (token == 0 || !Finite(now) || State == Phase.Pending || State == Phase.Seated)
		{
			return false;
		}
		Token = token;
		requested = (lastTick = now);
		State = Phase.Pending;
		return true;
	}

	internal bool Grant(long token, double seconds, double now)
	{
		if (State != Phase.Pending || token != Token || !Finite(now) || now < requested || now - requested >= 5.0 || !Finite(seconds) || seconds < 1.0 || seconds > 15.0)
		{
			return false;
		}
		started = (lastTick = now);
		duration = Math.Min(seconds, 0.5);
		State = Phase.Seated;
		return true;
	}

	internal Result Tick(double now, bool validPlayer, bool stillSeated, bool leaseHeld, bool nativeSitting = true)
	{
		if (State != Phase.Pending && State != Phase.Seated)
		{
			return Result.None;
		}
		if (!Finite(now) || now < lastTick || !validPlayer || (State == Phase.Pending && now - requested >= 5.0) || (State == Phase.Seated && (!stillSeated || now - lastTick > 2.5)))
		{
			Cancel();
			return Result.Cancel;
		}
		lastTick = now;
		if (State == Phase.Seated && now - started >= duration)
		{
			if (!leaseHeld)
			{
				Cancel();
				return Result.Cancel;
			}
			if (now - started > 3.0 || (!nativeSitting && now - started >= 3.0))
			{
				Cancel();
				return Result.Cancel;
			}
			WaitingForSeating = !nativeSitting;
			if (WaitingForSeating)
			{
				return Result.None;
			}
			State = Phase.Completed;
			return Result.Complete;
		}
		return Result.None;
	}

	internal void Cancel()
	{
		WaitingForSeating = false;
		State = Phase.Cancelled;
	}

	private static bool Finite(double n)
	{
		if (!double.IsNaN(n))
		{
			return !double.IsInfinity(n);
		}
		return false;
	}
}
internal static class LeaseRules
{
	internal static bool Available(long now, long occupant, long expires)
	{
		if (occupant != 0)
		{
			return expires <= now;
		}
		return true;
	}

	internal static bool Held(long now, long uid, long token, long occupant, long savedToken, long expires)
	{
		if (uid != 0 && token != 0 && uid == occupant && token == savedToken)
		{
			return expires > now;
		}
		return false;
	}
}
public sealed class LaunchCollisionMonitor : MonoBehaviour
{
	private static LaunchCollisionMonitor current;

	private Player player;

	private Rigidbody body;

	private LaunchCollisionScope scope;

	private Vector3 origin;

	private float began;

	private int frames;

	private int contacts;

	private bool ended;

	private bool modeWarning;

	private readonly LaunchImpactRules impacts = new LaunchImpactRules();

	private Vector3 preStepVelocity;

	private Vector3 impactPoint;

	private Vector3 impactNormal;

	private Vector3 impactRelative;

	private string impactObject;

	private bool landingLogged;

	private bool flightDamageEnabled;

	private bool damageSuppressionLogged;

	private bool endingOnLanding;

	private bool flightStarted;

	private bool victoryConsumed;

	private readonly LaunchLandingSequence landingSequence = new LaunchLandingSequence();

	private double supportAt = double.NegativeInfinity;

	private Vector3 supportNormal;

	private Vector3 supportPoint;

	private Collider supportCollider;

	private Rigidbody supportBody;

	internal void RecordLaunch()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)body))
		{
			preStepVelocity = body.linearVelocity;
			flightStarted = true;
		}
	}

	internal static bool IsActiveFor(Player p)
	{
		if (Object.op_Implicit((Object)(object)current) && (Object)(object)current.player == (Object)(object)p)
		{
			return current.CanImpact();
		}
		return false;
	}

	internal static LaunchCollisionMonitor DamageContextFor(Player p)
	{
		if (!IsActiveFor(p))
		{
			return null;
		}
		return current;
	}

	internal static void InterruptForJump(Player p)
	{
		if (IsActiveFor(p))
		{
			current.End("deliberate new jump");
		}
	}

	internal bool ProtectsNativeFallInCall(Player p)
	{
		if ((Object)(object)p == (Object)(object)player && !flightDamageEnabled)
		{
			if (ended)
			{
				return endingOnLanding;
			}
			return true;
		}
		return false;
	}

	internal static LaunchCollisionMonitor Begin(Player p, Rigidbody rb, bool damageEnabled = true)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: 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)
		EndActive("new launch");
		LaunchCollisionMonitor launchCollisionMonitor = ((Component)p).gameObject.AddComponent<LaunchCollisionMonitor>();
		launchCollisionMonitor.player = p;
		launchCollisionMonitor.body = rb;
		launchCollisionMonitor.origin = rb.position;
		launchCollisionMonitor.began = Time.unscaledTime;
		launchCollisionMonitor.flightDamageEnabled = damageEnabled;
		try
		{
			launchCollisionMonitor.scope = new LaunchCollisionScope(() => rb.collisionDetectionMode, delegate(CollisionDetectionMode mode)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)rb))
				{
					rb.collisionDetectionMode = mode;
				}
			});
			current = launchCollisionMonitor;
			Plugin.Log.LogInfo((object)string.Concat("RRLaunch CCD begin: original=", launchCollisionMonitor.scope.Original, "; active=", rb.collisionDetectionMode, "; kinematic=", rb.isKinematic, "; detectCollisions=", rb.detectCollisions, "; fixedDt=", Time.fixedDeltaTime, "; flightDamage=", damageEnabled));
			return launchCollisionMonitor;
		}
		catch
		{
			launchCollisionMonitor.End("setup failure");
			throw;
		}
	}

	internal static void EndActive(string reason)
	{
		if (Object.op_Implicit((Object)(object)current))
		{
			current.End(reason);
		}
	}

	internal void End(string reason, bool destroy = true)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: 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_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		if (ended)
		{
			if (reason != "landed")
			{
				endingOnLanding = false;
			}
			return;
		}
		endingOnLanding = reason == "landed";
		impacts.Stop();
		try
		{
			if (scope != null)
			{
				scope.Dispose();
			}
		}
		finally
		{
			ended = true;
			if (Object.op_Implicit((Object)(object)body) && scope != null && body.collisionDetectionMode != scope.Original)
			{
				Plugin.Log.LogError((object)string.Concat("RRLaunch original collision mode restoration FAILED: expected=", scope.Original, "; actual=", body.collisionDetectionMode));
			}
			if (Object.op_Implicit((Object)(object)body))
			{
				ManualLogSource log = Plugin.Log;
				object[] array = new object[10]
				{
					"RRLaunch end: reason=",
					reason,
					"; original=",
					(scope != null) ? ((object)scope.Original).ToString() : "unavailable",
					"; restored=",
					body.collisionDetectionMode,
					"; horizontalDistance=",
					null,
					null,
					null
				};
				Vector3 val = Vector3.ProjectOnPlane(body.position - origin, Vector3.up);
				array[7] = ((Vector3)(ref val)).magnitude.ToString("F3");
				array[8] = "; elapsed=";
				array[9] = (Time.unscaledTime - began).ToString("F3");
				log.LogInfo((object)string.Concat(array));
			}
			if ((Object)(object)current == (Object)(object)this)
			{
				current = null;
			}
			if (destroy)
			{
				Object.Destroy((Object)(object)this);
			}
		}
	}

	private void FixedUpdate()
	{
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: 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_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a9: Invalid comparison between Unknown and I4
		//IL_0356: Unknown result type (might be due to invalid IL or missing references)
		//IL_035b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0381: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
		if (ended)
		{
			return;
		}
		ZNetView val = (Object.op_Implicit((Object)(object)player) ? ((Component)player).GetComponent<ZNetView>() : null);
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner() || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || ((Character)player).IsAttached() || ((Character)player).IsSwimming() || body.isKinematic || !body.useGravity || !body.detectCollisions || !flightStarted)
		{
			End("interruption/death/ownership");
			return;
		}
		bool nativeGrounded = Time.unscaledTime - began > 0.15f && ((Character)player).IsOnGround();
		FlushImpact(nativeGrounded);
		if (ended)
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead())
		{