Decompiled source of PerformanceTweaks v1.0.0

PerformanceTweaks/PerformanceTweaks.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using PerformanceTweaks.Configuration;
using PerformanceTweaks.Patches;
using PerformanceTweaks.Runtime;
using UnityEngine;
using cakeslice;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PerformanceTweaks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Keevi")]
[assembly: AssemblyProduct("PerformanceTweaks")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5406f2ba-bc59-485d-91b1-3c59b7af05fb")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace PerformanceTweaks
{
	[BepInPlugin("com.keevi.performancetweaks", "Performance Tweaks", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "com.keevi.performancetweaks";

		public const string PLUGIN_NAME = "Performance Tweaks";

		public const string PLUGIN_VERSION = "1.0.0";

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				ModConfig.Bind(((BaseUnityPlugin)this).Config);
				LogSettings();
				ApplyHarmonyPatches();
				ApplySettingChangesImmediately();
				((Component)this).gameObject.AddComponent<FrameUpdater>();
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} failed to load and will not activate: {1}", "Performance Tweaks", arg));
				return;
			}
			Log.LogInfo((object)string.Format("{0} v{1} loaded.", "Performance Tweaks", "1.0.0"));
		}

		private void LogSettings()
		{
			Log.LogInfo((object)"Configuration:");
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)this).Config)
			{
				Log.LogInfo((object)$"  {item.Key.Key}: {item.Value.BoxedValue}");
			}
		}

		private static void ApplyHarmonyPatches()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Harmony val = new Harmony("com.keevi.performancetweaks");
			val.PatchAll(Assembly.GetExecutingAssembly());
			foreach (MethodBase patchedMethod in val.GetPatchedMethods())
			{
				Log.LogInfo((object)$"Patched {patchedMethod.DeclaringType}.{patchedMethod.Name}");
			}
		}

		private static void ApplySettingChangesImmediately()
		{
			ModConfig.SkipUnchangedRopes.SettingChanged += delegate
			{
				RopeLod.RebuildAll();
			};
			ModConfig.SimplifyDistantRopes.SettingChanged += delegate
			{
				RopeLod.RebuildAll();
			};
			ModConfig.DirtCameraOnDemand.SettingChanged += delegate
			{
				DirtCameraSwitch.Apply(MasterPainter.instance);
			};
			ModConfig.OutlineCamerasOnDemand.SettingChanged += delegate
			{
				OutlineEffectPatch.Forget();
			};
		}
	}
}
namespace PerformanceTweaks.Runtime
{
	internal static class BoatMotion
	{
		private sealed class BoatState
		{
			public readonly Transform Boat;

			public float StepTime = float.NegativeInfinity;

			public bool IsStill;

			private Vector3 position;

			private Quaternion rotation;

			public BoatState(Transform boat)
			{
				Boat = boat;
			}

			public void Measure(float stepTime)
			{
				//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_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//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_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				Vector3 newPosition = Boat.position;
				Quaternion newRotation = Boat.rotation;
				IsStill = stepTime - StepTime < 1.5f * Time.fixedDeltaTime && MovementMillimetres(newPosition, newRotation) < 5f;
				StepTime = stepTime;
				position = newPosition;
				rotation = newRotation;
			}

			private float MovementMillimetres(Vector3 newPosition, Quaternion newRotation)
			{
				//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				float num = Vector3.Distance(newPosition, position);
				float num2 = Quaternion.Angle(newRotation, rotation) * ((float)Math.PI / 180f) * 20f;
				return (num + num2) * 1000f;
			}
		}

		private const float ReachMetres = 20f;

		private const float StillMillimetres = 5f;

		private const float MillimetresPerMetre = 1000f;

		private const float MaxStepGap = 1.5f;

		private const float ForgetAfterSeconds = 1f;

		private static readonly List<BoatState> States = new List<BoatState>();

		private static BoatState lastState;

		public static bool IsStill(Transform boat)
		{
			float fixedTime = Time.fixedTime;
			if (lastState == null || lastState.Boat != boat)
			{
				lastState = StateOf(boat, fixedTime);
			}
			if (lastState.StepTime != fixedTime)
			{
				lastState.Measure(fixedTime);
			}
			return lastState.IsStill;
		}

		private static BoatState StateOf(Transform boat, float stepTime)
		{
			foreach (BoatState state in States)
			{
				if (state.Boat == boat)
				{
					return state;
				}
			}
			ForgetStaleStates(stepTime);
			BoatState boatState = new BoatState(boat);
			States.Add(boatState);
			return boatState;
		}

		private static void ForgetStaleStates(float stepTime)
		{
			for (int num = States.Count - 1; num >= 0; num--)
			{
				if (stepTime - States[num].StepTime > 1f)
				{
					States.RemoveAt(num);
				}
			}
		}
	}
	internal static class DirtCameraSwitch
	{
		private const int FramesRenderedPerClean = 2;

		private static int renderUntilFrame = -1;

		public static void Apply(MasterPainter painter)
		{
			if (!((Object)(object)painter == (Object)null))
			{
				Camera component = ((Component)painter).GetComponent<Camera>();
				bool flag = !ModConfig.DirtCameraOnDemand.Value || Time.frameCount <= renderUntilFrame;
				if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled != flag)
				{
					((Behaviour)component).enabled = flag;
				}
			}
		}

		public static void RenderForClean(MasterPainter painter)
		{
			renderUntilFrame = Time.frameCount + 2 - 1;
			Apply(painter);
			((MonoBehaviour)painter).StartCoroutine(ApplyAfterClean(painter));
		}

		private static IEnumerator ApplyAfterClean(MasterPainter painter)
		{
			while (Time.frameCount <= renderUntilFrame)
			{
				yield return null;
			}
			Apply(painter);
		}
	}
	internal sealed class FrameUpdater : MonoBehaviour
	{
		private void Update()
		{
			HiddenRopePauser.Update();
			ShopItemFreezer.Update();
		}
	}
	internal static class HiddenRopePauser
	{
		private sealed class PausableRope
		{
			public readonly RopeEffect Rope;

			public int HiddenFrames;

			public PausableRope(RopeEffect rope)
			{
				Rope = rope;
			}
		}

		private const int RefreshInterval = 20;

		private const int PruneEveryRegistrations = 256;

		private static readonly FieldRef<RopeEffect, ClothRope> ClothOf = AccessTools.FieldRefAccess<RopeEffect, ClothRope>("clothRope");

		private static readonly FieldRef<RopeEffect, bool> IsOutOfRange = AccessTools.FieldRefAccess<RopeEffect, bool>("outOfRange");

		private static readonly Predicate<PausableRope> IsDestroyed = (PausableRope entry) => (Object)(object)entry.Rope == (Object)null;

		private static readonly List<PausableRope> ropes = new List<PausableRope>();

		private static bool hasPausedRopes;

		public static void Register(RopeEffect rope)
		{
			ropes.Add(new PausableRope(rope));
			if (ropes.Count % 256 == 0)
			{
				ropes.RemoveAll(IsDestroyed);
			}
		}

		public static void Update()
		{
			bool value = ModConfig.PauseHiddenRopes.Value;
			if (!value && !hasPausedRopes)
			{
				return;
			}
			hasPausedRopes = false;
			for (int num = ropes.Count - 1; num >= 0; num--)
			{
				PausableRope pausableRope = ropes[num];
				if ((Object)(object)pausableRope.Rope == (Object)null)
				{
					ropes.RemoveAt(num);
				}
				else
				{
					bool flag = !value || NeedsUpdate(pausableRope);
					if (((Behaviour)pausableRope.Rope).enabled != flag)
					{
						((Behaviour)pausableRope.Rope).enabled = flag;
					}
					if (!flag)
					{
						hasPausedRopes = true;
					}
				}
			}
		}

		private static bool NeedsUpdate(PausableRope entry)
		{
			RopeEffect rope = entry.Rope;
			if ((Object)(object)rope.attachment == (Object)null || IsOutOfRange.Invoke(rope) || !IsHidden3DRope(rope))
			{
				entry.HiddenFrames = 0;
				return true;
			}
			entry.HiddenFrames++;
			return entry.HiddenFrames % 20 == 0;
		}

		private static bool IsHidden3DRope(RopeEffect rope)
		{
			ClothRope val = ClothOf.Invoke(rope);
			if ((Object)(object)val != (Object)null && (Object)(object)val.skinned != (Object)null && ((Component)val).gameObject.activeSelf)
			{
				return !((Renderer)val.skinned).isVisible;
			}
			return false;
		}
	}
	internal static class ItemPlacement
	{
		public static bool TryPlace(Transform twin, ShipItem item)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!RedundantWriteGuard.IsSkipping)
			{
				return false;
			}
			Transform currentActualBoat = item.currentActualBoat;
			if (currentActualBoat == null || !BoatMotion.IsStill(currentActualBoat))
			{
				return false;
			}
			Transform currentWalkCol = item.currentWalkCol;
			Vector3 position = currentActualBoat.TransformPoint(currentWalkCol.InverseTransformPoint(twin.position));
			Quaternion rotation = currentActualBoat.rotation * (Quaternion.Inverse(currentWalkCol.rotation) * twin.rotation);
			Transform transform = ((Component)item).transform;
			if (RedundantWriteGuard.IsSamePosition(transform, position) && RedundantWriteGuard.IsSameRotation(transform, rotation))
			{
				return true;
			}
			transform.position = position;
			transform.rotation = rotation;
			return true;
		}
	}
	internal static class RedundantWriteGuard
	{
		private const float PositionTolerance = 0.001f;

		private const float RotationToleranceDegrees = 0.1f;

		private static readonly float MinRotationDot = Mathf.Cos(0.0008726646f);

		public static bool IsSkipping => ModConfig.SkipRedundantItemUpdates.Value;

		public static void SetPosition(Transform transform, Vector3 position)
		{
			//IL_0012: 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 (!IsSkipping || !IsSamePosition(transform, position))
			{
				transform.position = position;
			}
		}

		public static void SetRotation(Transform transform, Quaternion rotation)
		{
			//IL_0012: 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 (!IsSkipping || !IsSameRotation(transform, rotation))
			{
				transform.rotation = rotation;
			}
		}

		public static void SetLocalScale(Transform transform, Vector3 scale)
		{
			//IL_0017: 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)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsSkipping || !(transform.localScale == scale))
			{
				transform.localScale = scale;
			}
		}

		public static void SetIsTrigger(Collider collider, bool isTrigger)
		{
			if (!IsSkipping || collider.isTrigger != isTrigger)
			{
				collider.isTrigger = isTrigger;
			}
		}

		public static void SetEnabled(Collider collider, bool enabled)
		{
			if (!IsSkipping || collider.enabled != enabled)
			{
				collider.enabled = enabled;
			}
		}

		public static bool IsSamePosition(Transform transform, Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = transform.position - position;
			return ((Vector3)(ref val)).sqrMagnitude < 1.0000001E-06f;
		}

		public static bool IsSameRotation(Transform transform, Quaternion rotation)
		{
			//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)
			return Mathf.Abs(Quaternion.Dot(transform.rotation, rotation)) >= MinRotationDot;
		}
	}
	internal static class RopeLod
	{
		private sealed class RopeState
		{
			public readonly ClothRope Cloth;

			public readonly PickupableBoatMooringRope MooringLine;

			public RopeShapeKey Key;

			public Quaternion RopeEndRotation;

			public Quaternion AttachmentRotation;

			public bool NeedsRebuild = true;

			public bool IsFlat;

			public RopeState(ClothRope cloth, PickupableBoatMooringRope mooringLine)
			{
				Cloth = cloth;
				MooringLine = mooringLine;
			}

			public void Remember(RopeEffect rope, RopeShapeKey key)
			{
				//IL_000e: 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_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				Key = key;
				RopeEndRotation = ((Component)rope).transform.localRotation;
				AttachmentRotation = rope.attachment.localRotation;
				NeedsRebuild = false;
			}
		}

		private const float MinRangeCheckInterval = 5f;

		private const float MaxRangeCheckInterval = 8f;

		private const float FlatRopeDeadZone = 1f;

		private static readonly Action<RopeEffect> UpdateAttachments = RopeMethod("UpdateAttachments");

		private static readonly Action<RopeEffect> ApplyLimits = RopeMethod("ApplyLimits");

		private static readonly Action<RopeEffect> UpdateTension = RopeMethod("UpdateTension");

		private static readonly Action<RopeEffect> DisplayRope = RopeMethod("DisplayRope");

		private static readonly Action<RopeEffect> CheckDistance = RopeMethod("CheckDistance");

		private static readonly Dictionary<RopeEffect, RopeState> states = new Dictionary<RopeEffect, RopeState>();

		private static int cameraFrame = -1;

		private static Vector3 cameraPosition;

		private static bool IsAnySettingOn
		{
			get
			{
				if (!ModConfig.SkipUnchangedRopes.Value)
				{
					return ModConfig.SimplifyDistantRopes.Value != DistantRopeSimplification.Off;
				}
				return true;
			}
		}

		public static bool TryUpdate(RopeEffect rope, LineRenderer line, ClothRope cloth, ref float rangeCheckTimer, ref bool isOutOfRange)
		{
			if (!IsAnySettingOn || !Settings.clothRopes || (Object)(object)cloth == (Object)null || (Object)(object)rope.attachment == (Object)null || GameState.shipyardUpdatingOrder || (Object)(object)Refs.observerMirror == (Object)null)
			{
				return false;
			}
			RunRangeCheck(rope, ref rangeCheckTimer);
			RopeState state = StateOf(rope, cloth);
			if (isOutOfRange)
			{
				Hide(line, cloth, state);
			}
			else if (WantsFlatRope(rope, state))
			{
				DrawFlat(rope, line, cloth, state);
			}
			else
			{
				Draw3D(rope, line, cloth, state);
			}
			return true;
		}

		public static void RebuildAll()
		{
			foreach (RopeState value in states.Values)
			{
				value.NeedsRebuild = true;
			}
		}

		public static void Forget(RopeEffect rope)
		{
			states.Remove(rope);
		}

		private static Action<RopeEffect> RopeMethod(string name)
		{
			return AccessTools.MethodDelegate<Action<RopeEffect>>(AccessTools.Method(typeof(RopeEffect), name, (Type[])null, (Type[])null), (object)null, true);
		}

		private static void RunRangeCheck(RopeEffect rope, ref float rangeCheckTimer)
		{
			if (rangeCheckTimer <= 0f)
			{
				CheckDistance(rope);
				rangeCheckTimer = Random.Range(5f, 8f);
			}
			rangeCheckTimer -= Time.deltaTime;
		}

		private static RopeState StateOf(RopeEffect rope, ClothRope cloth)
		{
			if (!states.TryGetValue(rope, out var value) || (Object)(object)value.Cloth != (Object)(object)cloth)
			{
				value = new RopeState(cloth, ((Component)rope).GetComponent<PickupableBoatMooringRope>());
				states[rope] = value;
			}
			return value;
		}

		private static void Hide(LineRenderer line, ClothRope cloth, RopeState state)
		{
			if (((Renderer)line).enabled)
			{
				((Renderer)line).enabled = false;
			}
			if (((Component)cloth).gameObject.activeSelf)
			{
				((Component)cloth).gameObject.SetActive(false);
			}
			state.NeedsRebuild = true;
		}

		private static bool WantsFlatRope(RopeEffect rope, RopeState state)
		{
			float num = FlatRopeDistance(ModConfig.SimplifyDistantRopes.Value);
			if (state.IsFlat)
			{
				num -= 1f;
			}
			state.IsFlat = DistanceFromViewer(rope) > num;
			return state.IsFlat;
		}

		private static float FlatRopeDistance(DistantRopeSimplification simplification)
		{
			return simplification switch
			{
				DistantRopeSimplification.Quality => 50f, 
				DistantRopeSimplification.Default => 30f, 
				DistantRopeSimplification.Performance => 15f, 
				_ => float.PositiveInfinity, 
			};
		}

		private static float DistanceFromViewer(RopeEffect rope)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)rope).transform.position;
			Vector3 val = rope.attachment.position - position;
			Vector3 val2 = ViewerPosition();
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			float num = ((sqrMagnitude > 0f) ? Mathf.Clamp01(Vector3.Dot(val2 - position, val) / sqrMagnitude) : 0f);
			return Vector3.Distance(val2, position + val * num);
		}

		private static Vector3 ViewerPosition()
		{
			//IL_0017: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.RopeDistanceMeasuredFrom.Value == RopeDistanceOrigin.Player)
			{
				return ((Component)Refs.observerMirror).transform.position;
			}
			if (cameraFrame != Time.frameCount)
			{
				Camera main = Camera.main;
				cameraPosition = (((Object)(object)main != (Object)null) ? ((Component)main).transform.position : ((Component)Refs.observerMirror).transform.position);
				cameraFrame = Time.frameCount;
			}
			return cameraPosition;
		}

		private static void DrawFlat(RopeEffect rope, LineRenderer line, ClothRope cloth, RopeState state)
		{
			if (!((Renderer)line).enabled)
			{
				((Renderer)line).enabled = true;
			}
			if (((Component)cloth).gameObject.activeSelf)
			{
				((Component)cloth).gameObject.SetActive(false);
			}
			RunGameRopeSteps(rope);
			state.NeedsRebuild = true;
		}

		private static void Draw3D(RopeEffect rope, LineRenderer line, ClothRope cloth, RopeState state)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (((Renderer)line).enabled)
			{
				((Renderer)line).enabled = false;
			}
			if (!((Component)cloth).gameObject.activeSelf)
			{
				((Component)cloth).gameObject.SetActive(true);
				state.NeedsRebuild = true;
			}
			bool value = ModConfig.SkipUnchangedRopes.Value;
			bool flag = value && IsStowed(state.MooringLine);
			RopeShapeKey key = (value ? RopeShapeKey.Capture(rope, flag) : default(RopeShapeKey));
			if (value && !state.NeedsRebuild && key.Matches(state.Key))
			{
				KeepEndRotations(rope, state);
				return;
			}
			if (flag)
			{
				UprightRope.Build(rope, ((Component)state.MooringLine.GetBoatRigidbody()).transform.up);
			}
			else
			{
				RunGameRopeSteps(rope);
			}
			cloth.UpdateRope();
			state.Remember(rope, key);
		}

		private static bool IsStowed(PickupableBoatMooringRope line)
		{
			if ((Object)(object)line != (Object)null && !Object.op_Implicit((Object)(object)((PickupableItem)line).held) && !line.IsMoored())
			{
				return line.IsAtInitialPos();
			}
			return false;
		}

		private static void RunGameRopeSteps(RopeEffect rope)
		{
			UpdateAttachments(rope);
			ApplyLimits(rope);
			UpdateTension(rope);
			DisplayRope(rope);
		}

		private static void KeepEndRotations(RopeEffect rope, RopeState state)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (((Component)rope).transform.localRotation != state.RopeEndRotation)
			{
				((Component)rope).transform.localRotation = state.RopeEndRotation;
			}
			if (rope.attachment.localRotation != state.AttachmentRotation)
			{
				rope.attachment.localRotation = state.AttachmentRotation;
			}
		}
	}
	internal struct RopeShapeKey
	{
		private const float PositionTolerance = 0.001f;

		private const float SagSwingTolerance = 0.005f;

		private const float LengthTolerance = 0.0001f;

		private const float MaxTiltTolerance = 0.08726646f;

		private const float GameCurveRateFactor = 0.75f;

		private const float BezierMidpointShare = 0.75f;

		private const float MaxHalfSagAngleDegrees = 90f;

		private Vector3 ropeEnd;

		private Vector3 attachmentEnd;

		private Vector3 worldUp;

		private float sag;

		private bool isStowedLine;

		private bool ropeEndIsHigher;

		private float currentLength;

		private float totalLength;

		private float width;

		private float sailArea;

		public static RopeShapeKey Capture(RopeEffect rope, bool isStowedLine)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = ((Component)rope).transform.parent;
			Vector3 position = ((Component)rope).transform.position;
			Vector3 position2 = rope.attachment.position;
			return new RopeShapeKey
			{
				ropeEnd = (((Object)(object)parent != (Object)null) ? parent.InverseTransformPoint(position) : position),
				attachmentEnd = (((Object)(object)parent != (Object)null) ? parent.InverseTransformPoint(position2) : position2),
				worldUp = (((Object)(object)parent != (Object)null) ? parent.InverseTransformDirection(Vector3.up) : Vector3.up),
				sag = EstimateSag(rope, Vector3.Distance(position, position2)),
				isStowedLine = isStowedLine,
				ropeEndIsHigher = (position.y >= position2.y),
				currentLength = rope.currentRopeLength,
				totalLength = rope.totalRopeLength,
				width = rope.ropeWidth,
				sailArea = rope.attachedSailArea
			};
		}

		public bool Matches(RopeShapeKey other)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (IsNear(ropeEnd, other.ropeEnd) && IsNear(attachmentEnd, other.attachmentEnd) && isStowedLine == other.isStowedLine && (isStowedLine || (ropeEndIsHigher == other.ropeEndIsHigher && IsTiltNear(other))) && Mathf.Abs(currentLength - other.currentLength) < 0.0001f && Mathf.Abs(totalLength - other.totalLength) < 0.0001f && Mathf.Abs(width - other.width) < 0.0001f)
			{
				return Mathf.Abs(sailArea - other.sailArea) < 0.0001f;
			}
			return false;
		}

		private bool IsTiltNear(RopeShapeKey other)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Cos(TiltTolerance(Mathf.Max(sag, other.sag)));
			return Vector3.Dot(((Vector3)(ref worldUp)).normalized, ((Vector3)(ref other.worldUp)).normalized) > num;
		}

		private static float EstimateSag(RopeEffect rope, float span)
		{
			float num = 1f - rope.totalRopeLength;
			float num2 = num * Mathf.Clamp01(rope.currentRopeLength) * 0.75f;
			float num3 = Mathf.Clamp(num * 90f, -90f, 90f);
			return Mathf.Abs(0.75f * span * num2 * Mathf.Sin(num3 * ((float)Math.PI / 180f)));
		}

		private static float TiltTolerance(float sag)
		{
			if (!(sag * 0.08726646f > 0.005f))
			{
				return 0.08726646f;
			}
			return 0.005f / sag;
		}

		private static bool IsNear(Vector3 point, Vector3 other)
		{
			//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)
			Vector3 val = point - other;
			return ((Vector3)(ref val)).sqrMagnitude < 1.0000001E-06f;
		}
	}
	internal static class ShopItemFreezer
	{
		private sealed class FreezableItem
		{
			public readonly ShipItem Item;

			public int IdleFrames;

			public float LastHeldTime = float.NegativeInfinity;

			private readonly List<Behaviour> switchedOff = new List<Behaviour>();

			private bool? mayFreeze;

			public bool IsFrozen { get; private set; }

			public bool MayFreeze
			{
				get
				{
					if (!mayFreeze.HasValue)
					{
						mayFreeze = !HasKeepUpdatingMarker(Item);
					}
					return mayFreeze.Value;
				}
			}

			public FreezableItem(ShipItem item)
			{
				Item = item;
			}

			public void Freeze()
			{
				SwitchOff((Behaviour)(object)Item);
				SwitchOff((Behaviour)(object)Item.itemRigidbodyC);
				SwitchOff((Behaviour)(object)((PickupableItem)Item).colChecker);
				SwitchOff((Behaviour)(object)((Component)Item).GetComponent<FoodState>());
				SwitchOff((Behaviour)(object)((Component)Item).GetComponent<CookableFood>());
				IsFrozen = true;
			}

			public void Thaw()
			{
				foreach (Behaviour item in switchedOff)
				{
					if ((Object)(object)item != (Object)null)
					{
						item.enabled = true;
					}
				}
				switchedOff.Clear();
				IdleFrames = 0;
				IsFrozen = false;
			}

			private void SwitchOff(Behaviour behaviour)
			{
				if (!((Object)(object)behaviour == (Object)null) && behaviour.enabled)
				{
					behaviour.enabled = false;
					switchedOff.Add(behaviour);
				}
			}
		}

		private const string KeepUpdatingMarker = "KeepUpdatingInShop";

		private const int IdleFramesBeforeFreezing = 10;

		private const float IdleSecondsAfterHolding = 1f;

		private static readonly FieldRef<GoPointerButton, bool> IsClicked = AccessTools.FieldRefAccess<GoPointerButton, bool>("isClicked");

		private static readonly FieldRef<GoPointerButton, GoPointer> StickyClickedBy = AccessTools.FieldRefAccess<GoPointerButton, GoPointer>("stickyClickedBy");

		private static readonly FieldRef<GoPointerButton, float> FlashOutlineTimer = AccessTools.FieldRefAccess<GoPointerButton, float>("flashOutlineTimer");

		private static readonly FieldRef<GoPointerButton, Outline> OutlineOf = AccessTools.FieldRefAccess<GoPointerButton, Outline>("outline");

		private static readonly FieldRef<ShipItemLight, bool> IsLightOn = AccessTools.FieldRefAccess<ShipItemLight, bool>("on");

		private static readonly List<FreezableItem> items = new List<FreezableItem>();

		private static readonly Dictionary<ShipItem, FreezableItem> itemsByShipItem = new Dictionary<ShipItem, FreezableItem>();

		private static bool hasFrozenItems;

		public static void Register(ShipItem item)
		{
			if (!((Object)(object)item == (Object)null) && !itemsByShipItem.ContainsKey(item))
			{
				FreezableItem freezableItem = new FreezableItem(item);
				items.Add(freezableItem);
				itemsByShipItem[item] = freezableItem;
			}
		}

		public static void Update()
		{
			bool value = ModConfig.FreezeIdleShopItems.Value;
			if (!value && !hasFrozenItems)
			{
				return;
			}
			hasFrozenItems = false;
			for (int num = items.Count - 1; num >= 0; num--)
			{
				FreezableItem freezableItem = items[num];
				if ((Object)(object)freezableItem.Item == (Object)null)
				{
					items.RemoveAt(num);
					itemsByShipItem.Remove(freezableItem.Item);
				}
				else
				{
					UpdateEntry(freezableItem, value);
					hasFrozenItems |= freezableItem.IsFrozen;
				}
			}
		}

		public static void Thaw(GoPointerButton button)
		{
			ShipItem val = (ShipItem)(object)((button is ShipItem) ? button : null);
			if (val != null && itemsByShipItem.TryGetValue(val, out var value) && value.IsFrozen)
			{
				value.Thaw();
			}
		}

		private static void UpdateEntry(FreezableItem entry, bool isFreezing)
		{
			if (Object.op_Implicit((Object)(object)((PickupableItem)entry.Item).held))
			{
				entry.LastHeldTime = Time.time;
			}
			if (!isFreezing || !IsIdle(entry))
			{
				if (entry.IsFrozen)
				{
					entry.Thaw();
				}
				entry.IdleFrames = 0;
			}
			else if (!entry.IsFrozen && ++entry.IdleFrames >= 10)
			{
				entry.Freeze();
			}
		}

		private static bool IsIdle(FreezableItem entry)
		{
			ShipItem item = entry.Item;
			if (item.sold || Object.op_Implicit((Object)(object)((PickupableItem)item).held) || Time.time - entry.LastHeldTime < 1f)
			{
				return false;
			}
			if (((GoPointerButton)item).IsLookedAt() || IsClicked.Invoke((GoPointerButton)(object)item) || (Object)(object)StickyClickedBy.Invoke((GoPointerButton)(object)item) != (Object)null)
			{
				return false;
			}
			if (FlashOutlineTimer.Invoke((GoPointerButton)(object)item) > 0f || ((GoPointerButton)item).enableRedOutline)
			{
				return false;
			}
			Outline val = OutlineOf.Invoke((GoPointerButton)(object)item);
			if (((Object)(object)val == (Object)null || !((Behaviour)val).enabled) && entry.MayFreeze)
			{
				return !AnimatesWhileIdle(item);
			}
			return false;
		}

		private static bool AnimatesWhileIdle(ShipItem item)
		{
			if (!(item is ShipItemClock) && !(item is ShipItemTotem))
			{
				ShipItemLight val = (ShipItemLight)(object)((item is ShipItemLight) ? item : null);
				if (val != null)
				{
					return IsLightOn.Invoke(val);
				}
				return false;
			}
			return true;
		}

		private static bool HasKeepUpdatingMarker(ShipItem item)
		{
			MonoBehaviour[] componentsInChildren = ((Component)item).GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "KeepUpdatingInShop")
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class UnmovedCrateContents
	{
		private struct CratePose
		{
			private Transform parent;

			private Vector3 localPosition;

			private Quaternion localRotation;

			private int contentsHash;

			public static bool TryCapture(CrateInventory crate, out CratePose pose)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				Transform transform = ((Component)crate).transform;
				pose = new CratePose
				{
					parent = transform.parent,
					localPosition = transform.localPosition,
					localRotation = transform.localRotation,
					contentsHash = crate.containedItems.Count
				};
				foreach (ShipItem containedItem in crate.containedItems)
				{
					if ((Object)(object)containedItem == (Object)null || (Object)(object)((Component)containedItem).transform.parent != (Object)(object)pose.parent)
					{
						return false;
					}
					pose.contentsHash = pose.contentsHash * 31 + ((Object)containedItem).GetInstanceID();
				}
				return true;
			}

			public bool Matches(CratePose other)
			{
				//IL_0022: 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)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)parent == (Object)(object)other.parent && contentsHash == other.contentsHash)
				{
					Vector3 val = localPosition - other.localPosition;
					if (((Vector3)(ref val)).sqrMagnitude < 9.999999E-09f)
					{
						return Mathf.Abs(Quaternion.Dot(localRotation, other.localRotation)) >= 0.9999999f;
					}
				}
				return false;
			}
		}

		private const float PositionTolerance = 0.0001f;

		private const float MinRotationDot = 0.9999999f;

		private const int PruneEveryChecks = 4096;

		private static readonly Dictionary<CrateInventory, CratePose> lastPoseByCrate = new Dictionary<CrateInventory, CratePose>();

		private static readonly List<CrateInventory> destroyedCrates = new List<CrateInventory>();

		private static int checksSincePrune;

		public static bool CanSkip(CrateInventory crate)
		{
			if (!ModConfig.SkipUnmovedCrateContents.Value)
			{
				return false;
			}
			PruneDestroyedCratesNowAndThen();
			if (IsOpenInUi(crate) || !CratePose.TryCapture(crate, out var pose))
			{
				lastPoseByCrate.Remove(crate);
				return false;
			}
			CratePose value;
			bool result = lastPoseByCrate.TryGetValue(crate, out value) && value.Matches(pose);
			lastPoseByCrate[crate] = pose;
			return result;
		}

		private static void PruneDestroyedCratesNowAndThen()
		{
			if (++checksSincePrune < 4096)
			{
				return;
			}
			checksSincePrune = 0;
			foreach (CrateInventory key in lastPoseByCrate.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					destroyedCrates.Add(key);
				}
			}
			foreach (CrateInventory destroyedCrate in destroyedCrates)
			{
				lastPoseByCrate.Remove(destroyedCrate);
			}
			destroyedCrates.Clear();
		}

		private static bool IsOpenInUi(CrateInventory crate)
		{
			CrateInventoryUI instance = CrateInventoryUI.instance;
			if ((Object)(object)instance != (Object)null && instance.showingUI)
			{
				return (Object)(object)instance.currentCrate == (Object)(object)crate;
			}
			return false;
		}
	}
	internal static class UprightRope
	{
		private const float CurveRateFactor = 0.75f;

		private const float MaxSagDegrees = 180f;

		private const float UprightDegrees = 90f;

		private static readonly FieldRef<RopeEffect, Transform> AttachmentOne = AccessTools.FieldRefAccess<RopeEffect, Transform>("attachmentOne");

		private static readonly FieldRef<RopeEffect, Transform> AttachmentTwo = AccessTools.FieldRefAccess<RopeEffect, Transform>("attachmentTwo");

		private static readonly Action<RopeEffect> ApplyLimits = RopeMethod("ApplyLimits");

		private static readonly Action<RopeEffect> DisplayRope = RopeMethod("DisplayRope");

		public static void Build(RopeEffect rope, Vector3 up)
		{
			//IL_0001: 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)
			AssignEnds(rope, up);
			ApplyLimits(rope);
			ApplyTension(rope, up);
			DisplayRope(rope);
		}

		private static Action<RopeEffect> RopeMethod(string name)
		{
			return AccessTools.MethodDelegate<Action<RopeEffect>>(AccessTools.Method(typeof(RopeEffect), name, (Type[])null, (Type[])null), (object)null, true);
		}

		private static void AssignEnds(RopeEffect rope, Vector3 up)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)rope).transform;
			Transform attachment = rope.attachment;
			bool flag = !rope.sheet && !rope.jibSheet && Vector3.Dot(transform.position - attachment.position, up) >= 0f;
			AttachmentOne.Invoke(rope) = (flag ? transform : attachment);
			AttachmentTwo.Invoke(rope) = (flag ? attachment : transform);
		}

		private static void ApplyTension(RopeEffect rope, Vector3 up)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			Transform val = AttachmentOne.Invoke(rope);
			Transform val2 = AttachmentTwo.Invoke(rope);
			val.LookAt(val2, up);
			val2.LookAt(val, up);
			float num = 1f - rope.totalRopeLength;
			rope.curveRate = num * rope.currentRopeLength * 0.75f;
			float num2 = (90f - Vector3.Angle(val2.forward, up)) / 90f;
			float num3 = num * 180f;
			val.Rotate(Vector3.right * (0.5f - 0.5f * num2) * num3, (Space)1);
			val2.Rotate(Vector3.right * (0.5f + 0.5f * num2) * num3, (Space)1);
		}
	}
}
namespace PerformanceTweaks.Patches
{
	[HarmonyPatch(typeof(CrateInventory), "LateUpdate")]
	internal static class CrateContentsPatch
	{
		private static bool Prefix(CrateInventory __instance)
		{
			return !UnmovedCrateContents.CanSkip(__instance);
		}
	}
	[HarmonyPatch(typeof(FoodState), "Update")]
	internal static class FoodDescriptionPatch
	{
		private const int RefreshIntervalFrames = 15;

		private static readonly MethodInfo UpdateLookTextMethod = AccessTools.Method(typeof(FoodState), "UpdateLookText", (Type[])null, (Type[])null);

		private static readonly Action<FoodState> UpdateLookText = AccessTools.MethodDelegate<Action<FoodState>>(UpdateLookTextMethod, (object)null, true);

		private static readonly FieldRef<FoodState, ShipItemFood> FoodOf = AccessTools.FieldRefAccess<FoodState, ShipItemFood>("food");

		public static void RefreshIfNeeded(FoodState state)
		{
			if (!ModConfig.FoodDescriptionsOnDemand.Value || IsShown(FoodOf.Invoke(state)) || IsOnTurn(state))
			{
				UpdateLookText(state);
			}
		}

		private static bool IsShown(ShipItemFood food)
		{
			if (!((Object)(object)food == (Object)null) && !((GoPointerButton)food).IsLookedAt())
			{
				return Object.op_Implicit((Object)(object)((PickupableItem)food).held);
			}
			return true;
		}

		private static bool IsOnTurn(FoodState state)
		{
			int num = ((Object)state).GetInstanceID() & 0x7FFFFFFF;
			return (Time.frameCount + num) % 15 == 0;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo operand = AccessTools.Method(typeof(FoodDescriptionPatch), "RefreshIfNeeded", (Type[])null, (Type[])null);
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, UpdateLookTextMethod))
				{
					item.operand = operand;
					num++;
				}
			}
			if (num == 0)
			{
				Plugin.Log.LogWarning((object)"Found no description refresh to route in FoodState.Update.");
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(ItemRigidbody), "MoveItemToWalkColRigidbody")]
	internal static class ItemPlacementPatch
	{
		private static bool Prefix(ItemRigidbody __instance, ShipItem ___item)
		{
			return !ItemPlacement.TryPlace(((Component)__instance).transform, ___item);
		}
	}
	[HarmonyPatch(typeof(MasterPainter), "Awake")]
	internal static class MasterPainterAwakePatch
	{
		private static void Postfix(MasterPainter __instance)
		{
			DirtCameraSwitch.Apply(__instance);
		}
	}
	[HarmonyPatch(typeof(MasterPainter), "DoApplyRenderTex")]
	internal static class MasterPainterFullCleanPatch
	{
		private static void Prefix(MasterPainter __instance)
		{
			DirtCameraSwitch.RenderForClean(__instance);
		}
	}
	[HarmonyPatch(typeof(OutlineEffect), "OnPreRender")]
	internal static class OutlineEffectPatch
	{
		private static readonly FieldRef<LinkedSet<Outline>, LinkedList<Outline>> OutlineList = AccessTools.FieldRefAccess<LinkedSet<Outline>, LinkedList<Outline>>("list");

		private static readonly HashSet<OutlineEffect> clearedWhileIdle = new HashSet<OutlineEffect>();

		public static void Forget()
		{
			clearedWhileIdle.Clear();
		}

		private static bool Prefix(OutlineEffect __instance, LinkedSet<Outline> ___outlines)
		{
			if (!ModConfig.OutlineCamerasOnDemand.Value || ___outlines == null || OutlineList.Invoke(___outlines).Count > 0)
			{
				clearedWhileIdle.Remove(__instance);
				return true;
			}
			return clearedWhileIdle.Add(__instance);
		}
	}
	[HarmonyPatch]
	internal static class RedundantItemWritesPatch
	{
		private static readonly Dictionary<MethodInfo, MethodInfo> GuardBySetter = new Dictionary<MethodInfo, MethodInfo>
		{
			{
				Setter(typeof(Transform), "position"),
				Guard("SetPosition")
			},
			{
				Setter(typeof(Transform), "rotation"),
				Guard("SetRotation")
			},
			{
				Setter(typeof(Transform), "localScale"),
				Guard("SetLocalScale")
			},
			{
				Setter(typeof(Collider), "isTrigger"),
				Guard("SetIsTrigger")
			},
			{
				Setter(typeof(Collider), "enabled"),
				Guard("SetEnabled")
			}
		};

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(ItemRigidbody), "FixedUpdate", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(ItemRigidbody), "LateUpdate", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(ItemRigidbody), "ToggleCollider", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(ItemRigidbody), "SetSubcollidersTriggers", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(ItemRigidbody), "MoveRigidbodyToWalkCol", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(CrateInventory), "LateUpdate", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (item.operand is MethodInfo key && GuardBySetter.TryGetValue(key, out var value))
				{
					item.opcode = OpCodes.Call;
					item.operand = value;
					num++;
				}
			}
			if (num == 0)
			{
				Plugin.Log.LogWarning((object)$"Found no writes to guard in {original.DeclaringType}.{original.Name}.");
			}
			return list;
		}

		private static MethodInfo Setter(Type type, string property)
		{
			return AccessTools.PropertySetter(type, property);
		}

		private static MethodInfo Guard(string method)
		{
			return AccessTools.Method(typeof(RedundantWriteGuard), method, (Type[])null, (Type[])null);
		}
	}
	[HarmonyPatch(typeof(RopeEffect), "LateUpdate")]
	internal static class RopeEffectUpdatePatch
	{
		private static bool Prefix(RopeEffect __instance, LineRenderer ___lineRenderer, ClothRope ___clothRope, ref float ___distanceCheckTimer, ref bool ___outOfRange)
		{
			return !RopeLod.TryUpdate(__instance, ___lineRenderer, ___clothRope, ref ___distanceCheckTimer, ref ___outOfRange);
		}
	}
	[HarmonyPatch(typeof(RopeEffect), "Start")]
	internal static class RopeEffectStartPatch
	{
		private static void Postfix(RopeEffect __instance)
		{
			HiddenRopePauser.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(RopeEffect), "OnDestroy")]
	internal static class RopeEffectDestroyPatch
	{
		private static void Postfix(RopeEffect __instance)
		{
			RopeLod.Forget(__instance);
		}
	}
	[HarmonyPatch]
	internal static class SailShadowRaycastPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(SailShadowCol), "CheckShadow", (Type[])null, (Type[])null));
		}

		private static void Prefix(out bool __state)
		{
			__state = ModConfig.SkipSyncForSailWindShadows.Value && Physics.autoSyncTransforms;
			if (__state)
			{
				Physics.autoSyncTransforms = false;
			}
		}

		private static void Finalizer(bool __state)
		{
			if (__state)
			{
				Physics.autoSyncTransforms = true;
			}
		}
	}
	[HarmonyPatch(typeof(ShopItemSpawner), "SpawnItem")]
	internal static class ShopItemSpawnPatch
	{
		private static void Postfix(ShipItem ___item)
		{
			ShopItemFreezer.Register(___item);
		}
	}
	[HarmonyPatch(typeof(GoPointerButton), "Look")]
	internal static class ShopItemLookPatch
	{
		private static void Postfix(GoPointerButton __instance)
		{
			ShopItemFreezer.Thaw(__instance);
		}
	}
}
namespace PerformanceTweaks.Configuration
{
	public enum DistantRopeSimplification
	{
		Off,
		Quality,
		Default,
		Performance
	}
	internal static class ModConfig
	{
		private const string ItemsSection = "Items";

		private const string BoatsSection = "Boats";

		private const string RenderingSection = "Rendering";

		public static ConfigEntry<bool> SkipRedundantItemUpdates { get; private set; }

		public static ConfigEntry<bool> SkipUnmovedCrateContents { get; private set; }

		public static ConfigEntry<bool> FreezeIdleShopItems { get; private set; }

		public static ConfigEntry<bool> FoodDescriptionsOnDemand { get; private set; }

		public static ConfigEntry<bool> SkipUnchangedRopes { get; private set; }

		public static ConfigEntry<DistantRopeSimplification> SimplifyDistantRopes { get; private set; }

		public static ConfigEntry<RopeDistanceOrigin> RopeDistanceMeasuredFrom { get; private set; }

		public static ConfigEntry<bool> PauseHiddenRopes { get; private set; }

		public static ConfigEntry<bool> SkipSyncForSailWindShadows { get; private set; }

		public static ConfigEntry<bool> DirtCameraOnDemand { get; private set; }

		public static ConfigEntry<bool> OutlineCamerasOnDemand { get; private set; }

		public static void Bind(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			SkipRedundantItemUpdates = config.Bind<bool>("Items", "Skip redundant item updates", true, new ConfigDescription("Skips item and crate updates that would change nothing.", (AcceptableValueBase)null, Array.Empty<object>()));
			SkipUnmovedCrateContents = config.Bind<bool>("Items", "Skip unmoved crate contents", true, new ConfigDescription("Puts the items inside a crate back into place only after the crate has moved or its contents changed.", (AcceptableValueBase)null, Array.Empty<object>()));
			FreezeIdleShopItems = config.Bind<bool>("Items", "Freeze idle shop items", true, new ConfigDescription("Pauses the updates of shop goods while nobody holds or looks at them.", (AcceptableValueBase)null, Array.Empty<object>()));
			FoodDescriptionsOnDemand = config.Bind<bool>("Items", "Food descriptions on demand", true, new ConfigDescription("Updates a food's description only while you look at it or hold it.", (AcceptableValueBase)null, Array.Empty<object>()));
			SkipUnchangedRopes = config.Bind<bool>("Boats", "Skip unchanged ropes", true, new ConfigDescription("Rebuilds a 3D rope only when it has moved on its boat.", (AcceptableValueBase)null, Array.Empty<object>()));
			SimplifyDistantRopes = config.Bind<DistantRopeSimplification>("Boats", "Simplify distant ropes", DistantRopeSimplification.Default, new ConfigDescription("Draws distant ropes as the game's flat ropes instead of 3D ropes.", (AcceptableValueBase)null, Array.Empty<object>()));
			RopeDistanceMeasuredFrom = config.Bind<RopeDistanceOrigin>("Boats", "Rope distance measured from", RopeDistanceOrigin.Camera, new ConfigDescription("Where the distance for Simplify distant ropes is measured from: the camera or your character.", (AcceptableValueBase)null, Array.Empty<object>()));
			PauseHiddenRopes = config.Bind<bool>("Boats", "Pause hidden ropes", true, new ConfigDescription("Updates ropes you can't see only a few times a second.", (AcceptableValueBase)null, Array.Empty<object>()));
			SkipSyncForSailWindShadows = config.Bind<bool>("Boats", "Skip sync for sail wind shadows", true, new ConfigDescription("Checks the wind shadow between sails against positions from the last physics step.", (AcceptableValueBase)null, Array.Empty<object>()));
			DirtCameraOnDemand = config.Bind<bool>("Rendering", "Dirt camera on demand", true, new ConfigDescription("Turns the hidden dirt camera on only when it is really needed.", (AcceptableValueBase)null, Array.Empty<object>()));
			OutlineCamerasOnDemand = config.Bind<bool>("Rendering", "Outline cameras on demand", true, new ConfigDescription("Renders the highlight outline cameras only while something is highlighted.", (AcceptableValueBase)null, Array.Empty<object>()));
		}
	}
	public enum RopeDistanceOrigin
	{
		Camera,
		Player
	}
}