Decompiled source of ContinuousBuild v1.0.0

BepInEx/plugins/ContinuousBuild.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Dedryx")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Dedryx 2026")]
[assembly: AssemblyDescription("Hold the placement button while building to continuously place pieces as you move through Valheim's snap points.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ContinuousBuild")]
[assembly: AssemblyTitle("ContinuousBuild")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ContinuousBuild
{
	internal struct Pose
	{
		internal double X;

		internal double Y;

		internal double Z;

		internal double Qx;

		internal double Qy;

		internal double Qz;

		internal double Qw;

		internal bool Matches(Pose other, double distance, double degrees)
		{
			double num = X - other.X;
			double num2 = Y - other.Y;
			double num3 = Z - other.Z;
			double num4 = Math.Abs(Qx * other.Qx + Qy * other.Qy + Qz * other.Qz + Qw * other.Qw);
			double num5 = Math.Sqrt((Qx * Qx + Qy * Qy + Qz * Qz + Qw * Qw) * (other.Qx * other.Qx + other.Qy * other.Qy + other.Qz * other.Qz + other.Qw * other.Qw));
			if (num * num + num2 * num2 + num3 * num3 <= distance * distance && num5 > 0.0)
			{
				return Math.Min(1.0, num4 / num5) >= Math.Cos(degrees * Math.PI / 360.0);
			}
			return false;
		}
	}
	internal sealed class PlacementGate
	{
		private readonly List<Pose> placed = new List<Pose>();

		private double nextAttempt;

		private Pose lastAim;

		private int lastRotation;

		internal bool Armed => placed.Count > 0;

		internal Pose LastSuccessful { get; private set; }

		internal void Reset()
		{
			placed.Clear();
			nextAttempt = 0.0;
		}

		internal bool IsNew(Pose pose, double positionTolerance, double rotationTolerance)
		{
			foreach (Pose item in placed)
			{
				if (pose.Matches(item, positionTolerance, rotationTolerance))
				{
					return false;
				}
			}
			return true;
		}

		internal bool CanAttempt(Pose pose, double now, double positionTolerance, double rotationTolerance)
		{
			if (Armed && now >= nextAttempt)
			{
				return IsNew(pose, positionTolerance, rotationTolerance);
			}
			return false;
		}

		internal bool AllowsCandidate(Pose pose, bool snapped, Pose aim, int rotation, double positionTolerance, double rotationTolerance)
		{
			if (Armed && snapped && IsNew(pose, positionTolerance, rotationTolerance))
			{
				if (rotation == lastRotation)
				{
					return !aim.Matches(lastAim, 0.01, 0.1);
				}
				return true;
			}
			return false;
		}

		internal void Attempt(double now)
		{
			nextAttempt = now + 0.25;
		}

		internal void Success(Pose pose, double now, Pose aim = default(Pose), int rotation = 0)
		{
			LastSuccessful = pose;
			lastAim = aim;
			lastRotation = rotation;
			placed.Add(pose);
			nextAttempt = now + 0.1;
		}
	}
	[BepInPlugin("dedryx.continuousbuild", "ContinuousBuild", "1.0.0")]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private delegate bool FindNearestSnap(Player player, Vector3 position, List<Transform> targets, float range, out Transform target, out float distance);

		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost", new Type[] { typeof(bool) })]
		private static class GhostPatch
		{
			private static void Prefix(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					refreshing = __instance;
					acceptedSnap = false;
				}
			}

			private static void Postfix(Player __instance)
			{
				if ((Object)(object)refreshing == (Object)(object)__instance)
				{
					refreshing = null;
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				if (__exception != null)
				{
					refreshing = null;
					acceptedSnap = false;
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "FindClosestSnapPoints")]
		private static class SnapRangePatch
		{
			private static void Prefix(Player __instance, ref float maxSnapDistance)
			{
				if (CanAssistSnap(__instance))
				{
					maxSnapDistance = Mathf.Max(maxSnapDistance, heldSnapDistance.Value);
				}
			}

			private static void Postfix(Player __instance, Transform ghost, float maxSnapDistance, List<Piece> pieces, ref Transform a, ref Transform b, ref bool __result)
			{
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				if (!__result || !improveAutoSnapping.Value || !CanAssistSnap(__instance) || manualSnapPoint.Invoke(__instance) >= 0 || (Object)(object)a == (Object)null || (Object)(object)b == (Object)null)
				{
					return;
				}
				Piece piece = ((Component)ghost).GetComponent<Piece>();
				if ((Object)(object)piece == (Object)null)
				{
					return;
				}
				Vector3 origin = ghost.position;
				Quaternion rotation = ghost.rotation;
				inspectingCandidates = true;
				try
				{
					if (!Accept(a, b))
					{
						__result = SnapFallback.Find(sourceSnaps.Invoke(__instance), targetSnaps.Invoke(__instance), maxSnapDistance, Nearest, Accept, out var source, out var target);
						a = source;
						b = target;
					}
				}
				finally
				{
					inspectingCandidates = false;
				}
				bool Accept(Transform from, Transform to)
				{
					//IL_0015: 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_0021: Unknown result type (might be due to invalid IL or missing references)
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0030: 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_003e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0077: Unknown result type (might be due to invalid IL or missing references)
					//IL_0079: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)from == (Object)null || (Object)(object)to == (Object)null)
					{
						return false;
					}
					Vector3 val = to.position - (from.position - origin);
					if (!overlaps(__instance, val, rotation, ((Object)ghost).name, pieces, piece.m_allowRotatedOverlap))
					{
						if (gate.Armed)
						{
							return gate.IsNew(Snapshot(val, rotation), positionTolerance.Value, rotationTolerance.Value);
						}
						return true;
					}
					return false;
				}
				bool Nearest(Transform from, List<Transform> targets, float range, out Transform to, out float distance)
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					return findNearestSnap(__instance, from.position, targets, range, out to, out distance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "IsOverlappingOtherPiece", new Type[]
		{
			typeof(Vector3),
			typeof(Quaternion),
			typeof(string),
			typeof(List<Piece>),
			typeof(bool)
		})]
		private static class SnapPatch
		{
			private static void Postfix(Player __instance, Vector3 p, Quaternion rotation, bool __result)
			{
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				if (!inspectingCandidates && !((Object)(object)refreshing != (Object)(object)__instance) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					acceptedSnap = !__result;
					if (acceptedSnap)
					{
						snappedPose = Snapshot(p, rotation);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[]
		{
			typeof(bool),
			typeof(float)
		})]
		private static class UpdatePatch
		{
			private static void Prefix(Player __instance, bool takeInput)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					updating = __instance;
					automatic = (succeeded = false);
					Sync(__instance, takeInput);
				}
			}

			private static void Postfix(Player __instance, bool takeInput, ref float ___m_placePressedTime)
			{
				if (!((Object)(object)updating != (Object)(object)__instance))
				{
					if (automatic)
					{
						___m_placePressedTime = -9999f;
					}
					if (automatic && !succeeded)
					{
						Log("Automatic placement rejected by vanilla or duplicate guard.");
					}
					Sync(__instance, takeInput);
					updating = null;
					automatic = false;
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				if (__exception != null)
				{
					Reset();
					updating = null;
					automatic = false;
				}
				return __exception;
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_0086: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Expected O, but got Unknown
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				int num = 0;
				for (int i = 1; i < list.Count; i++)
				{
					if (list[i - 1].opcode == OpCodes.Ldstr && object.Equals(list[i - 1].operand, "Attack") && CodeInstructionExtensions.Calls(list[i], AccessTools.Method(typeof(ZInput), "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null)))
					{
						CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
						val.labels.AddRange(list[i].labels);
						list[i].labels.Clear();
						list.Insert(i++, val);
						list[i].opcode = OpCodes.Call;
						list[i].operand = AccessTools.Method(typeof(Plugin), "PlaceInput", (Type[])null, (Type[])null);
						num++;
					}
				}
				if (num != 1)
				{
					throw new InvalidOperationException("Expected one vanilla Attack edge in UpdatePlacement; found " + num);
				}
				return list;
			}
		}

		[HarmonyPatch(typeof(Player), "TryPlacePiece", new Type[] { typeof(Piece) })]
		private static class TryPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
			{
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Expected O, but got Unknown
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				//IL_009f: Expected O, but got Unknown
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Expected O, but got Unknown
				//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Expected O, but got Unknown
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Expected O, but got Unknown
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				int num = 0;
				for (int i = 0; i < list.Count; i++)
				{
					if (CodeInstructionExtensions.Calls(list[i], AccessTools.Method(typeof(Player), "UpdatePlacementGhost", new Type[1] { typeof(bool) }, (Type[])null)))
					{
						Label label = generator.DefineLabel();
						list[i + 1].labels.Add(label);
						list.InsertRange(i + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[5]
						{
							new CodeInstruction(OpCodes.Ldarg_0, (object)null),
							new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Plugin), "AllowRefreshedPlacement", (Type[])null, (Type[])null)),
							new CodeInstruction(OpCodes.Brtrue, (object)label),
							new CodeInstruction(OpCodes.Ldc_I4_0, (object)null),
							new CodeInstruction(OpCodes.Ret, (object)null)
						});
						i += 5;
						num++;
					}
				}
				if (num != 1)
				{
					throw new InvalidOperationException("Expected one ghost refresh in TryPlacePiece; found " + num);
				}
				return list;
			}
		}

		[HarmonyPatch(typeof(Player), "PlacePiece", new Type[]
		{
			typeof(Piece),
			typeof(Vector3),
			typeof(Quaternion),
			typeof(bool),
			typeof(bool)
		})]
		private static class PlacedPatch
		{
			private static void Postfix(Player __instance, Piece piece, Vector3 pos, Quaternion rot)
			{
				//IL_002e: 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)
				if (!((Object)(object)updating != (Object)(object)__instance) && active && !((Object)(object)piece != (Object)(object)selected) && Held())
				{
					gate.Success(Snapshot(pos, rot), Time.time, Aim(__instance), placeRotation.Invoke(__instance));
					succeeded = true;
					Log("Placement succeeded; transform recorded.");
				}
			}
		}

		private const string Id = "dedryx.continuousbuild";

		private const string PluginVersion = "1.0.0";

		private static Plugin instance;

		private static ConfigEntry<bool> featureEnabled;

		private static ConfigEntry<bool> debug;

		private static ConfigEntry<bool> requireModifier;

		private static ConfigEntry<bool> improveAutoSnapping;

		private static ConfigEntry<KeyCode> modifier;

		private static ConfigEntry<float> positionTolerance;

		private static ConfigEntry<float> rotationTolerance;

		private static ConfigEntry<float> heldSnapDistance;

		private static Action<Player, bool> refreshGhost;

		private static Func<Humanoid, ItemData> getRightItem;

		private static Func<Player, bool> takeInput;

		private static FieldRef<Player, GameObject> ghost;

		private static FieldRef<Player, float> lastToolUse;

		private static FieldRef<Player, float> placeDelay;

		private static FieldRef<Player, int> placeRotation;

		private static FieldRef<Player, int> manualSnapPoint;

		private static FieldRef<Player, List<Transform>> sourceSnaps;

		private static FieldRef<Player, List<Transform>> targetSnaps;

		private static FindNearestSnap findNearestSnap;

		private static Func<Player, Vector3, Quaternion, string, List<Piece>, bool, bool> overlaps;

		private static bool inspectingCandidates;

		private static Player refreshing;

		private static bool acceptedSnap;

		private static Pose snappedPose;

		private static readonly PlacementGate gate = new PlacementGate();

		private static Player owner;

		private static Player updating;

		private static Piece selected;

		private static ItemData tool;

		private static bool active;

		private static bool automatic;

		private static bool succeeded;

		private Harmony harmony;

		private void Awake()
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Expected O, but got Unknown
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Expected O, but got Unknown
			instance = this;
			featureEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable continuous placement after a successful normal placement.");
			requireModifier = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireModifier", false, "When false, hold only the configured placement button to build continuously at new snap positions. When true, also hold ModifierKey.");
			modifier = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ModifierKey", (KeyCode)308, "Optional key used only when RequireModifier is true. Choose a key that does not conflict with vanilla AltPlace (LeftShift by default) to retain snapping.");
			debug = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log continuous-build transitions and placement attempts.");
			improveAutoSnapping = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ImproveAutoSnapping", true, "While holding placement in Auto snap mode, try the next nearest snap when vanilla's first choice overlaps an existing piece or repeats a placement from this hold. Named snap modes are unchanged.");
			heldSnapDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HeldSnapDistance", 1f, new ConfigDescription("Snap capture distance in meters while holding placement input in build mode. Vanilla uses 0.5; 1 doubles the capture range. Set 0.5 for vanilla behavior. Does not increase building reach. Larger values may select unintended nearby snap points.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			positionTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "PositionTolerance", 0.05f, new ConfigDescription("Positions within this distance (meters) count as the same location. Lower only for unusually small snap spacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 0.5f), Array.Empty<object>()));
			rotationTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "RotationTolerance", 10f, new ConfigDescription("Rotations within this angle (degrees) at the same position count as duplicates. Vanilla normally rotates in 22.5 degree steps.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>()));
			harmony = new Harmony("dedryx.continuousbuild");
			try
			{
				refreshGhost = AccessTools.MethodDelegate<Action<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "UpdatePlacementGhost", new Type[1] { typeof(bool) }, (Type[])null), (object)null, true);
				getRightItem = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetRightItem", Type.EmptyTypes, (Type[])null), (object)null, true);
				takeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "TakeInput", Type.EmptyTypes, (Type[])null), (object)null, true);
				ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");
				lastToolUse = AccessTools.FieldRefAccess<Player, float>("m_lastToolUseTime");
				placeDelay = AccessTools.FieldRefAccess<Player, float>("m_placeDelay");
				placeRotation = AccessTools.FieldRefAccess<Player, int>("m_placeRotation");
				manualSnapPoint = AccessTools.FieldRefAccess<Player, int>("m_manualSnapPoint");
				sourceSnaps = AccessTools.FieldRefAccess<Player, List<Transform>>("m_tempSnapPoints1");
				targetSnaps = AccessTools.FieldRefAccess<Player, List<Transform>>("m_tempSnapPoints2");
				findNearestSnap = AccessTools.MethodDelegate<FindNearestSnap>(AccessTools.DeclaredMethod(typeof(Player), "FindClosestSnappoint", (Type[])null, (Type[])null), (object)null, true);
				overlaps = AccessTools.MethodDelegate<Func<Player, Vector3, Quaternion, string, List<Piece>, bool, bool>>(AccessTools.DeclaredMethod(typeof(Player), "IsOverlappingOtherPiece", (Type[])null, (Type[])null), (object)null, true);
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"ContinuousBuild 1.0.0 initialized.");
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("ContinuousBuild could not locate/patch the required vanilla placement path; disabled. " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Reset();
			instance = null;
		}

		private static void Log(string message)
		{
			if (debug.Value)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)("[Debug] " + message));
			}
		}

		private static void Reset()
		{
			if (active)
			{
				Log("Continuous-build input stopped.");
			}
			gate.Reset();
			active = false;
			selected = null;
			tool = null;
			owner = null;
			acceptedSnap = false;
		}

		private static bool Held()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (featureEnabled.Value && (!requireModifier.Value || ZInput.GetKey(modifier.Value, true)))
			{
				if (!ZInput.GetButton("Attack"))
				{
					return ZInput.GetButton("JoyPlace");
				}
				return true;
			}
			return false;
		}

		private static bool CanAssistSnap(Player player)
		{
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)refreshing == (Object)(object)player && active && (Object)(object)owner == (Object)(object)player && Held() && takeInput(player) && ((Character)player).InPlaceMode() && (Object)(object)player.GetSelectedPiece() == (Object)(object)selected && !Hud.IsPieceSelectionVisible() && !Hud.InRadial())
			{
				return !ZInput.GetButton("JoyAltKeys");
			}
			return false;
		}

		private static bool Sync(Player player, bool takeInput)
		{
			Piece val = (((Character)player).InPlaceMode() ? player.GetSelectedPiece() : null);
			ItemData val2 = getRightItem((Humanoid)(object)player);
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !takeInput || !Held() || ((Character)player).IsDead() || Hud.IsPieceSelectionVisible() || Hud.InRadial() || ZInput.GetButton("JoyAltKeys") || (Object)(object)val == (Object)null || val.m_repairPiece || val.m_removePiece || val2 == null || (Object)(object)ghost.Invoke(player) == (Object)null)
			{
				Reset();
				return false;
			}
			if ((Object)(object)owner != (Object)(object)player || (Object)(object)selected != (Object)(object)val || tool != val2)
			{
				Reset();
			}
			if (!active)
			{
				Log("Continuous-build input started; waiting for a normal successful placement.");
			}
			owner = player;
			selected = val;
			tool = val2;
			active = true;
			return true;
		}

		private static Pose Snapshot(Vector3 p, Quaternion r)
		{
			//IL_000a: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			return new Pose
			{
				X = p.x,
				Y = p.y,
				Z = p.z,
				Qx = r.x,
				Qy = r.y,
				Qz = r.z,
				Qw = r.w
			};
		}

		private static Pose GhostPose(Player player)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			return Snapshot(ghost.Invoke(player).transform.position, ghost.Invoke(player).transform.rotation);
		}

		private static Pose Aim(Player player)
		{
			//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_0011: Unknown result type (might be due to invalid IL or missing references)
			return Snapshot(((Component)player).transform.position, Quaternion.LookRotation(((Character)player).GetLookDir()));
		}

		private static bool CandidateAllowed(Player player)
		{
			Pose pose = GhostPose(player);
			bool snapped = acceptedSnap && pose.Matches(snappedPose, 0.001, 0.1);
			return gate.AllowsCandidate(pose, snapped, Aim(player), placeRotation.Invoke(player), positionTolerance.Value, rotationTolerance.Value);
		}

		private static bool PlaceInput(string name, Player player)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			bool buttonDown = ZInput.GetButtonDown(name);
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return buttonDown;
			}
			if (!Sync(player, takeInput: true) || buttonDown || ZInput.GetButtonDown("JoyPlace"))
			{
				return buttonDown;
			}
			if (!gate.Armed || Time.time - lastToolUse.Invoke(player) <= placeDelay.Invoke(player))
			{
				return false;
			}
			refreshGhost(player, arg2: false);
			if ((Object)(object)ghost.Invoke(player) == (Object)null || !ghost.Invoke(player).activeInHierarchy || (int)player.GetPlacementStatus() != 0 || !CandidateAllowed(player) || !gate.CanAttempt(GhostPose(player), Time.time, positionTolerance.Value, rotationTolerance.Value))
			{
				return false;
			}
			gate.Attempt(Time.time);
			automatic = true;
			Log("New confirmed snap after aim/movement/rotation change: automatic placement attempted.");
			return true;
		}

		private static bool AllowRefreshedPlacement(Player player)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!automatic || (Object)(object)updating != (Object)(object)player)
			{
				return true;
			}
			if (Sync(player, takeInput: true) && gate.Armed && ghost.Invoke(player).activeInHierarchy && (int)player.GetPlacementStatus() == 0)
			{
				return CandidateAllowed(player);
			}
			return false;
		}
	}
	internal static class SnapFallback
	{
		internal delegate bool Closest<T>(T source, List<T> targets, float range, out T target, out float distance);

		internal static bool Find<T>(IList<T> sources, List<T> targets, float range, Closest<T> closest, Func<T, T, bool> acceptable, out T source, out T target)
		{
			source = default(T);
			target = default(T);
			bool result = false;
			float num = float.PositiveInfinity;
			int num2 = 0;
			List<T> list = new List<T>(targets.Count);
			foreach (T source2 in sources)
			{
				list.Clear();
				list.AddRange(targets);
				T target2;
				float distance;
				while (closest(source2, list, Math.Min(range, num), out target2, out distance))
				{
					if (++num2 > 256)
					{
						source = default(T);
						target = default(T);
						return false;
					}
					if (acceptable(source2, target2))
					{
						if (distance < num)
						{
							result = true;
							num = distance;
							source = source2;
							target = target2;
						}
						break;
					}
					if (!list.Remove(target2))
					{
						break;
					}
				}
			}
			return result;
		}
	}
}