Decompiled source of ButtCactus v1.1.0

plugins/ButtCactus.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ButtCactus.PanelKit;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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 ButtCactus
{
	internal static class BodypartFinder
	{
		public static bool TryParse(string typeName, out BodypartType type)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected I4, but got Unknown
			type = (BodypartType)0;
			if (string.IsNullOrWhiteSpace(typeName))
			{
				return false;
			}
			typeName = typeName.Trim();
			if (Enum.TryParse<BodypartType>(typeName, ignoreCase: true, out BodypartType result))
			{
				type = (BodypartType)(int)result;
				return true;
			}
			return false;
		}

		public static Bodypart Find(Character character, string typeName)
		{
			//IL_0083: 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)
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.ragdoll == (Object)null)
			{
				return null;
			}
			if (!TryParse(typeName, out var type))
			{
				ModLog.Warn("无法识别的身体部位名称:「" + typeName + "」。可选部位:" + Describe(character));
				return null;
			}
			List<Bodypart> partList = character.refs.ragdoll.partList;
			if (partList == null)
			{
				ModLog.Warn("角色 partList 为空,骨架尚未就绪。");
				return null;
			}
			for (int i = 0; i < partList.Count; i++)
			{
				Bodypart val = partList[i];
				if ((Object)(object)val != (Object)null && val.partType == type)
				{
					return val;
				}
			}
			ModLog.Warn("角色骨架上没有部位「" + typeName + "」。可选部位:" + Describe(character));
			return null;
		}

		public static bool TryFind(Character character, BodypartType type, out Bodypart part)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			part = null;
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.ragdoll == (Object)null)
			{
				return false;
			}
			List<Bodypart> partList = character.refs.ragdoll.partList;
			if (partList == null)
			{
				return false;
			}
			for (int i = 0; i < partList.Count; i++)
			{
				Bodypart val = partList[i];
				if ((Object)(object)val != (Object)null && val.partType == type)
				{
					part = val;
					return true;
				}
			}
			return false;
		}

		public static string Describe(Character character)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.ragdoll == (Object)null)
			{
				return "(角色骨架未就绪)";
			}
			List<Bodypart> partList = character.refs.ragdoll.partList;
			if (partList == null)
			{
				return "(partList 为空)";
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Bodypart item in partList)
			{
				if ((Object)(object)item != (Object)null)
				{
					stringBuilder.Append(item.partType).Append(", ");
				}
			}
			return stringBuilder.ToString();
		}

		public static void DumpAll(Character character)
		{
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.ragdoll == (Object)null)
			{
				ModLog.Warn("角色骨架尚未就绪,无法打印身体部位列表。");
			}
			else
			{
				ModLog.Info($"角色身体部位共 {character.refs.ragdoll.partList?.Count ?? 0} 个:{Describe(character)}");
			}
		}
	}
	internal sealed class ButtCactusPanel : ModPanelBase
	{
		protected override int WindowId => 61720;

		protected override string Title => L.T("屁股仙人球 v", "Butt Cactus v") + "1.1.0";

		protected override string TitleNote => L.T("真实粘附 / 屁股仙人球", "real stick / butt cactus");

		protected override string[] TabLabels => new string[4]
		{
			L.T("真实粘附", "Real Stick"),
			L.T("屁股仙人球", "Butt Cactus"),
			L.T("效果", "Effects"),
			L.T("设置", "Settings")
		};

		protected override float LoadScale()
		{
			if (ModConfig.PanelScale == null)
			{
				return 1f;
			}
			return ModConfig.PanelScale.Value;
		}

		protected override void SaveScale(float value)
		{
			if (ModConfig.PanelScale != null)
			{
				ModConfig.PanelScale.Value = value;
			}
		}

		protected override string Loc(string chinese, string english)
		{
			return L.T(chinese, english);
		}

		protected override void DrawTab(int index)
		{
			switch (index)
			{
			case 0:
				DrawRealTab();
				break;
			case 1:
				DrawButtTab();
				break;
			case 2:
				DrawEffectTab();
				break;
			default:
				DrawSettingsTab();
				break;
			}
		}

		private void DrawRealTab()
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			PanelWidgets.BeginCard(L.T("模式一:真实粘附(上腿)", "Mode 1: Real stick (legs)"));
			ModConfig.TestLegAttach.Value = PanelWidgets.ToggleRow(ModConfig.TestLegAttach.Value, L.T("启用真实粘附", "Enable real stick"), L.T("把两个原版仙人球直接粘在左右上腿骨上,不做物理模拟,靠腿部摆动产生晃动。", "Attach two vanilla cactus balls directly to the upper leg bones; wobble comes from leg motion."));
			PanelWidgets.EndCard();
			PanelWidgets.BeginCard(L.T("粘附部位", "Body parts"));
			PartRow(L.T("左上腿部位", "Left part"), ModConfig.TestLegLeftPart);
			PartRow(L.T("右上腿部位", "Right part"), ModConfig.TestLegRightPart);
			PanelWidgets.Hint(L.T("部位名填错时会写进日志,可在「设置」页开启打印全部部位。", "A wrong part name is logged; enable the body-part dump on the Settings tab."));
			PanelWidgets.EndCard();
			OffsetCard(L.T("左侧位置偏移", "Left offset"), "realL", PlacementDefaults.LegLeft, ModConfig.TestLeftOffsetRight, ModConfig.TestLeftOffsetUp, ModConfig.TestLeftOffsetForward);
			OffsetCard(L.T("右侧位置偏移", "Right offset"), "realR", PlacementDefaults.LegRight, ModConfig.TestRightOffsetRight, ModConfig.TestRightOffsetUp, ModConfig.TestRightOffsetForward);
		}

		private void DrawButtTab()
		{
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			PanelWidgets.BeginCard(L.T("模式二:屁股仙人球(骨盆锁定)", "Mode 2: Butt cactus (hip lock)"));
			ModConfig.Enabled.Value = PanelWidgets.ToggleRow(ModConfig.Enabled.Value, L.T("启用屁股仙人球", "Enable butt cactus"), L.T("每帧按当前朝向把球锁在骨盆两侧,并可叠加惯性晃动。", "Locks the balls to the hip each frame and can add inertia wobble."));
			PanelWidgets.EndCard();
			PanelWidgets.BeginCard(L.T("自动定位", "Auto placement"));
			ModConfig.AnchorToLegs.Value = PanelWidgets.ToggleRow(ModConfig.AnchorToLegs.Value, L.T("以双腿根部为基准高度", "Anchor height to leg roots"));
			NumRow(L.T("上下偏移(米)", "Up (m)"), ModConfig.VerticalOffset, -1f, 1f, "buttV");
			NumRow(L.T("左右间距(米)", "Spacing (m)"), ModConfig.LateralSpacing, 0f, 2f, "buttLat");
			NumRow(L.T("贴背距离(米)", "Back (m)"), ModConfig.BackwardOffset, 0f, 2f, "buttBack");
			NumRow(L.T("半径修正", "Radius x"), ModConfig.RadiusScale, 0.1f, 3f, "buttRad");
			PartRow(L.T("基准部位", "Base part"), ModConfig.BasePart);
			PanelWidgets.Hint(L.T("间距/贴背填 0 表示按球半径自动,使两球刚好相切。", "0 means auto by ball radius so the two balls just touch."));
			PanelWidgets.EndCard();
			OffsetCard(L.T("左侧位置偏移", "Left offset"), "buttL", PlacementDefaults.ButtLeft, ModConfig.LeftOffsetRight, ModConfig.LeftOffsetUp, ModConfig.LeftOffsetForward);
			OffsetCard(L.T("右侧位置偏移", "Right offset"), "buttR", PlacementDefaults.ButtRight, ModConfig.RightOffsetRight, ModConfig.RightOffsetUp, ModConfig.RightOffsetForward);
			PanelWidgets.BeginCard(L.T("外观与晃动", "Look & wobble"));
			NumRow(L.T("缩放", "Scale"), ModConfig.Scale, 0.1f, 5f, "buttScale");
			NumRow(L.T("旋转X", "Rot X"), ModConfig.RotX, -180f, 180f, "buttRx");
			NumRow(L.T("旋转Y", "Rot Y"), ModConfig.RotY, -180f, 180f, "buttRy");
			NumRow(L.T("旋转Z", "Rot Z"), ModConfig.RotZ, -180f, 180f, "buttRz");
			NumRow(L.T("晃动强度", "Wobble"), ModConfig.WobbleStrength, 0f, 3f, "wobble");
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (PanelWidgets.Btn(L.T("立即重建", "Rebuild now"), 120f, PanelTheme.ButtonPrimary))
			{
				ButtCactusService.RequestRebuild();
				Notify(L.T("已请求重建。", "Rebuild requested."));
			}
			GUILayout.EndHorizontal();
			PanelWidgets.Hint(L.T("晃动强度 0 为不晃动,越大越明显;修改参数后若没立刻生效,可点「立即重建」。", "Wobble 0 disables it; if a change does not apply immediately, press Rebuild now."));
			PanelWidgets.EndCard();
		}

		private void DrawEffectTab()
		{
			PanelWidgets.BeginCard(L.T("效果", "Effects"));
			ModConfig.ImmuneToCactus.Value = PanelWidgets.ToggleRow(ModConfig.ImmuneToCactus.Value, L.T("免疫仙人球效果(仅本机)", "Immune to cactus (local)"), L.T("任何仙人球粘到身上都不掉血、不扎刺、不影响体力条;他人的球仍正常显示以保持视角一致。", "No damage, thorns or stamina drain from any cactus; others' balls still show for a consistent view."));
			ModConfig.DisableInteraction.Value = PanelWidgets.ToggleRow(ModConfig.DisableInteraction.Value, L.T("禁用交互", "Disable interaction"), L.T("仙人球不会被自己或他人捡起。", "Cactus balls cannot be picked up."));
			ModConfig.NotifyWhenTaken.Value = PanelWidgets.ToggleRow(ModConfig.NotifyWhenTaken.Value, L.T("被取走时屏幕提示", "Taken notification"), L.T("别人取走你身上的仙人球时在屏幕上方提示。", "Show a notice when someone takes a ball off you."));
			ModConfig.AddWeight.Value = PanelWidgets.ToggleRow(ModConfig.AddWeight.Value, L.T("增加负重", "Add weight"), L.T("给角色增加负重。开启「免疫」时在本机无效。", "Adds weight; ignored locally when immunity is on."));
			ModConfig.AddThorns.Value = PanelWidgets.ToggleRow(ModConfig.AddThorns.Value, L.T("造成扎刺伤害", "Thorns damage"), L.T("持续扎刺伤害。开启「免疫」时在本机无效。", "Thorns damage; ignored locally when immunity is on."));
			PanelWidgets.EndCard();
		}

		private void DrawSettingsTab()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			PanelWidgets.BeginCard(L.T("面板", "Panel"));
			KeyBindRow(L.T("面板快捷键", "Panel key"), ModConfig.HotkeyPanel.Value, delegate(KeyCode k)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				ModConfig.HotkeyPanel.Value = k;
			}, ModConfig.HotkeyPanel);
			PanelWidgets.EndCard();
			PanelWidgets.BeginCard(L.T("高级", "Advanced"));
			PartRow(L.T("仙人球预制体名", "Prefab name"), ModConfig.PrefabName);
			ModConfig.DebugDumpBodyparts.Value = PanelWidgets.ToggleRow(ModConfig.DebugDumpBodyparts.Value, L.T("打印所有身体部位", "Dump body parts"));
			NumRow(L.T("远程同步间隔(秒)", "Remote sync (s)"), ModConfig.RemoteSyncInterval, 0f, 2f, "sync");
			PanelWidgets.EndCard();
			PanelWidgets.BeginCard(L.T("日志", "Log"));
			ModConfig.VerboseLog.Value = PanelWidgets.ToggleRow(ModConfig.VerboseLog.Value, L.T("详细日志", "Verbose log"));
			NumRow(L.T("快照间隔(秒)", "Snapshot (s)"), ModConfig.SnapshotInterval, 0f, 60f, "snap");
			PanelWidgets.EndCard();
			PanelWidgets.BeginCard(L.T("保存", "Save"));
			if (PanelWidgets.BtnWide(L.T("保存到配置文件", "Save to config file"), PanelTheme.ButtonPrimary))
			{
				SaveAll();
			}
			PanelWidgets.Hint(L.T("数字框按回车或点击别处生效,再点保存写入文件。", "Press Enter (or click away) to apply a number, then save to write it to disk."));
			PanelWidgets.EndCard();
		}

		protected override void DrawStatusStrip()
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			StateLabel(L.T("真实粘附:", "Real: "), ModConfig.TestLegAttach.Value);
			StateLabel(L.T("屁股仙人球:", "Butt: "), ModConfig.Enabled.Value);
			GUILayout.Label(L.T("语言:", "Lang: ") + (L.English ? "EN" : "中文"), PanelTheme.Hint, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
		}

		protected override void DrawFooterHint()
		{
			//IL_0013: 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)
			string text = ((ModConfig.HotkeyPanel != null) ? ((object)ModConfig.HotkeyPanel.Value/*cast due to .constrained prefix*/).ToString() : "F8");
			GUILayout.Label(L.T("按 " + text + " 开关本面板。", text + " toggles this panel."), PanelTheme.Hint, Array.Empty<GUILayoutOption>());
		}

		protected override void DrawFooterExtras()
		{
			GUILayout.Space(8f);
			if (GUILayout.Button(L.T("English", "中文"), PanelTheme.Mini, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(58f),
				GUILayout.Height(18f)
			}))
			{
				L.Toggle();
				Notify(L.T("已切换为中文。", "Switched to English."));
			}
			if (GUILayout.Button(L.T("保存", "Save"), PanelTheme.Mini, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(44f),
				GUILayout.Height(18f)
			}))
			{
				SaveAll();
			}
		}

		protected override void DrawNavFooter()
		{
			GUILayout.Label(L.T("字体:", "Font: ") + PanelTheme.FontName, PanelTheme.Hint, Array.Empty<GUILayoutOption>());
		}

		private void SaveAll()
		{
			ModConfig.Save();
			Notify(L.T("已保存到配置文件。", "Saved to config file."));
		}

		private void StateLabel(string label, bool on)
		{
			GUILayout.Label(label + L.OnOff(on), on ? PanelTheme.Value : PanelTheme.Hint, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
		}

		private void PartRow(string label, ConfigEntry<string> entry)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(label, PanelTheme.Sub, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(118f) });
			entry.Value = PanelWidgets.TextBox(entry.Value, 132f);
			GUILayout.EndHorizontal();
		}

		private static void NumRow(string label, ConfigEntry<float> entry, float min, float max, string key)
		{
			PanelWidgets.NumberFieldRow(label, entry.Value, min, max, key, delegate(float v)
			{
				entry.Value = v;
			});
		}

		private void OffsetCard(string title, string keyPrefix, Vector3 origin, ConfigEntry<float> right, ConfigEntry<float> up, ConfigEntry<float> forward)
		{
			//IL_0017: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			PanelWidgets.BeginCard(title);
			OffsetRow(L.T("左右(米)", "Side (m)"), right, origin.x, keyPrefix + "R");
			OffsetRow(L.T("上下(米)", "Up (m)"), up, origin.y, keyPrefix + "U");
			OffsetRow(L.T("前后(米)", "Forward (m)"), forward, origin.z, keyPrefix + "F");
			PanelWidgets.Hint(L.T("0 为你当前配置的位置(即坐标原点),输入 ± 值在此基础上移动。", "0 is your configured position (the origin); enter +/- to move from there."));
			PanelWidgets.EndCard();
		}

		private static void OffsetRow(string label, ConfigEntry<float> entry, float origin, string key)
		{
			float value = entry.Value - origin;
			PanelWidgets.NumberFieldRow(label, value, -1f - origin, 1f - origin, key, delegate(float v)
			{
				entry.Value = Mathf.Clamp(origin + v, -1f, 1f);
			});
		}
	}
	internal class ButtCactusService : MonoBehaviour
	{
		private class Slot
		{
			public bool IsLeft;

			public GameObject Go;

			public StickyItemComponent Sticky;

			public Bodypart Part;

			public bool Attaching;

			public int LastViewId = -1;

			public string LastPartName = "无";

			public bool PreviouslyStuck;

			public float Lateral;

			public float Backward;

			public float Vertical;

			public Vector3 LastSentOffset;

			public float NextRemoteSync;

			public bool WobbleInit;

			public Vector3 PrevBase;

			public Vector3 PrevBaseVel;

			public Vector3 WobbleOffset;

			public Vector3 WobbleVel;
		}

		private const float WobbleStiffness = 180f;

		private const float WobbleDamping = 9f;

		private const float WobbleMaxOffset = 0.12f;

		private const float WobbleMaxAccel = 80f;

		private readonly Slot[] _slots = new Slot[2]
		{
			new Slot
			{
				IsLeft = true
			},
			new Slot
			{
				IsLeft = false
			}
		};

		private static float _ballRadius = -1f;

		private bool _running;

		private float _nextAttemptTime;

		private float _nextSnapshotTime;

		private bool _dumped;

		private bool _hadCharacter;

		private bool _rebuildRequested;

		public static ButtCactusService Instance { get; private set; }

		private IEnumerator AttachRoutine(Slot slot, Character me)
		{
			string side = (slot.IsLeft ? "左" : "右");
			slot.Attaching = true;
			try
			{
				Bodypart part = FindBase(me);
				if ((Object)(object)part == (Object)null)
				{
					ModLog.Warn("[" + side + "臀] 找不到基准部位「" + ModConfig.BasePart.Value + "」。");
					yield break;
				}
				string text = CactusPrefabFinder.Resolve();
				if (string.IsNullOrEmpty(text))
				{
					ModLog.Error("[" + side + "臀] 找不到仙人球预制体。");
					yield break;
				}
				Quaternion spawnRot = part.transform.rotation * Quaternion.Euler(ModConfig.RotX.Value, ModConfig.RotY.Value, ModConfig.RotZ.Value);
				GameObject go;
				try
				{
					go = PhotonNetwork.Instantiate(text, part.transform.position, spawnRot, (byte)0, (object[])null);
				}
				catch (Exception ex)
				{
					ModLog.Error("[" + side + "臀] 生成仙人球失败:" + ex.Message);
					yield break;
				}
				if ((Object)(object)go == (Object)null)
				{
					ModLog.Error("[" + side + "臀] 生成仙人球返回空对象。");
					yield break;
				}
				CactusBall cactus = go.GetComponent<CactusBall>();
				if ((Object)(object)cactus == (Object)null)
				{
					ModLog.Warn("[" + side + "臀] 预制体上没有 CactusBall 组件(" + text + ")。");
					SafeDestroy(go);
					yield break;
				}
				if (ModConfig.Scale.Value != 1f)
				{
					Transform transform = go.transform;
					transform.localScale *= ModConfig.Scale.Value;
				}
				((StickyItemComponent)cactus).addWeightToStuckPlayer = (ModConfig.AddWeight.Value ? 1 : 0);
				((StickyItemComponent)cactus).addThornsToStuckPlayer = (ModConfig.AddThorns.Value ? 1 : 0);
				if ((Object)(object)((ItemComponent)cactus).item != (Object)null)
				{
					((ItemComponent)cactus).item.SetKinematic(true);
				}
				if ((Object)(object)((StickyItemComponent)cactus).physicalCollider != (Object)null)
				{
					((StickyItemComponent)cactus).physicalCollider.isTrigger = true;
				}
				slot.Go = go;
				slot.Sticky = (StickyItemComponent)(object)cactus;
				PhotonView component = go.GetComponent<PhotonView>();
				slot.LastViewId = (((Object)(object)component != (Object)null) ? component.ViewID : (-1));
				CactusTracker cactusTracker = go.AddComponent<CactusTracker>();
				cactusTracker.IsLeft = slot.IsLeft;
				cactusTracker.ViewId = slot.LastViewId;
				cactusTracker.Sticky = (StickyItemComponent)(object)cactus;
				yield return null;
				yield return null;
				if ((Object)(object)go == (Object)null || (Object)(object)cactus == (Object)null)
				{
					slot.Go = null;
					slot.Sticky = null;
					yield break;
				}
				if ((Object)(object)((ItemComponent)cactus).item != (Object)null && (int)((ItemComponent)cactus).item.itemState != 0)
				{
					((ItemComponent)cactus).item.itemState = (ItemState)0;
				}
				string detail;
				float num = PlacementMath.MeasureRadius(go, out detail);
				if (_ballRadius <= 0f && num > 0f)
				{
					_ballRadius = num;
				}
				float num2 = ((_ballRadius > 0f) ? _ballRadius : num) * ModConfig.RadiusScale.Value;
				float num3 = ((ModConfig.LateralSpacing.Value > 0f) ? ModConfig.LateralSpacing.Value : num2);
				float num4 = ((ModConfig.BackwardOffset.Value > 0f) ? ModConfig.BackwardOffset.Value : num2);
				float value = ModConfig.VerticalOffset.Value;
				Vector3 val = PlacementMath.TryGetButtBase(me, ModConfig.AnchorToLegs.Value, out detail);
				Vector3 offset = PlacementMath.ComputeWorldOffset(me, slot.IsLeft, num3, num4, value);
				offset = PlacementMath.ApplyDelta(me, offset, GetDelta(slot.IsLeft));
				Vector3 val2 = val + offset;
				Vector3 worldOffset = val2 - part.transform.position;
				slot.Part = part;
				slot.LastPartName = ((object)Unsafe.As<BodypartType, BodypartType>(ref part.partType)/*cast due to .constrained prefix*/).ToString();
				slot.Lateral = num3;
				slot.Backward = num4;
				slot.Vertical = value;
				slot.WobbleInit = false;
				go.transform.SetPositionAndRotation(val2, spawnRot);
				if (StickyApi.Stick((StickyItemComponent)(object)cactus, me, part, worldOffset))
				{
					EnforceNoInteraction(slot);
					ModLog.Info($"[{side}臀] 仙人球已粘到 {part.partType}。");
					yield break;
				}
				ModLog.Warn($"[{side}臀] 粘附 {part.partType} 失败,稍后重试。");
				SafeDestroy(go);
				slot.Go = null;
				slot.Sticky = null;
			}
			finally
			{
				slot.Attaching = false;
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		public static void RequestRebuild()
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Instance._rebuildRequested = true;
			}
		}

		public string GetSlotStatus(bool isLeft)
		{
			Slot slot = (isLeft ? _slots[0] : _slots[1]);
			if (slot.Attaching)
			{
				return "附着中";
			}
			if ((Object)(object)slot.Go == (Object)null)
			{
				return "未生成";
			}
			if ((Object)(object)slot.Sticky != (Object)null && (Object)(object)slot.Sticky.stuckToCharacter == (Object)(object)Character.localCharacter)
			{
				return "已粘附";
			}
			return "未粘附";
		}

		private void Update()
		{
			if (!ModConfig.Enabled.Value)
			{
				if (_running)
				{
					_running = false;
					DetachAll();
				}
				return;
			}
			_running = true;
			if (_rebuildRequested)
			{
				_rebuildRequested = false;
				ModLog.Info("收到重建请求,重新生成仙人球。");
				DetachAll();
				_nextAttemptTime = 0f;
			}
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || !PhotonNetwork.InRoom)
			{
				if (_hadCharacter)
				{
					_hadCharacter = false;
				}
				Slot[] slots = _slots;
				for (int i = 0; i < slots.Length; i++)
				{
					slots[i].PreviouslyStuck = false;
				}
				_dumped = false;
				return;
			}
			if (!_hadCharacter)
			{
				_hadCharacter = true;
				ModLog.Info("开始维持屁股仙人球:角色「" + ((Object)localCharacter).name + "」。");
			}
			if (ModConfig.DebugDumpBodyparts.Value && !_dumped)
			{
				BodypartFinder.DumpAll(localCharacter);
				_dumped = true;
			}
			MaintainSlots(localCharacter);
			LockPlacement(localCharacter);
			float value = ModConfig.SnapshotInterval.Value;
			if (value > 0f && Time.time >= _nextSnapshotTime)
			{
				_nextSnapshotTime = Time.time + value;
				LogSnapshot(localCharacter);
			}
		}

		private void MaintainSlots(Character me)
		{
			bool flag = true;
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if (slot.Attaching)
				{
					flag = false;
					continue;
				}
				if ((Object)(object)slot.Go != (Object)null && (Object)(object)slot.Sticky != (Object)null && (Object)(object)slot.Sticky.stuckToCharacter == (Object)(object)me)
				{
					slot.PreviouslyStuck = true;
					continue;
				}
				if (slot.PreviouslyStuck && (Object)(object)slot.Go == (Object)null && ModConfig.NotifyWhenTaken.Value)
				{
					CactusNotify.Show("你身上的" + (slot.IsLeft ? "左" : "右") + "臀仙人球被别人取走了!");
				}
				slot.PreviouslyStuck = false;
				if ((Object)(object)slot.Go != (Object)null || slot.LastViewId != -1)
				{
					ModLog.Info(string.Format("{0}臀仙人球失效,重新生成(ViewID={1},部位={2})。", slot.IsLeft ? "左" : "右", slot.LastViewId, slot.LastPartName));
				}
				SafeDestroy(slot.Go);
				slot.Go = null;
				slot.Sticky = null;
				slot.Part = null;
				slot.LastViewId = -1;
				flag = false;
			}
			if (flag || Time.time < _nextAttemptTime)
			{
				return;
			}
			_nextAttemptTime = Time.time + 1f;
			slots = _slots;
			foreach (Slot slot2 in slots)
			{
				if (!slot2.Attaching && (Object)(object)slot2.Go == (Object)null)
				{
					((MonoBehaviour)this).StartCoroutine(AttachRoutine(slot2, me));
				}
			}
		}

		private void DetachAll()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if ((Object)(object)slot.Go != (Object)null)
				{
					StickyApi.Unstick(slot.Sticky);
					SafeDestroy(slot.Go);
				}
				slot.Go = null;
				slot.Sticky = null;
				slot.Part = null;
				slot.NextRemoteSync = 0f;
				slot.LastSentOffset = Vector3.zero;
				slot.WobbleInit = false;
				slot.WobbleOffset = Vector3.zero;
				slot.WobbleVel = Vector3.zero;
				slot.PreviouslyStuck = false;
			}
		}

		private static void SafeDestroy(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			try
			{
				if (PhotonNetwork.InRoom)
				{
					PhotonNetwork.Destroy(go);
				}
				else
				{
					Object.Destroy((Object)(object)go);
				}
			}
			catch (Exception ex)
			{
				ModLog.Warn("销毁仙人球失败,改为本地销毁:" + ex.Message);
				Object.Destroy((Object)(object)go);
			}
		}

		private void OnDestroy()
		{
			DetachAll();
		}

		private static Bodypart FindBase(Character me)
		{
			Bodypart val = BodypartFinder.Find(me, ModConfig.BasePart.Value);
			if ((Object)(object)val == (Object)null && (Object)(object)me != (Object)null && me.refs != null && (Object)(object)me.refs.hip != (Object)null)
			{
				ModLog.Warn("基准部位「" + ModConfig.BasePart.Value + "」不可用,回退到 Hip。");
				val = me.refs.hip;
			}
			return val;
		}

		private static Vector3 GetDelta(bool isLeft)
		{
			//IL_0045: 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)
			if (!isLeft)
			{
				return new Vector3(ModConfig.RightOffsetRight.Value, ModConfig.RightOffsetUp.Value, ModConfig.RightOffsetForward.Value);
			}
			return new Vector3(ModConfig.LeftOffsetRight.Value, ModConfig.LeftOffsetUp.Value, ModConfig.LeftOffsetForward.Value);
		}

		private void LogSnapshot(Character me)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("诊断快照:");
			stringBuilder.Append("在房间=").Append(PhotonNetwork.InRoom);
			stringBuilder.Append(",房主=").Append(PhotonNetwork.IsMasterClient);
			stringBuilder.Append(",角色=").Append(((Object)(object)me == (Object)null) ? "无" : ((Object)me).name);
			stringBuilder.Append(",基准部位=").Append(ModConfig.BasePart.Value);
			stringBuilder.Append(",粘附列表总数=").Append(StickyItemComponent.ALL_STUCK_ITEMS?.Count ?? 0);
			if ((Object)(object)me != (Object)null)
			{
				PlacementMath.TryGetFrame(me, out var forward, out var right, out var _);
				stringBuilder.Append(",当前前方=").Append(((Vector3)(ref forward)).ToString("F2"));
				stringBuilder.Append(",当前右方=").Append(((Vector3)(ref right)).ToString("F2"));
			}
			ModLog.Info(stringBuilder.ToString());
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				ModLog.Info(DescribeSlot(slot, me));
			}
		}

		private string DescribeSlot(Slot slot, Character me)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			string text = (slot.IsLeft ? "左臀" : "右臀");
			Bodypart val = FindBase(me);
			string text2 = (((Object)(object)slot.Go == (Object)null) ? "无" : ((Object)slot.Go).name);
			string text3 = "无";
			if ((Object)(object)slot.Go != (Object)null)
			{
				PhotonView component = slot.Go.GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null)
				{
					text3 = component.ViewID.ToString();
				}
			}
			string text4 = (((Object)(object)slot.Sticky == (Object)null || (Object)(object)slot.Sticky.stuckToCharacter == (Object)null) ? "无" : ((Object)slot.Sticky.stuckToCharacter).name);
			bool flag = (Object)(object)slot.Sticky != (Object)null && (Object)(object)slot.Sticky.stuckToCharacter == (Object)(object)me;
			string text5 = (((Object)(object)slot.Sticky == (Object)null) ? "无" : ((object)Unsafe.As<BodypartType, BodypartType>(ref slot.Sticky.stuckToBodypart)/*cast due to .constrained prefix*/).ToString());
			string text6 = (((Object)(object)slot.Sticky != (Object)null && (Object)(object)((ItemComponent)slot.Sticky).item != (Object)null) ? ((object)((ItemComponent)slot.Sticky).item.itemState/*cast due to .constrained prefix*/).ToString() : "无");
			string text7 = (((Object)(object)val == (Object)null) ? "无" : ((object)val.transform.position/*cast due to .constrained prefix*/).ToString());
			string text8 = "无";
			string text9 = "无";
			if ((Object)(object)slot.Go != (Object)null)
			{
				text8 = ((object)slot.Go.transform.position/*cast due to .constrained prefix*/).ToString();
				if ((Object)(object)val != (Object)null)
				{
					text9 = Vector3.Distance(slot.Go.transform.position, val.transform.position).ToString("F3") + "m";
				}
			}
			string text10 = "无";
			if ((Object)(object)me != (Object)null)
			{
				text10 = ((object)PlacementMath.TryGetButtBase(me, ModConfig.AnchorToLegs.Value, out var _)/*cast due to .constrained prefix*/).ToString();
			}
			return "  [" + text + "] 部位=" + (((object)Unsafe.As<BodypartType, BodypartType>(ref val?.partType)/*cast due to .constrained prefix*/).ToString() ?? "无") + " 部位位置=" + text7 + " 屁股基准=" + text10 + " " + $"仙人球={text2} ViewID={text3} 已粘角色={text4} 已粘到本人={flag} " + "粘附部位=" + text5 + " 物品状态=" + text6 + " 仙人球位置=" + text8 + " 距部位=" + text9;
		}

		private void LockPlacement(Character me)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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)
			//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)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			float value = ModConfig.RemoteSyncInterval.Value;
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if (slot.Attaching || (Object)(object)slot.Go == (Object)null || (Object)(object)slot.Sticky == (Object)null || (Object)(object)slot.Part == (Object)null || (Object)(object)slot.Sticky.stuckToCharacter != (Object)(object)me)
				{
					continue;
				}
				string detail;
				Vector3 val = PlacementMath.TryGetButtBase(me, ModConfig.AnchorToLegs.Value, out detail);
				Vector3 offset = PlacementMath.ComputeWorldOffset(me, slot.IsLeft, slot.Lateral, slot.Backward, slot.Vertical);
				offset = PlacementMath.ApplyDelta(me, offset, GetDelta(slot.IsLeft));
				Vector3 val2 = val + offset + ComputeWobble(slot, val) - slot.Part.transform.position;
				slot.Sticky.stuckLocalOffset = slot.Part.transform.InverseTransformVector(val2);
				EnforceNoInteraction(slot);
				if (!(value <= 0f) && !(Time.time < slot.NextRemoteSync))
				{
					slot.NextRemoteSync = Time.time + value;
					Vector3 val3 = val2 - slot.LastSentOffset;
					if (((Vector3)(ref val3)).sqrMagnitude > 0.0001f)
					{
						slot.LastSentOffset = val2;
						SendRemoteOffset(slot, me, val2);
					}
				}
			}
		}

		private Vector3 ComputeWobble(Slot slot, Vector3 basePos)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_0031: 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)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: 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_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)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: 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_0167: 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)
			float value = ModConfig.WobbleStrength.Value;
			float num = Mathf.Clamp(Time.deltaTime, 0.001f, 0.05f);
			if (!slot.WobbleInit)
			{
				slot.WobbleInit = true;
				slot.PrevBase = basePos;
				slot.PrevBaseVel = Vector3.zero;
				slot.WobbleOffset = Vector3.zero;
				slot.WobbleVel = Vector3.zero;
				return Vector3.zero;
			}
			Vector3 val = (basePos - slot.PrevBase) / num;
			Vector3 val2 = (val - slot.PrevBaseVel) / num;
			slot.PrevBase = basePos;
			slot.PrevBaseVel = val;
			if (value <= 0f)
			{
				slot.WobbleOffset = Vector3.zero;
				slot.WobbleVel = Vector3.zero;
				return Vector3.zero;
			}
			if (((Vector3)(ref val2)).sqrMagnitude > 6400f)
			{
				val2 = ((Vector3)(ref val2)).normalized * 80f;
			}
			Vector3 val3 = -180f * slot.WobbleOffset - 9f * slot.WobbleVel - val2;
			slot.WobbleVel += val3 * num;
			slot.WobbleOffset += slot.WobbleVel * num;
			if (((Vector3)(ref slot.WobbleOffset)).sqrMagnitude > 0.0144f)
			{
				slot.WobbleOffset = ((Vector3)(ref slot.WobbleOffset)).normalized * 0.12f;
				slot.WobbleVel *= 0.5f;
			}
			return slot.WobbleOffset * value;
		}

		private static void EnforceNoInteraction(Slot slot)
		{
			if (ModConfig.DisableInteraction.Value && !((Object)(object)slot.Sticky == (Object)null))
			{
				if ((Object)(object)((ItemComponent)slot.Sticky).item != (Object)null)
				{
					((ItemComponent)slot.Sticky).item.blockInteraction = true;
				}
				List<StickyItemComponent> aLL_STUCK_ITEMS = StickyItemComponent.ALL_STUCK_ITEMS;
				if (aLL_STUCK_ITEMS != null && aLL_STUCK_ITEMS.Contains(slot.Sticky))
				{
					aLL_STUCK_ITEMS.Remove(slot.Sticky);
				}
			}
		}

		private static void SendRemoteOffset(Slot slot, Character me, Vector3 offsetFromPart)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected I4, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			PhotonView val = (((Object)(object)me != (Object)null) ? ((Component)me).GetComponent<PhotonView>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)slot.Sticky == (Object)null || (Object)(object)((MonoBehaviourPun)slot.Sticky).photonView == (Object)null || (Object)(object)slot.Part == (Object)null)
			{
				return;
			}
			try
			{
				((MonoBehaviourPun)slot.Sticky).photonView.RPC("RPC_StickToCharacterRemote", (RpcTarget)1, new object[3]
				{
					val.ViewID,
					(int)slot.Part.partType,
					offsetFromPart
				});
			}
			catch (Exception ex)
			{
				ModLog.Warn("同步球位置失败:" + ex.Message);
			}
		}
	}
	internal class CactusNotify : MonoBehaviour
	{
		private class Entry
		{
			public string Text;

			public float EndTime;
		}

		private const int MaxEntries = 3;

		private const float DefaultDuration = 4f;

		private readonly List<Entry> _entries = new List<Entry>();

		private GUIStyle _box;

		private GUIStyle _text;

		public static void Show(string text, float duration = 4f)
		{
			CactusNotify cactusNotify = Object.FindAnyObjectByType<CactusNotify>();
			if (!((Object)(object)cactusNotify == (Object)null))
			{
				cactusNotify.Enqueue(text, duration);
			}
		}

		private void Enqueue(string text, float duration)
		{
			if (!string.IsNullOrEmpty(text))
			{
				while (_entries.Count >= 3)
				{
					_entries.RemoveAt(0);
				}
				_entries.Add(new Entry
				{
					Text = text,
					EndTime = Time.time + Mathf.Max(0.5f, duration)
				});
				ModLog.Info("[提示] " + text);
			}
		}

		private void Update()
		{
			for (int num = _entries.Count - 1; num >= 0; num--)
			{
				if (Time.time >= _entries[num].EndTime)
				{
					_entries.RemoveAt(num);
				}
			}
		}

		private void EnsureStyle()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			PanelTheme.EnsureBuilt();
			if (_text == null)
			{
				_text = new GUIStyle(PanelTheme.Value)
				{
					fontSize = 18,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					wordWrap = false
				};
			}
			_text.normal.textColor = PanelTheme.TxtWarn;
			_text.hover.textColor = PanelTheme.TxtWarn;
			if (_box == null)
			{
				_box = new GUIStyle(PanelTheme.Card);
				_box.padding = new RectOffset(14, 14, 9, 9);
			}
			Font toastFont = PanelTheme.ToastFont;
			if ((Object)(object)toastFont != (Object)null)
			{
				_box.font = toastFont;
				_text.font = toastFont;
			}
		}

		private void OnGUI()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_entries.Count == 0)
				{
					return;
				}
				EnsureStyle();
				float num = (float)Screen.height * 0.1f;
				Rect val2 = default(Rect);
				for (int i = 0; i < _entries.Count; i++)
				{
					Entry entry = _entries[i];
					float num2 = entry.EndTime - Time.time;
					if (!(num2 <= 0f))
					{
						float num3 = Mathf.Clamp01(num2 / 0.6f);
						Vector2 val = _text.CalcSize(new GUIContent(entry.Text));
						float num4 = Mathf.Min(val.x + 28f, (float)Screen.width - 40f);
						((Rect)(ref val2))..ctor(((float)Screen.width - num4) * 0.5f, num, num4, val.y + 18f);
						Color color = GUI.color;
						GUI.color = new Color(1f, 1f, 1f, num3);
						GUI.Box(val2, GUIContent.none, _box);
						GUI.Label(val2, entry.Text, _text);
						GUI.color = color;
						num += ((Rect)(ref val2)).height + 6f;
					}
				}
			}
			catch (Exception ex)
			{
				ModLog.Warn("绘制提示失败:" + ex.Message);
			}
		}
	}
	internal static class CactusPrefabFinder
	{
		private const string DirectPath = "0_Items/CactusBall";

		private static string _resolved;

		private static bool _searched;

		public static string Resolve()
		{
			string text = ModConfig.PrefabName?.Value;
			if (!string.IsNullOrWhiteSpace(text))
			{
				text = text.Trim();
				string text2 = (text.Contains("/") ? text : ("0_Items/" + text));
				ModLog.Detail("使用配置指定的仙人球预制体路径:" + text2);
				return text2;
			}
			if (!string.IsNullOrEmpty(_resolved))
			{
				return _resolved;
			}
			if (_searched)
			{
				return null;
			}
			_searched = true;
			ModLog.Detail("尝试直连读取预制体:Resources.Load<GameObject>(\"0_Items/CactusBall\")");
			GameObject val = Resources.Load<GameObject>("0_Items/CactusBall");
			if ((Object)(object)val != (Object)null && (Object)(object)val.GetComponent<CactusBall>() != (Object)null)
			{
				_resolved = "0_Items/CactusBall";
				ModLog.Info("已定位仙人球预制体(直连):" + _resolved);
				return _resolved;
			}
			ModLog.Detail("直连失败,扫描 Resources/0_Items 全部预制体,查找带 CactusBall 组件的对象……");
			GameObject[] array = Resources.LoadAll<GameObject>("0_Items");
			ModLog.Detail($"0_Items 下共加载 {((array != null) ? array.Length : 0)} 个预制体。");
			GameObject[] array2 = array;
			foreach (GameObject val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2.GetComponent<CactusBall>() != (Object)null)
				{
					_resolved = "0_Items/" + ((Object)val2).name;
					ModLog.Info("已自动定位仙人球预制体(扫描):" + _resolved);
					return _resolved;
				}
			}
			ModLog.Error("未能自动定位仙人球预制体,请在配置文件中手动填写「3-高级 / 仙人球预制体名」。");
			return null;
		}

		public static void Reset()
		{
			_resolved = null;
			_searched = false;
		}
	}
	internal class CactusTracker : MonoBehaviour
	{
		public bool IsLeft;

		public int ViewId = -1;

		public StickyItemComponent Sticky;

		private string Side
		{
			get
			{
				if (!IsLeft)
				{
					return "右臀";
				}
				return "左臀";
			}
		}

		private void OnDisable()
		{
			try
			{
				ModLog.Info($"[{Side}] 仙人球对象被禁用(ViewID={ViewId},对象名={((Object)this).name})。");
			}
			catch (Exception ex)
			{
				ModLog.Warn("[" + Side + "] 记录禁用事件失败:" + ex.Message);
			}
		}

		private void OnDestroy()
		{
			//IL_006c: 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)
			try
			{
				string text = "无";
				string text2 = "无";
				string text3 = "未知";
				if ((Object)(object)Sticky != (Object)null)
				{
					text = (((Object)(object)Sticky.stuckToCharacter == (Object)null) ? "空" : ((Object)Sticky.stuckToCharacter).name);
					text2 = (((Object)(object)((ItemComponent)Sticky).item == (Object)null) ? "无" : ((object)((ItemComponent)Sticky).item.itemState/*cast due to .constrained prefix*/).ToString());
					text3 = (StickyItemComponent.ALL_STUCK_ITEMS != null && StickyItemComponent.ALL_STUCK_ITEMS.Contains(Sticky)).ToString();
				}
				else
				{
					text = "组件已销毁";
				}
				string arg = "未知";
				PhotonView component = ((Component)this).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null)
				{
					arg = ((component.Owner == null) ? "无主" : component.Owner.NickName);
				}
				ModLog.Info($"[{Side}] 仙人球对象被销毁(ViewID={ViewId},Owner={arg}," + "粘附角色=" + text + ",物品状态=" + text2 + ",在粘附列表=" + text3 + ")。");
			}
			catch (Exception ex)
			{
				ModLog.Warn("[" + Side + "] 记录销毁事件失败:" + ex.Message);
			}
		}
	}
	internal static class L
	{
		public static bool English
		{
			get
			{
				if (ModConfig.UseEnglish != null)
				{
					return ModConfig.UseEnglish.Value;
				}
				return false;
			}
		}

		public static string T(string chinese, string english)
		{
			if (!English)
			{
				return chinese;
			}
			return english;
		}

		public static void Toggle()
		{
			if (ModConfig.UseEnglish != null)
			{
				ModConfig.UseEnglish.Value = !ModConfig.UseEnglish.Value;
				ModConfig.Save();
			}
		}

		public static string OnOff(bool on)
		{
			if (!on)
			{
				return T("关", "off");
			}
			return T("开", "on");
		}
	}
	internal static class CactusInteractGuard
	{
		public static void Enforce(StickyItemComponent sticky)
		{
			if (!((Object)(object)sticky == (Object)null))
			{
				if ((Object)(object)((ItemComponent)sticky).item != (Object)null)
				{
					((ItemComponent)sticky).item.blockInteraction = true;
				}
				List<StickyItemComponent> aLL_STUCK_ITEMS = StickyItemComponent.ALL_STUCK_ITEMS;
				if (aLL_STUCK_ITEMS != null && aLL_STUCK_ITEMS.Contains(sticky))
				{
					aLL_STUCK_ITEMS.Remove(sticky);
				}
			}
		}
	}
	internal class LegAttachTest : MonoBehaviour
	{
		private class Slot
		{
			public bool IsLeft;

			public GameObject Go;

			public StickyItemComponent Sticky;

			public Bodypart Part;

			public bool Attaching;

			public bool AppliedInit;

			public float AppliedLateral;

			public float AppliedBackward;

			public float AppliedVertical;

			public Vector3 AppliedDelta;

			public int LastViewId = -1;

			public bool PreviouslyStuck;
		}

		private readonly Slot[] _slots = new Slot[2]
		{
			new Slot
			{
				IsLeft = true
			},
			new Slot
			{
				IsLeft = false
			}
		};

		private static float _ballRadius = -1f;

		private bool _active;

		private float _nextAttemptTime;

		private bool _hadCharacter;

		private void Update()
		{
			if (!ModConfig.TestLegAttach.Value)
			{
				if (_active)
				{
					_active = false;
					ModLog.Info("真实粘附:已关闭,正在清理测试仙人球。");
					DetachAll();
				}
				return;
			}
			_active = true;
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || !PhotonNetwork.InRoom)
			{
				_hadCharacter = false;
				Slot[] slots = _slots;
				for (int i = 0; i < slots.Length; i++)
				{
					slots[i].PreviouslyStuck = false;
				}
				return;
			}
			if (!_hadCharacter)
			{
				_hadCharacter = true;
				ModLog.Info("真实粘附:开始把真实仙人球粘到上腿(角色「" + ((Object)localCharacter).name + "」)。");
			}
			MaintainSlots(localCharacter);
			ApplyLiveTuning(localCharacter);
		}

		private void MaintainSlots(Character me)
		{
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			bool flag = true;
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if (slot.Attaching)
				{
					flag = false;
					continue;
				}
				if ((Object)(object)slot.Go != (Object)null && (Object)(object)slot.Sticky != (Object)null && (Object)(object)slot.Sticky.stuckToCharacter == (Object)(object)me)
				{
					slot.PreviouslyStuck = true;
					CactusInteractGuard.Enforce(slot.Sticky);
					continue;
				}
				bool flag2 = (Object)(object)slot.Go == (Object)null;
				if (slot.PreviouslyStuck && flag2 && ModConfig.NotifyWhenTaken.Value)
				{
					CactusNotify.Show("你的测试仙人球(" + (slot.IsLeft ? "左" : "右") + "腿)被别人取走了!");
				}
				slot.PreviouslyStuck = false;
				if ((Object)(object)slot.Go != (Object)null || slot.LastViewId != -1)
				{
					string text = (((Object)(object)slot.Sticky == (Object)null || (Object)(object)slot.Sticky.stuckToCharacter == (Object)null) ? "空" : ((Object)slot.Sticky.stuckToCharacter).name);
					string text2 = (((Object)(object)slot.Sticky == (Object)null || (Object)(object)((ItemComponent)slot.Sticky).item == (Object)null) ? "无" : ((object)((ItemComponent)slot.Sticky).item.itemState/*cast due to .constrained prefix*/).ToString());
					ModLog.Info("真实粘附:" + (slot.IsLeft ? "左" : "右") + "侧已失效并重建" + string.Format("(ViewID={0},对象={1},", slot.LastViewId, ((Object)(object)slot.Go == (Object)null) ? "已销毁" : ((Object)slot.Go).name) + "粘附角色=" + text + ",物品状态=" + text2 + ")。");
				}
				SafeDestroy(slot.Go);
				slot.Go = null;
				slot.Sticky = null;
				slot.Part = null;
				slot.LastViewId = -1;
				flag = false;
			}
			if (flag || Time.time < _nextAttemptTime)
			{
				return;
			}
			_nextAttemptTime = Time.time + 1f;
			slots = _slots;
			foreach (Slot slot2 in slots)
			{
				if (!slot2.Attaching && (Object)(object)slot2.Go == (Object)null)
				{
					((MonoBehaviour)this).StartCoroutine(AttachRoutine(slot2, me));
				}
			}
		}

		private IEnumerator AttachRoutine(Slot slot, Character me)
		{
			string side = (slot.IsLeft ? "左" : "右");
			string text = (slot.IsLeft ? ModConfig.TestLegLeftPart.Value : ModConfig.TestLegRightPart.Value);
			slot.Attaching = true;
			try
			{
				Bodypart part = BodypartFinder.Find(me, text);
				if ((Object)(object)part == (Object)null)
				{
					ModLog.Warn("真实粘附:找不到" + side + "上腿部位「" + text + "」,请检查配置或开启「调试_打印所有身体部位」。");
					yield break;
				}
				string text2 = CactusPrefabFinder.Resolve();
				if (string.IsNullOrEmpty(text2))
				{
					ModLog.Error("真实粘附:未能解析仙人球预制体路径,放弃本次附着。");
					yield break;
				}
				Quaternion spawnRot = part.transform.rotation * Quaternion.Euler(ModConfig.RotX.Value, ModConfig.RotY.Value, ModConfig.RotZ.Value);
				GameObject go;
				try
				{
					go = PhotonNetwork.Instantiate(text2, part.transform.position, spawnRot, (byte)0, (object[])null);
				}
				catch (Exception arg)
				{
					ModLog.Error($"真实粘附:Instantiate 生成仙人球失败:{arg}");
					yield break;
				}
				CactusBall cactus = (((Object)(object)go != (Object)null) ? go.GetComponent<CactusBall>() : null);
				if ((Object)(object)cactus == (Object)null)
				{
					ModLog.Warn("真实粘附:生成的预制体上没有 CactusBall 组件,已销毁。");
					if ((Object)(object)go != (Object)null)
					{
						SafeDestroy(go);
					}
					yield break;
				}
				if (ModConfig.Scale.Value != 1f)
				{
					Transform transform = go.transform;
					transform.localScale *= ModConfig.Scale.Value;
				}
				((StickyItemComponent)cactus).addWeightToStuckPlayer = 0;
				((StickyItemComponent)cactus).addThornsToStuckPlayer = 0;
				if ((Object)(object)((ItemComponent)cactus).item != (Object)null)
				{
					((ItemComponent)cactus).item.SetKinematic(true);
				}
				if ((Object)(object)((StickyItemComponent)cactus).physicalCollider != (Object)null)
				{
					((StickyItemComponent)cactus).physicalCollider.isTrigger = true;
				}
				slot.Go = go;
				slot.Sticky = (StickyItemComponent)(object)cactus;
				PhotonView component = go.GetComponent<PhotonView>();
				slot.LastViewId = (((Object)(object)component != (Object)null) ? component.ViewID : (-1));
				CactusTracker cactusTracker = go.AddComponent<CactusTracker>();
				cactusTracker.IsLeft = slot.IsLeft;
				cactusTracker.ViewId = slot.LastViewId;
				cactusTracker.Sticky = (StickyItemComponent)(object)cactus;
				yield return null;
				yield return null;
				if ((Object)(object)go == (Object)null || (Object)(object)cactus == (Object)null)
				{
					slot.Go = null;
					slot.Sticky = null;
					yield break;
				}
				if ((Object)(object)((ItemComponent)cactus).item != (Object)null && (int)((ItemComponent)cactus).item.itemState != 0)
				{
					((ItemComponent)cactus).item.itemState = (ItemState)0;
				}
				string detail;
				float num = PlacementMath.MeasureRadius(go, out detail);
				if (_ballRadius <= 0f && num > 0f)
				{
					_ballRadius = num;
				}
				float num2 = ((_ballRadius > 0f) ? _ballRadius : num) * ModConfig.RadiusScale.Value;
				float num3 = ((ModConfig.LateralSpacing.Value > 0f) ? ModConfig.LateralSpacing.Value : num2);
				float num4 = ((ModConfig.BackwardOffset.Value > 0f) ? ModConfig.BackwardOffset.Value : num2);
				float value = ModConfig.VerticalOffset.Value;
				Vector3 delta = GetDelta(slot.IsLeft);
				Vector3 val = PlacementMath.ApplyDelta(me, PlacementMath.ComputeWorldOffset(me, slot.IsLeft, num3, num4, value), delta);
				Vector3 val2 = part.transform.position + val;
				slot.Part = part;
				slot.AppliedLateral = num3;
				slot.AppliedBackward = num4;
				slot.AppliedVertical = value;
				slot.AppliedDelta = delta;
				slot.AppliedInit = true;
				go.transform.SetPositionAndRotation(val2, spawnRot);
				if (StickyApi.Stick((StickyItemComponent)(object)cactus, me, part, val))
				{
					CactusInteractGuard.Enforce((StickyItemComponent)(object)cactus);
					ModLog.Info($"真实粘附:{side}球已粘到 {part.partType},球心={val2},已禁用交互。");
					yield break;
				}
				ModLog.Warn($"真实粘附:{side}球粘附「{part.partType}」失败,稍后重试。");
				SafeDestroy(go);
				slot.Go = null;
				slot.Sticky = null;
			}
			finally
			{
				slot.Attaching = false;
			}
		}

		private static Vector3 GetDelta(bool isLeft)
		{
			//IL_0045: 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)
			if (!isLeft)
			{
				return new Vector3(ModConfig.TestRightOffsetRight.Value, ModConfig.TestRightOffsetUp.Value, ModConfig.TestRightOffsetForward.Value);
			}
			return new Vector3(ModConfig.TestLeftOffsetRight.Value, ModConfig.TestLeftOffsetUp.Value, ModConfig.TestLeftOffsetForward.Value);
		}

		private void ApplyLiveTuning(Character me)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: 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_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: 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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			float num = ((_ballRadius > 0f) ? _ballRadius : 0.4f) * ModConfig.RadiusScale.Value;
			float num2 = ((ModConfig.LateralSpacing.Value > 0f) ? ModConfig.LateralSpacing.Value : num);
			float num3 = ((ModConfig.BackwardOffset.Value > 0f) ? ModConfig.BackwardOffset.Value : num);
			float value = ModConfig.VerticalOffset.Value;
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if (slot.Attaching || (Object)(object)slot.Go == (Object)null || (Object)(object)slot.Sticky == (Object)null || (Object)(object)slot.Part == (Object)null || (Object)(object)slot.Sticky.stuckToCharacter != (Object)(object)me)
				{
					continue;
				}
				Vector3 delta = GetDelta(slot.IsLeft);
				if (slot.AppliedInit && !(Mathf.Abs(num2 - slot.AppliedLateral) > 1E-05f) && !(Mathf.Abs(num3 - slot.AppliedBackward) > 1E-05f) && !(Mathf.Abs(value - slot.AppliedVertical) > 1E-05f))
				{
					Vector3 val = delta - slot.AppliedDelta;
					if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f))
					{
						continue;
					}
				}
				Vector3 val2 = PlacementMath.ApplyDelta(me, PlacementMath.ComputeWorldOffset(me, slot.IsLeft, num2, num3, value), delta);
				slot.Sticky.stuckLocalOffset = slot.Part.transform.InverseTransformVector(val2);
				slot.Go.transform.SetPositionAndRotation(slot.Part.transform.position + val2, slot.Go.transform.rotation);
				slot.AppliedLateral = num2;
				slot.AppliedBackward = num3;
				slot.AppliedVertical = value;
				slot.AppliedDelta = delta;
				slot.AppliedInit = true;
				SendRemoteOffset(slot, me, val2);
				ModLog.Detail(string.Format("真实粘附:{0}侧定位参数变化,已重算并应用,球心={1}。", slot.IsLeft ? "左" : "右", slot.Part.transform.position + val2));
			}
		}

		private static void SendRemoteOffset(Slot slot, Character me, Vector3 worldOffset)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected I4, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			PhotonView val = (((Object)(object)me != (Object)null) ? ((Component)me).GetComponent<PhotonView>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)slot.Sticky == (Object)null || (Object)(object)((MonoBehaviourPun)slot.Sticky).photonView == (Object)null || (Object)(object)slot.Part == (Object)null)
			{
				return;
			}
			try
			{
				((MonoBehaviourPun)slot.Sticky).photonView.RPC("RPC_StickToCharacterRemote", (RpcTarget)1, new object[3]
				{
					val.ViewID,
					(int)slot.Part.partType,
					worldOffset
				});
			}
			catch (Exception ex)
			{
				ModLog.Warn("真实粘附:同步位置给其他玩家失败:" + ex.Message);
			}
		}

		private void DetachAll()
		{
			Slot[] slots = _slots;
			foreach (Slot slot in slots)
			{
				if ((Object)(object)slot.Go != (Object)null)
				{
					StickyApi.Unstick(slot.Sticky);
					SafeDestroy(slot.Go);
				}
				slot.Go = null;
				slot.Sticky = null;
				slot.Part = null;
				slot.AppliedInit = false;
				slot.PreviouslyStuck = false;
			}
		}

		private static void SafeDestroy(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			try
			{
				if (PhotonNetwork.InRoom)
				{
					PhotonNetwork.Destroy(go);
				}
				else
				{
					Object.Destroy((Object)(object)go);
				}
			}
			catch (Exception ex)
			{
				ModLog.Warn("真实粘附:销毁失败,改为本地销毁:" + ex.Message);
				Object.Destroy((Object)(object)go);
			}
		}

		private void OnDestroy()
		{
			DetachAll();
		}
	}
	internal static class ModConfig
	{
		private static ConfigFile _config;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> TestLegAttach;

		public static ConfigEntry<string> TestLegLeftPart;

		public static ConfigEntry<string> TestLegRightPart;

		public static ConfigEntry<float> TestLeftOffsetRight;

		public static ConfigEntry<float> TestLeftOffsetUp;

		public static ConfigEntry<float> TestLeftOffsetForward;

		public static ConfigEntry<float> TestRightOffsetRight;

		public static ConfigEntry<float> TestRightOffsetUp;

		public static ConfigEntry<float> TestRightOffsetForward;

		public static ConfigEntry<string> BasePart;

		public static ConfigEntry<bool> AnchorToLegs;

		public static ConfigEntry<float> LateralSpacing;

		public static ConfigEntry<float> BackwardOffset;

		public static ConfigEntry<float> VerticalOffset;

		public static ConfigEntry<float> RadiusScale;

		public static ConfigEntry<float> LeftOffsetRight;

		public static ConfigEntry<float> LeftOffsetUp;

		public static ConfigEntry<float> LeftOffsetForward;

		public static ConfigEntry<float> RightOffsetRight;

		public static ConfigEntry<float> RightOffsetUp;

		public static ConfigEntry<float> RightOffsetForward;

		public static ConfigEntry<float> Scale;

		public static ConfigEntry<float> RotX;

		public static ConfigEntry<float> RotY;

		public static ConfigEntry<float> RotZ;

		public static ConfigEntry<bool> AddWeight;

		public static ConfigEntry<bool> AddThorns;

		public static ConfigEntry<bool> NotifyWhenTaken;

		public static ConfigEntry<bool> ImmuneToCactus;

		public static ConfigEntry<bool> DisableInteraction;

		public static ConfigEntry<float> WobbleStrength;

		public static ConfigEntry<string> PrefabName;

		public static ConfigEntry<bool> DebugDumpBodyparts;

		public static ConfigEntry<float> RemoteSyncInterval;

		public static ConfigEntry<bool> VerboseLog;

		public static ConfigEntry<float> SnapshotInterval;

		public static ConfigEntry<KeyCode> HotkeyPanel;

		public static ConfigEntry<float> PanelScale;

		public static ConfigEntry<bool> UseEnglish;

		public static void Bind(ConfigFile config)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Expected O, but got Unknown
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Expected O, but got Unknown
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Expected O, but got Unknown
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Expected O, but got Unknown
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Expected O, but got Unknown
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Expected O, but got Unknown
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Expected O, but got Unknown
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Expected O, but got Unknown
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Expected O, but got Unknown
			//IL_0425: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Expected O, but got Unknown
			//IL_045d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Expected O, but got Unknown
			//IL_0495: Unknown result type (might be due to invalid IL or missing references)
			//IL_049f: Expected O, but got Unknown
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Expected O, but got Unknown
			//IL_0505: Unknown result type (might be due to invalid IL or missing references)
			//IL_050f: Expected O, but got Unknown
			//IL_053d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0547: Expected O, but got Unknown
			//IL_0636: Unknown result type (might be due to invalid IL or missing references)
			//IL_0640: Expected O, but got Unknown
			//IL_0689: Unknown result type (might be due to invalid IL or missing references)
			//IL_0693: Expected O, but got Unknown
			//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Expected O, but got Unknown
			_config = config;
			Enabled = config.Bind<bool>("0-总开关", "启用屁股仙人球", false, "关闭后不再生成,并会清理已生成的仙人球。可在面板里直接开关。");
			TestLegAttach = config.Bind<bool>("1-真实粘附(上腿)", "启用真实粘附", false, "开启后把两个原版仙人球直接粘在左右上腿骨上,不做任何物理模拟,完全靠腿部摆动产生晃动。");
			TestLegLeftPart = config.Bind<string>("1-真实粘附(上腿)", "左上腿部位", "Leg_L", "左侧仙人球粘附的身体部位名称,默认 Leg_L(左大腿)。");
			TestLegRightPart = config.Bind<string>("1-真实粘附(上腿)", "右上腿部位", "Leg_R", "右侧仙人球粘附的身体部位名称,默认 Leg_R(右大腿)。");
			TestLeftOffsetRight = config.Bind<float>("1-真实粘附(上腿)", "左侧_左右(米)", 0.06f, new ConfigDescription("相对左大腿骨的左右偏移(米),正数向右。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			TestLeftOffsetUp = config.Bind<float>("1-真实粘附(上腿)", "左侧_上下(米)", 0.74f, new ConfigDescription("相对左大腿骨的上下偏移(米),正数向上。调小可让球往下贴。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			TestLeftOffsetForward = config.Bind<float>("1-真实粘附(上腿)", "左侧_前后(米)", 0f, new ConfigDescription("相对左大腿骨的前后偏移(米),正数向前。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			TestRightOffsetRight = config.Bind<float>("1-真实粘附(上腿)", "右侧_左右(米)", -0.269f, new ConfigDescription("相对右大腿骨的左右偏移(米),正数向右。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			TestRightOffsetUp = config.Bind<float>("1-真实粘附(上腿)", "右侧_上下(米)", 0.7f, new ConfigDescription("相对右大腿骨的上下偏移(米),正数向上。调小可让球往下贴。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			TestRightOffsetForward = config.Bind<float>("1-真实粘附(上腿)", "右侧_前后(米)", 0f, new ConfigDescription("相对右大腿骨的前后偏移(米),正数向前。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			BasePart = config.Bind<string>("2-屁股仙人球", "基准部位", "Hip", "作为两半屁股中点的身体部位名称。默认 Hip(骨盆中心)。");
			AnchorToLegs = config.Bind<bool>("2-屁股仙人球", "以双腿根部为基准高度", true, "用左右大腿骨根部的连线中点作为屁股中心高度,更贴合真实屁股;关闭则完全以「基准部位」为准。");
			LateralSpacing = config.Bind<float>("2-屁股仙人球", "左右间距(米)", 0f, new ConfigDescription("每个球球心到中线的距离。0=按球半径自动,使两球刚好相切。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			BackwardOffset = config.Bind<float>("2-屁股仙人球", "贴背距离(米)", 0f, new ConfigDescription("沿角色背后方向偏移的距离。0=按球半径自动。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			VerticalOffset = config.Bind<float>("2-屁股仙人球", "上下偏移(米)", -1f, new ConfigDescription("相对基准位置的上下偏移(米),正数向上。太靠上就调小。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			RadiusScale = config.Bind<float>("2-屁股仙人球", "半径修正", 1f, new ConfigDescription("自动半径的倍率。两球仍重叠就调大,缝隙太大就调小。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			LeftOffsetRight = config.Bind<float>("2-屁股仙人球", "左侧_左右(米)", 0.07f, new ConfigDescription("左侧球沿角色「右」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			LeftOffsetUp = config.Bind<float>("2-屁股仙人球", "左侧_上下(米)", 0.8f, new ConfigDescription("左侧球沿角色「上」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			LeftOffsetForward = config.Bind<float>("2-屁股仙人球", "左侧_前后(米)", 0f, new ConfigDescription("左侧球沿角色「前」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			RightOffsetRight = config.Bind<float>("2-屁股仙人球", "右侧_左右(米)", 0.06f, new ConfigDescription("右侧球沿角色「右」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			RightOffsetUp = config.Bind<float>("2-屁股仙人球", "右侧_上下(米)", 0.8f, new ConfigDescription("右侧球沿角色「上」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			RightOffsetForward = config.Bind<float>("2-屁股仙人球", "右侧_前后(米)", 0f, new ConfigDescription("右侧球沿角色「前」方向的偏移(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			Scale = config.Bind<float>("2-屁股仙人球", "缩放", 1f, new ConfigDescription("仙人球的缩放倍数。缩放仅本机可见。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			RotX = config.Bind<float>("2-屁股仙人球", "旋转X", 0f, new ConfigDescription("仙人球自转角度(度)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			RotY = config.Bind<float>("2-屁股仙人球", "旋转Y", 0f, new ConfigDescription("仙人球自转角度(度)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			RotZ = config.Bind<float>("2-屁股仙人球", "旋转Z", 0f, new ConfigDescription("仙人球自转角度(度)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			WobbleStrength = config.Bind<float>("2-屁股仙人球", "晃动强度", 3f, new ConfigDescription("模式二的惯性晃动幅度。0=不晃动,越大越明显。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			AddWeight = config.Bind<bool>("3-效果", "增加负重", false, "粘在身上的仙人球给角色增加负重(与原版一致)。开启「免疫仙人球效果」时在本机无效。");
			AddThorns = config.Bind<bool>("3-效果", "造成扎刺伤害", false, "粘在身上的仙人球持续造成扎刺伤害。开启「免疫仙人球效果」时在本机无效。");
			NotifyWhenTaken = config.Bind<bool>("3-效果", "被取走时屏幕提示", true, "身上的仙人球被他人取走时,在屏幕上方弹出一行提示。");
			ImmuneToCactus = config.Bind<bool>("3-效果", "免疫仙人球效果(仅本机)", true, "任何仙人球粘到身上都不掉血、不扎刺、不影响体力条。他人的仙人球仍正常显示以保持视角一致。仅本机生效。");
			DisableInteraction = config.Bind<bool>("3-效果", "禁用交互", true, "仙人球不会被自己或他人捡起,也不会出现在低头拾取提示里。");
			PrefabName = config.Bind<string>("4-高级", "仙人球预制体名", "", "留空自动查找。可填 \"CactusBall\" 或完整路径 \"0_Items/xxx\"。");
			DebugDumpBodyparts = config.Bind<bool>("4-高级", "调试_打印所有身体部位", false, "把当前角色的所有身体部位名称与坐标打印到日志。");
			RemoteSyncInterval = config.Bind<float>("4-高级", "远程同步间隔(秒)", 0.25f, new ConfigDescription("每隔多少秒把当前偏移重发给其他玩家。0=只在粘附瞬间同步一次。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			VerboseLog = config.Bind<bool>("5-日志", "详细日志", false, "输出粘附流程的每一步细节,排错时开启。");
			SnapshotInterval = config.Bind<float>("5-日志", "诊断快照间隔(秒)", 2f, new ConfigDescription("每隔多少秒打印一次状态快照。0=关闭。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
			HotkeyPanel = config.Bind<KeyCode>("6-面板", "面板快捷键", (KeyCode)289, "按此键打开/关闭本面板。");
			PanelScale = config.Bind<float>("6-面板", "面板缩放", 1f, new ConfigDescription("面板整体缩放倍率(含文字)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2.5f), Array.Empty<object>()));
			UseEnglish = config.Bind<bool>("6-面板", "英文界面", false, "开启后菜单文案切换为英文。也可在面板底栏一键切换。");
		}

		public static void Save()
		{
			try
			{
				ConfigFile config = _config;
				if (config != null)
				{
					config.Save();
				}
				ModLog.Info("配置已保存。");
			}
			catch (Exception ex)
			{
				ModLog.Error("保存配置失败:" + ex.Message);
			}
		}
	}
	internal static class ModLog
	{
		public static bool Verbose
		{
			get
			{
				if (ModConfig.VerboseLog != null)
				{
					return ModConfig.VerboseLog.Value;
				}
				return true;
			}
		}

		public static void Info(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		public static void Detail(string message)
		{
			if (Verbose)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[详细] " + message));
				}
			}
		}

		public static void Warn(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		public static void Error(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "UpdateWeight")]
	internal static class CactusImmunityPatch
	{
		private static void Prefix(CharacterAfflictions __instance)
		{
			try
			{
				if (!ModConfig.Enabled.Value || !ModConfig.ImmuneToCactus.Value)
				{
					return;
				}
				Character localCharacter = Character.localCharacter;
				if ((Object)(object)localCharacter == (Object)null || localCharacter.refs == null || (Object)(object)localCharacter.refs.afflictions != (Object)(object)__instance)
				{
					return;
				}
				if ((Object)(object)localCharacter.data != (Object)null && (Object)(object)localCharacter.data.currentStickyItem != (Object)null)
				{
					localCharacter.data.currentStickyItem.addWeightToStuckPlayer = 0;
					localCharacter.data.currentStickyItem.addThornsToStuckPlayer = 0;
				}
				List<StickyItemComponent> aLL_STUCK_ITEMS = StickyItemComponent.ALL_STUCK_ITEMS;
				if (aLL_STUCK_ITEMS == null)
				{
					return;
				}
				for (int i = 0; i < aLL_STUCK_ITEMS.Count; i++)
				{
					StickyItemComponent val = aLL_STUCK_ITEMS[i];
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val.stuckToCharacter != (Object)(object)localCharacter))
					{
						val.addWeightToStuckPlayer = 0;
						val.addThornsToStuckPlayer = 0;
					}
				}
			}
			catch (Exception ex)
			{
				ModLog.Warn("免疫仙人球效果时出错:" + ex.Message);
			}
		}
	}
	internal static class PlacementDefaults
	{
		public static readonly Vector3 LegLeft = new Vector3(0.06f, 0.74f, 0f);

		public static readonly Vector3 LegRight = new Vector3(-0.269f, 0.7f, 0f);

		public static readonly Vector3 ButtLeft = new Vector3(0.07f, 0.8f, 0f);

		public static readonly Vector3 ButtRight = new Vector3(0.06f, 0.8f, 0f);
	}
	internal static class PlacementMath
	{
		public static bool TryGetFrame(Character character, out Vector3 forward, out Vector3 right, out Vector3 up)
		{
			//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_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)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_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_00c5: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00f1: 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)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			forward = Vector3.forward;
			right = Vector3.right;
			up = Vector3.up;
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			Vector3 val = (((Object)(object)character.data != (Object)null) ? character.data.lookDirection_Flat : Vector3.zero);
			val.y = 0f;
			Vector3 val2;
			if (((Vector3)(ref val)).sqrMagnitude > 1E-06f)
			{
				forward = ((Vector3)(ref val)).normalized;
				val2 = Vector3.Cross(Vector3.up, forward);
				right = ((Vector3)(ref val2)).normalized;
				up = Vector3.up;
				return true;
			}
			Bodypart val3 = ((character.refs != null) ? character.refs.hip : null);
			if ((Object)(object)val3 != (Object)null)
			{
				Vector3 val4 = Vector3.ProjectOnPlane(val3.transform.forward, Vector3.up);
				if (((Vector3)(ref val4)).sqrMagnitude > 1E-06f)
				{
					forward = ((Vector3)(ref val4)).normalized;
					val2 = Vector3.Cross(Vector3.up, forward);
					right = ((Vector3)(ref val2)).normalized;
					up = Vector3.up;
					return true;
				}
			}
			return false;
		}

		public static Vector3 TryGetButtBase(Character character, bool anchorToLegs, out string detail)
		{
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0087: 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_008e: 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_0118: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			Bodypart val = null;
			Bodypart part = null;
			Bodypart part2 = null;
			if ((Object)(object)character != (Object)null && character.refs != null && (Object)(object)character.refs.hip != (Object)null)
			{
				val = character.refs.hip;
			}
			if (anchorToLegs)
			{
				BodypartFinder.TryFind(character, (BodypartType)11, out part);
				BodypartFinder.TryFind(character, (BodypartType)14, out part2);
			}
			if ((Object)(object)part != (Object)null && (Object)(object)part2 != (Object)null)
			{
				Vector3 val2 = (part.transform.position + part2.transform.position) * 0.5f;
				Vector3 val3 = val2;
				if ((Object)(object)val != (Object)null)
				{
					((Vector3)(ref val2))..ctor(val.transform.position.x, val3.y, val3.z);
				}
				detail = $"双腿根部中点(髋关节高度),Leg_L={part.transform.position},Leg_R={part2.transform.position}";
				return val2;
			}
			if ((Object)(object)val != (Object)null)
			{
				detail = "双腿部位不可用,回退到骨盆 Hip";
				return val.transform.position;
			}
			detail = "无法取得基准位置";
			if (!((Object)(object)character != (Object)null))
			{
				return Vector3.zero;
			}
			return ((Component)character).transform.position;
		}

		public static Vector3 ComputeWorldOffset(Character character, bool isLeft, float lateralSpacing, float backwardOffset, float verticalOffset)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			TryGetFrame(character, out var forward, out var right, out var up);
			return right * (isLeft ? (0f - lateralSpacing) : lateralSpacing) - forward * backwardOffset + up * verticalOffset;
		}

		public static Vector3 ApplyDelta(Character character, Vector3 offset, Vector3 delta)
		{
			//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_001c: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (delta == Vector3.zero)
			{
				return offset;
			}
			TryGetFrame(character, out var forward, out var right, out var up);
			return offset + right * delta.x + up * delta.y + forward * delta.z;
		}

		public static float MeasureRadius(GameObject go, out string detail)
		{
			//IL_0107: 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)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			string text = "无";
			Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				SphereCollider val2 = (SphereCollider)(object)((val is SphereCollider) ? val : null);
				float num2;
				string text2;
				if (val2 != null)
				{
					num2 = val2.radius * MaxAbsScale(((Component)val2).transform);
					text2 = "SphereCollider";
				}
				else
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null);
					if (val3 != null)
					{
						num2 = val3.radius * MaxAbsScale(((Component)val3).transform);
						text2 = "CapsuleCollider";
					}
					else
					{
						Bounds bounds = val.bounds;
						Vector3 extents = ((Bounds)(ref bounds)).extents;
						num2 = Mathf.Min(new float[3] { extents.x, extents.y, extents.z });
						text2 = ((object)val).GetType().Name;
					}
				}
				if (num2 > num)
				{
					num = num2;
					text = text2;
				}
			}
			float num3 = 0f;
			Renderer[] componentsInChildren2 = go.GetComponentsInChildren<Renderer>(true);
			bool flag = false;
			Bounds bounds2 = default(Bounds);
			((Bounds)(ref bounds2))..ctor(go.transform.position, Vector3.zero);
			foreach (Renderer val4 in componentsInChildren2)
			{
				if (!((Object)(object)val4 == (Object)null) && (val4 is MeshRenderer || val4 is SkinnedMeshRenderer))
				{
					if (!flag)
					{
						bounds2 = val4.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds2)).Encapsulate(val4.bounds);
					}
				}
			}
			if (flag)
			{
				num3 = Mathf.Max(new float[3]
				{
					((Bounds)(ref bounds2)).extents.x,
					((Bounds)(ref bounds2)).extents.y,
					((Bounds)(ref bounds2)).extents.z
				});
			}
			float num4 = ((num > 0f) ? num : num3);
			detail = $"碰撞体半径={num:F3}({text}),渲染半径={num3:F3},采用={num4:F3}";
			return num4;
		}

		private static float MaxAbsScale(Transform t)
		{
			//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_000f: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lossyScale = t.lossyScale;
			return Mathf.Max(new float[3]
			{
				Mathf.Abs(lossyScale.x),
				Mathf.Abs(lossyScale.y),
				Mathf.Abs(lossyScale.z)
			});
		}
	}
	[BepInPlugin("peak.buttcactus", "屁股仙人球", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private GameObject _host;

		private GameObject _panelHost;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			try
			{
				new Harmony("peak.buttcactus").PatchAll(typeof(Plugin).Assembly);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("应用免疫补丁失败:" + ex));
			}
			_host = new GameObject("ButtCactus_Host");
			_host.AddComponent<ButtCactusService>();
			_host.AddComponent<LegAttachTest>();
			_host.AddComponent<CactusNotify>();
			Object.DontDestroyOnLoad((Object)(object)_host);
			PanelTheme.Log = delegate(string msg)
			{
				Log.LogInfo((object)msg);
			};
			_panelHost = new GameObject("ButtCactus_PanelHost");
			((Object)_panelHost).hideFlags = (HideFlags)61;
			PanelHost panelHost = _panelHost.AddComponent<PanelHost>();
			panelHost.Panel = new ButtCactusPanel();
			panelHost.ToggleKey = () => ModConfig.HotkeyPanel.Value;
			panelHost.LogError = delegate(string msg)
			{
				Log.LogError((object)msg);
			};
			Object.DontDestroyOnLoad((Object)(object)_panelHost);
			Log.LogInfo((object)string.Format("{0} v{1} 已加载,按 {2} 打开面板。", "屁股仙人球", "1.1.0", ModConfig.HotkeyPanel.Value));
		}
	}
	internal static class PluginInfo
	{
		public const string Guid = "peak.buttcactus";

		public const string Name = "屁股仙人球";

		public const string Version = "1.1.0";
	}
	internal static class StickyApi
	{
		private static MethodInfo _stickMethod;

		private static MethodInfo _unstickMethod;

		private static MethodInfo StickMethod
		{
			get
			{
				if (_stickMethod == null)
				{
					_stickMethod = typeof(StickyItemComponent).GetMethod("StickToCharacterLocal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (_stickMethod == null)
					{
						ModLog.Error("未能反射到 StickyItemComponent.StickToCharacterLocal,粘附功能不可用。");
					}
					else
					{
						ModLog.Detail("已反射到 StickyItemComponent.StickToCharacterLocal。");
					}
				}
				return _stickMethod;
			}
		}

		private static MethodInfo UnstickMethod
		{
			get
			{
				if (_unstickMethod == null)
				{
					_unstickMethod = typeof(StickyItemComponent).GetMethod("Unstick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (_unstickMethod == null)
					{
						ModLog.Warn("未能反射到 StickyItemComponent.Unstick,清理功能将退化为直接销毁。");
					}
					else
					{
						ModLog.Detail("已反射到 StickyItemComponent.Unstick。");
					}
				}
				return _unstickMethod;
			}
		}

		public static bool Stick(StickyItemComponent sticky, Character character, Bodypart bodypart, Vector3 worldOffset)
		{
			//IL_0045: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sticky == (Object)null || (Object)(object)character == (Object)null || (Object)(object)bodypart == (Object)null)
			{
				return false;
			}
			MethodInfo stickMethod = StickMetho