Decompiled source of Offline Companions v0.1.0

Companions.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 Companions
{
	[BepInPlugin("com.profmags.companions", "Offline Companions", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class CompanionsPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.profmags.companions";

		public const string PluginName = "Offline Companions";

		public const string PluginVersion = "0.1.0";

		private static Harmony _harmony;

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyCode> DirectTargetKey;

		internal static ConfigEntry<bool> MaleShowSpeechText;

		internal static ConfigEntry<bool> MaleEnableVoiceAudio;

		internal static ConfigEntry<bool> FemaleShowSpeechText;

		internal static ConfigEntry<bool> FemaleEnableVoiceAudio;

		internal static ConfigEntry<bool> SpawnStarterCompanion;

		private bool _fontFixWarned;

		private void Awake()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Offline Companions v0.1.0 loading...");
			DirectTargetKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "DirectTargetKey", (KeyCode)122, "Press while looking at an enemy to direct companions to focus-fire that target.");
			MaleShowSpeechText = ((BaseUnityPlugin)this).Config.Bind<bool>("Speech", "MaleShowOverheadText", false, "Show overhead speech text for male companions.");
			MaleEnableVoiceAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Speech", "MaleEnableVoiceAudio", true, "Play voice audio clips for male companions.");
			FemaleShowSpeechText = ((BaseUnityPlugin)this).Config.Bind<bool>("Speech", "FemaleShowOverheadText", true, "Show overhead speech text for female companions.");
			FemaleEnableVoiceAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Speech", "FemaleEnableVoiceAudio", false, "Play voice audio clips for female companions.");
			SpawnStarterCompanion = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SpawnStarterCompanion", true, "Spawn a free companion when entering a new world for the first time.");
			_harmony = new Harmony("com.profmags.companions");
			try
			{
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				int num = 0;
				foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods())
				{
					_ = patchedMethod;
					num++;
				}
				Log.LogInfo((object)string.Format("{0} loaded successfully! ({1} methods patched)", "Offline Companions", num));
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[Companions] Harmony PatchAll failed: {arg}");
			}
			SceneManager.sceneLoaded += OnSceneLoaded;
			Game.m_playerInitialSpawn += CompanionManager.SpawnStarterCompanion;
			((Component)this).gameObject.AddComponent<CompanionVoice>();
			EnsureTmpDefaultFont("awake");
		}

		private void Start()
		{
			EnsureTmpDefaultFont("start");
		}

		private void Update()
		{
			CompanionManager.ProcessRespawns(Time.deltaTime);
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Game.m_playerInitialSpawn -= CompanionManager.SpawnStarterCompanion;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Offline Companions unloaded.");
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			EnsureTmpDefaultFont("scene:" + ((Scene)(ref scene)).name);
		}

		private static bool IsBrokenTmpFont(TMP_FontAsset font)
		{
			if (!((Object)(object)font == (Object)null))
			{
				return ((Object)font).name.IndexOf("LiberationSans", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static TMP_FontAsset FindReplacementTmpFont()
		{
			TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
			foreach (TMP_Text val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					TMP_FontAsset font = val.font;
					if (!IsBrokenTmpFont(font))
					{
						return font;
					}
				}
			}
			TMP_FontAsset[] array2 = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			TMP_FontAsset val2 = null;
			foreach (TMP_FontAsset val3 in array2)
			{
				if (!IsBrokenTmpFont(val3))
				{
					string text = ((Object)val3).name.ToLowerInvariant();
					if (text.Contains("averia") || text.Contains("norse") || text.Contains("valheim"))
					{
						return val3;
					}
					if ((Object)(object)val2 == (Object)null)
					{
						val2 = val3;
					}
				}
			}
			return val2;
		}

		private void EnsureTmpDefaultFont(string source)
		{
			if (!IsBrokenTmpFont(TMP_Settings.defaultFontAsset))
			{
				return;
			}
			TMP_FontAsset val = FindReplacementTmpFont();
			if ((Object)(object)val == (Object)null)
			{
				if (!_fontFixWarned)
				{
					_fontFixWarned = true;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"[Fonts] Could not find a replacement TMP font asset yet.");
					}
				}
				return;
			}
			TMP_Settings.defaultFontAsset = val;
			_fontFixWarned = false;
			int num = 0;
			TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
			foreach (TMP_Text val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && IsBrokenTmpFont(val2.font))
				{
					val2.font = val;
					num++;
				}
			}
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[Fonts] TMP default font repaired to '{((Object)val).name}' ({source}), reassigned={num}.");
			}
		}
	}
	internal static class ReflectionHelper
	{
		private static readonly FieldInfo _blockingField = AccessTools.Field(typeof(Character), "m_blocking");

		internal static readonly FieldInfo LeftItemField = AccessTools.Field(typeof(Humanoid), "m_leftItem");

		internal static readonly FieldInfo RightItemField = AccessTools.Field(typeof(Humanoid), "m_rightItem");

		private static readonly FieldInfo _blockTimerField = AccessTools.Field(typeof(Humanoid), "m_blockTimer");

		private static readonly FieldInfo _attackDrawTimeField = AccessTools.Field(typeof(Humanoid), "m_attackDrawTime");

		internal static readonly MethodInfo UpdateVisualsMethod = AccessTools.Method(typeof(VisEquipment), "UpdateVisuals", (Type[])null, (Type[])null);

		private static bool _warnedBlocking;

		private static bool _warnedBlockTimer;

		private static readonly FieldInfo _allStationsField = AccessTools.Field(typeof(CraftingStation), "m_allStations");

		private static readonly FieldInfo _projOwnerField = AccessTools.Field(typeof(Projectile), "m_owner");

		internal static bool TrySetBlocking(Character c, bool value)
		{
			if ((Object)(object)c == (Object)null || _blockingField == null)
			{
				WarnOnce(ref _warnedBlocking, "Character.m_blocking");
				return false;
			}
			try
			{
				_blockingField.SetValue(c, value);
				return true;
			}
			catch (Exception)
			{
				WarnOnce(ref _warnedBlocking, "Character.m_blocking");
				return false;
			}
		}

		internal static bool GetBlocking(Character c)
		{
			if ((Object)(object)c == (Object)null || _blockingField == null)
			{
				return false;
			}
			try
			{
				return (bool)_blockingField.GetValue(c);
			}
			catch
			{
				return false;
			}
		}

		internal static ItemData GetLeftItem(Humanoid h)
		{
			if ((Object)(object)h == (Object)null || LeftItemField == null)
			{
				return null;
			}
			try
			{
				object? value = LeftItemField.GetValue(h);
				return (ItemData)((value is ItemData) ? value : null);
			}
			catch
			{
				return null;
			}
		}

		internal static ItemData GetRightItem(Humanoid h)
		{
			if ((Object)(object)h == (Object)null || RightItemField == null)
			{
				return null;
			}
			try
			{
				object? value = RightItemField.GetValue(h);
				return (ItemData)((value is ItemData) ? value : null);
			}
			catch
			{
				return null;
			}
		}

		internal static bool TrySetBlockTimer(Humanoid h, float value)
		{
			if ((Object)(object)h == (Object)null || _blockTimerField == null)
			{
				WarnOnce(ref _warnedBlockTimer, "Humanoid.m_blockTimer");
				return false;
			}
			try
			{
				_blockTimerField.SetValue(h, value);
				return true;
			}
			catch (Exception)
			{
				WarnOnce(ref _warnedBlockTimer, "Humanoid.m_blockTimer");
				return false;
			}
		}

		internal static float GetBlockTimer(Humanoid h)
		{
			if ((Object)(object)h == (Object)null || _blockTimerField == null)
			{
				return -1f;
			}
			try
			{
				return (float)_blockTimerField.GetValue(h);
			}
			catch
			{
				return -1f;
			}
		}

		internal static bool TrySetAttackDrawTime(Humanoid h, float value)
		{
			if ((Object)(object)h == (Object)null || _attackDrawTimeField == null)
			{
				return false;
			}
			try
			{
				_attackDrawTimeField.SetValue(h, value);
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static List<CraftingStation> GetAllCraftingStations()
		{
			if (_allStationsField == null)
			{
				return null;
			}
			try
			{
				return _allStationsField.GetValue(null) as List<CraftingStation>;
			}
			catch
			{
				return null;
			}
		}

		internal static Character GetProjectileOwner(Projectile proj)
		{
			if ((Object)(object)proj == (Object)null || _projOwnerField == null)
			{
				return null;
			}
			try
			{
				object? value = _projOwnerField.GetValue(proj);
				return (Character)((value is Character) ? value : null);
			}
			catch
			{
				return null;
			}
		}

		private static void WarnOnce(ref bool flag, string fieldName)
		{
			if (!flag)
			{
				flag = true;
				CompanionsPlugin.Log.LogWarning((object)("[ReflectionHelper] Failed to access " + fieldName + " — this field may have been renamed in a game update."));
			}
		}
	}
	public static class CompanionManager
	{
		public struct RespawnData
		{
			public string PrefabName;

			public string Name;

			public string AppearanceSerialized;

			public string OwnerId;

			public int CombatStance;

			public long TombstoneId;

			public float Timer;
		}

		private const string BankDataKey = "TraderSharedBank_Balance";

		private static readonly List<RespawnData> _respawnQueue = new List<RespawnData>();

		public static int GetBankBalance()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return 0;
			}
			if (localPlayer.m_customData.TryGetValue("TraderSharedBank_Balance", out var value) && int.TryParse(value, out var result))
			{
				return result;
			}
			return 0;
		}

		public static bool CanAfford()
		{
			return GetBankBalance() >= 2000;
		}

		public static bool Purchase(CompanionAppearance appearance, CompanionTierDef def = null)
		{
			if (def == null)
			{
				def = CompanionTierData.Companion;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			int num = 2000;
			int bankBalance = GetBankBalance();
			if (bankBalance < num)
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, $"Not enough coins in bank! Need {num:N0}", 0, (Sprite)null, false);
				}
				return false;
			}
			bankBalance -= num;
			localPlayer.m_customData["TraderSharedBank_Balance"] = bankBalance.ToString();
			if (!SpawnCompanion(appearance, def))
			{
				bankBalance += num;
				localPlayer.m_customData["TraderSharedBank_Balance"] = bankBalance.ToString();
				return false;
			}
			MessageHud instance2 = MessageHud.instance;
			if (instance2 != null)
			{
				instance2.ShowMessage((MessageType)2, "Your new " + def.DisplayName.ToLower() + " has arrived!", 0, (Sprite)null, false);
			}
			return true;
		}

		public static void RestoreFollowTargets()
		{
			CompanionSetup[] array = Object.FindObjectsByType<CompanionSetup>((FindObjectsSortMode)0);
			for (int i = 0; i < array.Length; i++)
			{
				array[i].RestoreFollowTarget();
			}
		}

		public static void QueueRespawn(RespawnData data)
		{
			_respawnQueue.Add(data);
			CompanionsPlugin.Log.LogInfo((object)$"[CompanionManager] Queued respawn for \"{data.Name}\" in {data.Timer}s");
		}

		public static void ProcessRespawns(float dt)
		{
			if (_respawnQueue.Count == 0)
			{
				return;
			}
			for (int num = _respawnQueue.Count - 1; num >= 0; num--)
			{
				RespawnData respawnData = _respawnQueue[num];
				respawnData.Timer -= dt;
				_respawnQueue[num] = respawnData;
				if (respawnData.Timer <= 0f)
				{
					_respawnQueue.RemoveAt(num);
					DoRespawn(respawnData);
				}
			}
		}

		private static void DoRespawn(RespawnData data)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00df: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			CompanionsPlugin.Log.LogInfo((object)("[CompanionManager] Processing respawn for \"" + data.Name + "\" — prefab=" + data.PrefabName));
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				data.Timer = 1f;
				_respawnQueue.Add(data);
				CompanionsPlugin.Log.LogDebug((object)"[CompanionManager] Player not loaded — re-queued respawn");
				return;
			}
			Vector3 worldSpawnPoint = GetWorldSpawnPoint();
			CompanionsPlugin.Log.LogDebug((object)$"[CompanionManager] World spawn point: {worldSpawnPoint:F1}");
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.GetPrefab(data.PrefabName) : null);
			if ((Object)(object)val == (Object)null)
			{
				CompanionsPlugin.Log.LogError((object)("[CompanionManager] Respawn failed — prefab not found: " + data.PrefabName));
				return;
			}
			Vector3 val2 = FindSpawnPosition(worldSpawnPoint, 4f);
			Quaternion val3 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
			GameObject val4 = Object.Instantiate<GameObject>(val, val2, val3);
			ZNetView component = val4.GetComponent<ZNetView>();
			if (((component != null) ? component.GetZDO() : null) == null)
			{
				CompanionsPlugin.Log.LogError((object)"[CompanionManager] Respawn failed — ZDO not available.");
				Object.Destroy((Object)(object)val4);
				return;
			}
			ZDO zDO = component.GetZDO();
			zDO.Set(CompanionSetup.AppearanceHash, data.AppearanceSerialized);
			zDO.Set(CompanionSetup.OwnerHash, data.OwnerId);
			zDO.Set(CompanionSetup.NameHash, data.Name);
			zDO.Set(CompanionSetup.CombatStanceHash, data.CombatStance, false);
			zDO.Set(CompanionSetup.TombstoneIdHash, data.TombstoneId);
			zDO.Set(CompanionSetup.FollowHash, true);
			zDO.Set(CompanionSetup.ActionModeHash, 0, false);
			zDO.Set(CompanionSetup.StayHomeHash, false);
			zDO.Persistent = true;
			CompanionAppearance a = CompanionAppearance.Deserialize(data.AppearanceSerialized);
			CompanionSetup component2 = val4.GetComponent<CompanionSetup>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.ApplyAppearance(a);
			}
			Character component3 = val4.GetComponent<Character>();
			if ((Object)(object)component3 != (Object)null && !string.IsNullOrEmpty(data.Name))
			{
				component3.m_name = data.Name;
			}
			CompanionAI component4 = val4.GetComponent<CompanionAI>();
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				component4?.SetFollowTarget(((Component)Player.m_localPlayer).gameObject);
			}
			if ((Object)(object)component4 != (Object)null)
			{
				component4.FreezeTimer = 5f;
			}
			CompanionsPlugin.Log.LogInfo((object)($"[CompanionManager] Respawned \"{data.Name}\" at world spawn {val2:F1} — " + $"tombstoneId={data.TombstoneId}"));
			MessageHud instance2 = MessageHud.instance;
			if (instance2 != null)
			{
				instance2.ShowMessage((MessageType)2, data.Name + " has returned at the world spawn!", 0, (Sprite)null, false);
			}
		}

		private static Vector3 GetWorldSpawnPoint()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetLocationIcon(Game.instance.m_StartLocation, ref val))
			{
				return val + Vector3.up * 2f;
			}
			return Vector3.up * 2f;
		}

		public static void SpawnStarterCompanion()
		{
			if (!CompanionsPlugin.SpawnStarterCompanion.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			string key = $"HC_StarterCompanion_{ZNet.instance.GetWorldUID()}";
			if (localPlayer.m_customData.ContainsKey(key))
			{
				return;
			}
			CompanionAppearance companionAppearance = CompanionAppearance.Default();
			companionAppearance.ModelIndex = Random.Range(0, 2);
			if (SpawnCompanion(companionAppearance, CompanionTierData.Companion))
			{
				localPlayer.m_customData[key] = "1";
				CompanionsPlugin.Log.LogInfo((object)"[CompanionManager] Starter companion spawned!");
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, "A companion has joined you on your journey!", 0, (Sprite)null, false);
				}
			}
		}

		private static bool SpawnCompanion(CompanionAppearance appearance, CompanionTierDef def)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.GetPrefab(def.PrefabName) : null);
			if ((Object)(object)val == (Object)null)
			{
				CompanionsPlugin.Log.LogError((object)("[CompanionManager] Prefab not found: " + def.PrefabName));
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			Vector3 val2 = FindSpawnPosition(((Component)localPlayer).transform.position, 4f);
			Quaternion val3 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
			GameObject val4 = Object.Instantiate<GameObject>(val, val2, val3);
			ZNetView component = val4.GetComponent<ZNetView>();
			if (((component != null) ? component.GetZDO() : null) == null)
			{
				CompanionsPlugin.Log.LogError((object)"[CompanionManager] ZDO not available after spawn — aborting.");
				Object.Destroy((Object)(object)val4);
				return false;
			}
			ZDO zDO = component.GetZDO();
			zDO.Set(CompanionSetup.AppearanceHash, appearance.Serialize());
			zDO.Set(CompanionSetup.OwnerHash, localPlayer.GetPlayerID().ToString());
			zDO.Persistent = true;
			CompanionSetup component2 = val4.GetComponent<CompanionSetup>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.ApplyAppearance(appearance);
			}
			val4.GetComponent<CompanionAI>()?.SetFollowTarget(((Component)localPlayer).gameObject);
			CompanionsPlugin.Log.LogInfo((object)$"[CompanionManager] Spawned companion for player {localPlayer.GetPlayerID()}");
			return true;
		}

		private static Vector3 FindSpawnPosition(Vector3 origin, float radius)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			float y = default(float);
			for (int i = 0; i < 20; i++)
			{
				Vector2 val = Random.insideUnitCircle * radius;
				Vector3 val2 = origin + new Vector3(val.x, 0f, val.y);
				if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(val2, ref y))
				{
					val2.y = y;
					return val2;
				}
			}
			return origin + Vector3.right * 2f;
		}
	}
	[Serializable]
	public class SpeechConfig
	{
		public string[] Action;

		public string[] Gather;

		public string[] Forage;

		public string[] Combat;

		public string[] Follow;

		public string[] Hungry;

		public string[] Repair;

		public string[] Overweight;

		public string[] Smelt;

		public string[] Idle;

		private static SpeechConfig _instance;

		public static SpeechConfig Instance
		{
			get
			{
				if (_instance == null)
				{
					Load();
				}
				return _instance;
			}
		}

		public static void Load()
		{
			string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "speech.json");
			if (File.Exists(text))
			{
				try
				{
					_instance = JsonUtility.FromJson<SpeechConfig>(File.ReadAllText(text));
					CompanionsPlugin.Log.LogInfo((object)("[Speech] Loaded " + text));
					return;
				}
				catch (Exception ex)
				{
					CompanionsPlugin.Log.LogWarning((object)("[Speech] Failed to parse " + text + ": " + ex.Message + " — using defaults"));
				}
			}
			_instance = Defaults();
			try
			{
				string contents = JsonUtility.ToJson((object)_instance, true);
				File.WriteAllText(text, contents);
				CompanionsPlugin.Log.LogInfo((object)("[Speech] Created default " + text));
			}
			catch (Exception ex2)
			{
				CompanionsPlugin.Log.LogWarning((object)("[Speech] Failed to write defaults to " + text + ": " + ex2.Message));
			}
		}

		private static SpeechConfig Defaults()
		{
			SpeechConfig speechConfig = new SpeechConfig();
			speechConfig.Action = new string[5] { "By your word!", "So it shall be.", "The gods guide my hand.", "Aye, consider it done!", "I heed your call." };
			speechConfig.Gather = new string[6] { "The land provides, if you know where to look.", "Good timber here.", "Odin's bounty is plentiful.", "I'll strip this land bare if I must.", "These arms were made for more than swinging axes... but it'll do.", "The earth yields its riches." };
			speechConfig.Forage = new string[5] { "The meadows offer their gifts.", "Freya's garden blooms well here.", "Even warriors must forage.", "Ripe for the picking.", "The wild provides for those who seek." };
			speechConfig.Combat = new string[7] { "For Odin!", "Taste my steel!", "To Valhalla!", "Stand and fight!", "Skål! Come meet your end!", "They shall not pass!", "By Thor's hammer!" };
			speechConfig.Follow = new string[6] { "Lead on, I am your shield.", "Where the path takes us, I follow.", "A fine day to wander the wilds.", "My blade is yours to command.", "The road calls to us.", "I walk beside you, friend." };
			speechConfig.Hungry = new string[5] { "My belly roars like a troll...", "Even Fenrir ate better than this.", "A warrior fights on mead and meat, not empty guts.", "I'd trade my axe for a leg of boar right now.", "The hunger gnaws at my strength..." };
			speechConfig.Repair = new string[5] { "This blade has seen better days.", "My armor holds by thread and prayer.", "A dull edge brings a swift death.", "The smithy calls to my gear.", "Even the gods mend their weapons." };
			speechConfig.Overweight = new string[5] { "By Odin's beard, my back is breaking!", "I carry the weight of Jotunheim on my shoulders...", "Not even Thor could haul this much further!", "My legs buckle beneath this burden!", "We must lighten this load before I collapse." };
			speechConfig.Smelt = new string[6] { "The forge fire burns bright.", "Good ore makes good steel.", "The bellows sing their song.", "Another ingot for the hoard.", "Dwarven work, this smelting.", "The flames hunger for more." };
			speechConfig.Idle = new string[6] { "The winds whisper of adventure...", "Skål!", "A calm before the storm, perhaps.", "I could use a horn of mead.", "I sense something stirring in the mist.", "What tales will they sing of us, I wonder?" };
			return speechConfig;
		}
	}
	public static class CombatPatches
	{
		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		private static class BlockAttack_Patch
		{
			private static void Prefix(Humanoid __instance, HitData hit, Character attacker)
			{
				if (!((Object)(object)((Component)__instance).GetComponent<CompanionSetup>() == (Object)null))
				{
					float blockTimer = ReflectionHelper.GetBlockTimer(__instance);
					string arg = (((Object)(object)attacker != (Object)null) ? attacker.m_name : "unknown");
					float num = ((hit != null) ? hit.GetTotalDamage() : 0f);
					if (blockTimer >= 0f)
					{
						CompanionsPlugin.Log.LogDebug((object)($"[Combat] PARRY — BlockAttack fired! timer={blockTimer:F3}→0 " + $"attacker=\"{arg}\" dmg={num:F0} " + "companion=\"" + ((Character)__instance).m_name + "\""));
						ReflectionHelper.TrySetBlockTimer(__instance, 0f);
					}
					else
					{
						CompanionsPlugin.Log.LogWarning((object)($"[Combat] BlockAttack fired but timer={blockTimer:F3} (not blocking?) " + $"attacker=\"{arg}\" dmg={num:F0} " + "companion=\"" + ((Character)__instance).m_name + "\""));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "GetMaxHealth")]
		private static class GetMaxHealth_Patch
		{
			private static void Postfix(Character __instance, ref float __result)
			{
				BaseAI baseAI = __instance.GetBaseAI();
				if ((Object)(object)baseAI == (Object)null || !(baseAI is CompanionAI))
				{
					return;
				}
				CompanionFood component = ((Component)__instance).GetComponent<CompanionFood>();
				if (!((Object)(object)component == (Object)null))
				{
					float num = 25f;
					float totalHealthBonus = component.TotalHealthBonus;
					float num2 = num + totalHealthBonus;
					if (Time.time - _lastHealthLogTime > 5f)
					{
						_lastHealthLogTime = Time.time;
						CompanionsPlugin.Log.LogDebug((object)($"[Combat] GetMaxHealth — base={num:F0} + food={totalHealthBonus:F1} " + $"= {num2:F1} (vanilla was {__result:F1}) " + "companion=\"" + __instance.m_name + "\""));
					}
					__result = num2;
				}
			}
		}

		private static float _lastHealthLogTime;

		private const float HealthLogInterval = 5f;
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class DirectedTargetPatch
	{
		private static float _cooldown;

		private static bool _holdActive;

		private static float _holdTimer;

		private static bool _holdFired;

		private const float HoldThreshold = 0.4f;

		private static readonly string[] ComeHereLines = new string[3] { "Coming!", "On my way back!", "Right behind you." };

		private static readonly string[] AttackLines = new string[4] { "On it!", "Going in!", "I'll take them down!", "For Odin!" };

		private static readonly string[] CartPullLines = new string[3] { "I'll haul this.", "Got the cart!", "Let me pull." };

		private static readonly string[] CartReleasedLines = new string[3] { "Letting go.", "Cart's free.", "Released!" };

		private static readonly string[] DoorLines = new string[3] { "Getting the door.", "I'll get it.", "Door's open!" };

		private static readonly string[] SitLines = new string[3] { "Nice and warm.", "Good spot to rest.", "I'll sit here." };

		private static readonly string[] SleepLines = new string[3] { "Time for some rest.", "I could use some sleep.", "Wake me if you need me." };

		private static readonly string[] WakeLines = new string[3] { "I'm up!", "Already?", "Right, let's go." };

		private static readonly string[] DepositLines = new string[3] { "Dropping off my haul.", "Storing the goods.", "Lightening my load." };

		private static readonly string[] DepositEmptyLines = new string[2] { "I've got nothing to drop off.", "Already empty." };

		private static readonly string[] HarvestLines = new string[3] { "I'll get that.", "On it!", "Looks like good stuff." };

		private static readonly string[] CancelLines = new string[3] { "Standing by.", "Awaiting orders.", "Ready when you are." };

		private static readonly string[] MoveLines = new string[3] { "Heading over.", "On my way.", "Moving out." };

		private static readonly string[] RepairLines = new string[3] { "I'll fix my gear up.", "Time for repairs.", "This needs some work." };

		private static readonly string[] BoardLines = new string[3] { "Coming aboard!", "All aboard!", "I'll hop on." };

		private static readonly string[] RepairNothingLines = new string[3] { "Nothing to fix here.", "My gear's fine.", "No repairs needed." };

		private static void Postfix(Player __instance)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_054c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || InventoryGui.IsVisible() || Minimap.IsOpen() || Menu.IsVisible() || TextInput.IsVisible() || Console.IsVisible() || StoreGui.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Hud.IsPieceSelectionVisible())
			{
				return;
			}
			float deltaTime = Time.deltaTime;
			_cooldown -= deltaTime;
			bool flag = ZInput.IsGamepadActive();
			bool flag2 = (flag ? ZInput.GetButtonDown("JoyUse") : Input.GetKeyDown(CompanionsPlugin.DirectTargetKey.Value));
			bool flag3 = (flag ? ZInput.GetButton("JoyUse") : Input.GetKey(CompanionsPlugin.DirectTargetKey.Value));
			if (flag && (flag2 || flag3) && ((Object)(object)((Humanoid)__instance).GetHoverObject() != (Object)null || Hud.InRadial()))
			{
				_holdActive = false;
				_holdTimer = 0f;
				return;
			}
			if (flag2 && _cooldown <= 0f)
			{
				_holdActive = true;
				_holdTimer = 0f;
				_holdFired = false;
			}
			if (_holdActive && flag3)
			{
				_holdTimer += deltaTime;
				if (_holdTimer >= 0.4f && !_holdFired)
				{
					_holdFired = true;
					_cooldown = 0.5f;
					FireComeToMe(__instance, flag);
				}
			}
			else
			{
				if (!_holdActive)
				{
					return;
				}
				_holdActive = false;
				if (_holdFired || _cooldown > 0f)
				{
					return;
				}
				_cooldown = 0.5f;
				string text = (flag ? "gamepad" : "keyboard");
				CompanionsPlugin.Log.LogDebug((object)("[Direct] Command triggered via " + text));
				Camera mainCamera = Utils.GetMainCamera();
				if ((Object)(object)mainCamera == (Object)null)
				{
					return;
				}
				Ray val = default(Ray);
				((Ray)(ref val))..ctor(((Component)mainCamera).transform.position, ((Component)mainCamera).transform.forward);
				int mask = LayerMask.GetMask(new string[12]
				{
					"character", "character_net", "character_ghost", "character_noenv", "Default", "Default_small", "piece", "piece_nonsolid", "static_solid", "terrain",
					"vehicle", "item"
				});
				RaycastHit[] array = Physics.RaycastAll(val, 50f, mask);
				if (array.Length > 1)
				{
					Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				}
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] Raycast hits={array.Length}, pos={((Component)mainCamera).transform.position:F1}, dir={((Component)mainCamera).transform.forward:F2}");
				CompanionSetup[] setups = Object.FindObjectsByType<CompanionSetup>((FindObjectsSortMode)0);
				string localId = __instance.GetPlayerID().ToString();
				for (int i = 0; i < array.Length; i++)
				{
					Collider collider = ((RaycastHit)(ref array[i])).collider;
					if ((Object)(object)collider == (Object)null)
					{
						continue;
					}
					CompanionsPlugin.Log.LogDebug((object)($"[Direct] Hit[{i}]: \"{((Object)((Component)collider).gameObject).name}\" dist={((RaycastHit)(ref array[i])).distance:F2} " + "layer=" + LayerMask.LayerToName(((Component)collider).gameObject.layer) + " " + $"pos={((RaycastHit)(ref array[i])).point:F2}"));
					Character componentInParent = ((Component)collider).GetComponentInParent<Character>();
					if ((Object)(object)componentInParent != (Object)null)
					{
						if (!((Object)(object)componentInParent == (Object)(object)__instance) && !((Object)(object)((Component)componentInParent).GetComponent<CompanionSetup>() != (Object)null) && !componentInParent.IsDead() && BaseAI.IsEnemy((Character)(object)__instance, componentInParent))
						{
							DirectAttack(setups, localId, componentInParent);
							return;
						}
						continue;
					}
					Vagon componentInParent2 = ((Component)collider).GetComponentInParent<Vagon>();
					if ((Object)(object)componentInParent2 != (Object)null)
					{
						DirectCart(setups, localId, componentInParent2);
						return;
					}
					Door componentInParent3 = ((Component)collider).GetComponentInParent<Door>();
					if ((Object)(object)componentInParent3 != (Object)null)
					{
						DirectDoor(setups, localId, componentInParent3);
						return;
					}
					Fireplace componentInParent4 = ((Component)collider).GetComponentInParent<Fireplace>();
					if ((Object)(object)componentInParent4 != (Object)null)
					{
						DirectSit(setups, localId, componentInParent4);
						return;
					}
					Bed componentInParent5 = ((Component)collider).GetComponentInParent<Bed>();
					if ((Object)(object)componentInParent5 != (Object)null)
					{
						DirectSleep(setups, localId, componentInParent5);
						return;
					}
					CraftingStation componentInParent6 = ((Component)collider).GetComponentInParent<CraftingStation>();
					if ((Object)(object)componentInParent6 != (Object)null)
					{
						DirectRepair(setups, localId, componentInParent6);
						return;
					}
					Ship componentInParent7 = ((Component)collider).GetComponentInParent<Ship>();
					if ((Object)(object)componentInParent7 != (Object)null)
					{
						DirectBoard(setups, localId, componentInParent7);
						return;
					}
					Container componentInParent8 = ((Component)collider).GetComponentInParent<Container>();
					if ((Object)(object)componentInParent8 != (Object)null && (Object)(object)((Component)componentInParent8).GetComponent<CompanionSetup>() == (Object)null)
					{
						DirectDeposit(setups, localId, componentInParent8);
						return;
					}
					GameObject harvestable = GetHarvestable(collider);
					if ((Object)(object)harvestable != (Object)null)
					{
						DirectGatherMode(setups, localId, harvestable);
						return;
					}
					int layer = ((Component)collider).gameObject.layer;
					if (layer == LayerMask.NameToLayer("terrain") || layer == LayerMask.NameToLayer("Default") || layer == LayerMask.NameToLayer("static_solid") || layer == LayerMask.NameToLayer("piece") || layer == LayerMask.NameToLayer("piece_nonsolid"))
					{
						DirectGround(setups, localId, ((RaycastHit)(ref array[i])).point);
						return;
					}
				}
				CancelAll(setups, localId);
			}
		}

		private static void FireComeToMe(Player player, bool isGamepad)
		{
			CompanionSetup[] array = Object.FindObjectsByType<CompanionSetup>((FindObjectsSortMode)0);
			string localId = player.GetPlayerID().ToString();
			CancelAll(array, localId);
			CompanionTalk companionTalk = null;
			CompanionSetup[] array2 = array;
			foreach (CompanionSetup companionSetup in array2)
			{
				if (IsOwned(companionSetup, localId))
				{
					companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					if ((Object)(object)companionTalk != (Object)null)
					{
						break;
					}
				}
			}
			SayRandom(companionTalk, ComeHereLines, "Action");
			string text = (isGamepad ? "gamepad hold" : "keyboard hold");
			CompanionsPlugin.Log.LogDebug((object)("[Direct] Come-to-me triggered via " + text));
		}

		private static void DirectAttack(CompanionSetup[] setups, string localId, Character enemy)
		{
			int num = 0;
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CompanionAI component = ((Component)companionSetup).GetComponent<CompanionAI>();
				if (!((Object)(object)component == (Object)null))
				{
					CancelExistingActions(companionSetup);
					component.m_targetCreature = enemy;
					component.SetAlerted(alert: true);
					component.DirectedTargetLockTimer = 10f;
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, AttackLines, "Combat");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → attack \"{enemy.m_name}\"");
		}

		private static void DirectCart(CompanionSetup[] setups, string localId, Vagon vagon)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: 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_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			CompanionsPlugin.Log.LogDebug((object)("[Direct] DirectCart called — vagon=\"" + ((Object)vagon).name + "\" " + $"attachPoint={vagon.m_attachPoint.position:F2} " + $"attachOffset={vagon.m_attachOffset:F2} " + $"detachDist={vagon.m_detachDistance:F2}"));
			CompanionSetup[] array = setups;
			foreach (CompanionSetup companionSetup in array)
			{
				if (!IsOwned(companionSetup, localId))
				{
					continue;
				}
				Character component = ((Component)companionSetup).GetComponent<Character>();
				bool flag = (Object)(object)component != (Object)null && vagon.IsAttached(component);
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] Cart detach check: \"{component?.m_name}\" attached={flag}");
				if (!flag)
				{
					continue;
				}
				Humanoid component2 = ((Component)companionSetup).GetComponent<Humanoid>();
				if ((Object)(object)component2 != (Object)null)
				{
					vagon.Interact(component2, false, false);
					CompanionAI component3 = ((Component)companionSetup).GetComponent<CompanionAI>();
					if ((Object)(object)component3 != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
					{
						component3.SetFollowTarget(((Component)Player.m_localPlayer).gameObject);
					}
					SayRandom(((Component)companionSetup).GetComponent<CompanionTalk>(), CartReleasedLines, "Action");
					CompanionsPlugin.Log.LogDebug((object)"[Direct] Companion → detach from cart");
				}
				return;
			}
			CompanionSetup companionSetup2 = null;
			float num = float.MaxValue;
			array = setups;
			foreach (CompanionSetup companionSetup3 in array)
			{
				if (IsOwned(companionSetup3, localId) && companionSetup3.GetIsCommandable())
				{
					float num2 = Vector3.Distance(((Component)companionSetup3).transform.position, ((Component)vagon).transform.position);
					if (num2 < num)
					{
						num = num2;
						companionSetup2 = companionSetup3;
					}
				}
			}
			if ((Object)(object)companionSetup2 == (Object)null)
			{
				CompanionsPlugin.Log.LogWarning((object)"[Direct] DirectCart — no commandable companion found");
				return;
			}
			Humanoid component4 = ((Component)companionSetup2).GetComponent<Humanoid>();
			if ((Object)(object)component4 == (Object)null)
			{
				return;
			}
			CompanionAI component5 = ((Component)companionSetup2).GetComponent<CompanionAI>();
			if ((Object)(object)component5 == (Object)null)
			{
				return;
			}
			CancelExistingActions(companionSetup2);
			Vector3 val = vagon.m_attachPoint.position - vagon.m_attachOffset;
			val.y = ((Component)companionSetup2).transform.position.y;
			float num3 = Vector3.Distance(((Component)companionSetup2).transform.position, val);
			if (num3 < 3f)
			{
				((Component)companionSetup2).transform.position = val;
				Rigidbody component6 = ((Component)companionSetup2).GetComponent<Rigidbody>();
				if ((Object)(object)component6 != (Object)null)
				{
					component6.position = val;
					component6.velocity = Vector3.zero;
				}
				Vector3 val2 = ((Component)vagon).transform.position - ((Component)companionSetup2).transform.position;
				val2.y = 0f;
				if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
				{
					((Component)companionSetup2).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized);
				}
				component5.FreezeTimer = 1f;
				component5.SetFollowTarget(((Component)vagon).gameObject);
				vagon.Interact(component4, false, false);
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] Cart close snap — pos={((Component)companionSetup2).transform.position:F2}");
			}
			else
			{
				component5.SetPendingCart(vagon, component4);
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] Cart navigation started — dist={num3:F1}m");
			}
			SayRandom(((Component)companionSetup2).GetComponent<CompanionTalk>(), CartPullLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] Companion → cart attach (dist={num:F1}m)");
		}

		private static void DirectDoor(CompanionSetup[] setups, string localId, Door door)
		{
			int num = 0;
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup);
				DoorHandler component = ((Component)companionSetup).GetComponent<DoorHandler>();
				if (!((Object)(object)component == (Object)null))
				{
					component.DirectOpenDoor(door);
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, DoorLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → open door \"{door.m_name}\"");
		}

		private static void DirectSit(CompanionSetup[] setups, string localId, Fireplace fire)
		{
			if (!fire.IsBurning())
			{
				return;
			}
			int num = 0;
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup, cancelRest: false);
				CompanionRest component = ((Component)companionSetup).GetComponent<CompanionRest>();
				if (!((Object)(object)component == (Object)null))
				{
					component.DirectSit(((Component)fire).gameObject);
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, SitLines, "Idle");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → sit near fire");
		}

		private static void DirectSleep(CompanionSetup[] setups, string localId, Bed bed)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource log = CompanionsPlugin.Log;
			string[] obj = new string[6]
			{
				"[Direct] DirectSleep called — bed=\"",
				((Object)bed).name,
				"\" ",
				$"pos={((Component)bed).transform.position:F2} ",
				"spawnPoint=",
				null
			};
			Transform spawnPoint = bed.m_spawnPoint;
			object obj2;
			if (spawnPoint == null)
			{
				obj2 = null;
			}
			else
			{
				Vector3 position = spawnPoint.position;
				obj2 = ((Vector3)(ref position)).ToString("F2");
			}
			if (obj2 == null)
			{
				obj2 = "null";
			}
			obj[5] = (string)obj2;
			log.LogDebug((object)string.Concat(obj));
			int num = 0;
			int num2 = 0;
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup, cancelRest: false);
				CompanionRest component = ((Component)companionSetup).GetComponent<CompanionRest>();
				if (!((Object)(object)component == (Object)null))
				{
					bool flag = component.IsResting || component.IsNavigating;
					component.DirectSleep(bed);
					bool flag2 = component.IsResting || component.IsNavigating;
					if (!flag && flag2)
					{
						num++;
					}
					else if (flag && !flag2)
					{
						num2++;
					}
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
				}
			}
			if (num2 > 0)
			{
				SayRandom(companionTalk, WakeLines, "Action");
			}
			else if (num > 0)
			{
				SayRandom(companionTalk, SleepLines, "Idle");
			}
			else
			{
				SayRandom(companionTalk, CancelLines, "Action");
			}
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] Sleep command — started={num}, wokeUp={num2}");
		}

		private static void DirectDeposit(CompanionSetup[] setups, string localId, Container chest)
		{
			if (chest.GetInventory() == null)
			{
				return;
			}
			int num = 0;
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				Humanoid component = ((Component)companionSetup).GetComponent<Humanoid>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				Inventory inventory = component.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				bool flag = false;
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (!ShouldKeep(allItem, component))
					{
						flag = true;
						break;
					}
				}
				if ((Object)(object)companionTalk == (Object)null)
				{
					companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
				}
				if (flag)
				{
					CancelExistingActions(companionSetup);
					CompanionAI component2 = ((Component)companionSetup).GetComponent<CompanionAI>();
					if (!((Object)(object)component2 == (Object)null))
					{
						component2.SetPendingDeposit(chest, component);
						num++;
					}
				}
			}
			if (num > 0)
			{
				SayRandom(companionTalk, DepositLines, "Action");
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] Dispatched {num} companion(s) to deposit in \"{chest.m_name}\"");
			}
			else
			{
				SayRandom(companionTalk, DepositEmptyLines, "Action");
				CompanionsPlugin.Log.LogDebug((object)"[Direct] No companions had items to deposit");
			}
		}

		private static void DirectRepair(CompanionSetup[] setups, string localId, CraftingStation station)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			CompanionsPlugin.Log.LogDebug((object)("[Direct] DirectRepair called — station=\"" + station.m_name + "\" " + $"level={station.GetLevel(true)} pos={((Component)station).transform.position:F1}"));
			CompanionTalk companionTalk = null;
			int num = 0;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				RepairController component = ((Component)companionSetup).GetComponent<RepairController>();
				if (!((Object)(object)component == (Object)null))
				{
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					CancelExistingActions(companionSetup);
					if (component.DirectRepairAt(station))
					{
						num++;
					}
				}
			}
			if (num > 0)
			{
				SayRandom(companionTalk, RepairLines, "Repair");
				CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → repair at \"{station.m_name}\"");
			}
			else
			{
				SayRandom(companionTalk, RepairNothingLines, "Repair");
				CompanionsPlugin.Log.LogDebug((object)("[Direct] No companions can repair at \"" + station.m_name + "\""));
			}
		}

		private static void DirectBoard(CompanionSetup[] setups, string localId, Ship ship)
		{
			//IL_0020: 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_0099: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00ce: 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_00f3: 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)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			CompanionsPlugin.Log.LogDebug((object)("[Direct] DirectBoard called — ship=\"" + ((Object)ship).name + "\" " + $"pos={((Component)ship).transform.position:F2} up={((Component)ship).transform.up:F2}"));
			CompanionTalk companionTalk = null;
			int num = 0;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup);
				CompanionAI component = ((Component)companionSetup).GetComponent<CompanionAI>();
				if (!((Object)(object)component == (Object)null))
				{
					Vector3 position = ((Component)companionSetup).transform.position;
					Vector3 val = ((Component)ship).transform.position + ((Component)ship).transform.up * 1.5f;
					((Component)companionSetup).transform.position = val;
					Rigidbody component2 = ((Component)companionSetup).GetComponent<Rigidbody>();
					if ((Object)(object)component2 != (Object)null)
					{
						component2.position = val;
						component2.velocity = Vector3.zero;
					}
					component.SetFollowTarget(((Component)ship).gameObject);
					component.FreezeTimer = 1f;
					Character component3 = ((Component)companionSetup).GetComponent<Character>();
					CompanionsPlugin.Log.LogDebug((object)("[Direct] Board: \"" + (component3?.m_name ?? "?") + "\" " + $"teleported {position:F1} → {val:F1}"));
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, BoardLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → board ship");
		}

		private static void DirectGatherMode(CompanionSetup[] setups, string localId, GameObject target)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			int num = HarvestController.DetermineHarvestModeStatic(target);
			if (num < 0)
			{
				CompanionsPlugin.Log.LogDebug((object)("[Direct] DirectGatherMode — target \"" + ((Object)target).name + "\" is not harvestable (mode=-1)"));
				return;
			}
			string arg = num switch
			{
				2 => "Stone", 
				1 => "Wood", 
				_ => "Ore", 
			};
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] DirectGatherMode — target=\"{((Object)target).name}\" mode={arg} pos={target.transform.position:F1}");
			CompanionTalk companionTalk = null;
			int num2 = 0;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup);
				ZNetView component = ((Component)companionSetup).GetComponent<ZNetView>();
				if (((component != null) ? component.GetZDO() : null) != null)
				{
					component.GetZDO().Set(CompanionSetup.ActionModeHash, num, false);
					HarvestController component2 = ((Component)companionSetup).GetComponent<HarvestController>();
					if ((Object)(object)component2 != (Object)null && num2 == 0)
					{
						component2.SetDirectedTarget(target);
					}
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num2++;
				}
			}
			SayRandom(companionTalk, HarvestLines, "Gather");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num2} companion(s) → gather mode {arg}");
		}

		private static void DirectGround(CompanionSetup[] setups, string localId, Vector3 point)
		{
			//IL_000a: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] DirectGround called — point={point:F1}");
			bool flag = false;
			CompanionSetup[] array = setups;
			foreach (CompanionSetup companionSetup in array)
			{
				if (IsOwned(companionSetup, localId) && companionSetup.GetIsCommandable())
				{
					HarvestController component = ((Component)companionSetup).GetComponent<HarvestController>();
					if ((Object)(object)component != (Object)null && component.IsInGatherMode)
					{
						flag = true;
						break;
					}
				}
			}
			if (flag)
			{
				CompanionsPlugin.Log.LogDebug((object)"[Direct] DirectGround — companion(s) in gather mode, exiting gather instead");
				ExitGatherMode(setups, localId);
				return;
			}
			CompanionTalk companionTalk = null;
			int num = 0;
			array = setups;
			foreach (CompanionSetup companionSetup2 in array)
			{
				if (!IsOwned(companionSetup2, localId) || !companionSetup2.GetIsCommandable())
				{
					continue;
				}
				CancelExistingActions(companionSetup2);
				CompanionAI component2 = ((Component)companionSetup2).GetComponent<CompanionAI>();
				if (!((Object)(object)component2 == (Object)null))
				{
					component2.SetMoveTarget(point);
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup2).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, MoveLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) → move to {point:F1}");
		}

		private static void ExitGatherMode(CompanionSetup[] setups, string localId)
		{
			CompanionTalk companionTalk = null;
			int num = 0;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId) || !companionSetup.GetIsCommandable())
				{
					continue;
				}
				ZNetView component = ((Component)companionSetup).GetComponent<ZNetView>();
				if (((component != null) ? component.GetZDO() : null) != null)
				{
					int @int = component.GetZDO().GetInt(CompanionSetup.ActionModeHash, 0);
					component.GetZDO().Set(CompanionSetup.ActionModeHash, 0, false);
					Character component2 = ((Component)companionSetup).GetComponent<Character>();
					CompanionsPlugin.Log.LogDebug((object)string.Format("[Direct] ExitGather: \"{0}\" mode {1} → {2}", component2?.m_name ?? "?", @int, 0));
					if ((Object)(object)companionTalk == (Object)null)
					{
						companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
					}
					num++;
				}
			}
			SayRandom(companionTalk, CancelLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)$"[Direct] {num} companion(s) exited gather mode → follow");
		}

		private static void CancelAll(CompanionSetup[] setups, string localId)
		{
			CompanionTalk companionTalk = null;
			foreach (CompanionSetup companionSetup in setups)
			{
				if (!IsOwned(companionSetup, localId))
				{
					continue;
				}
				if ((Object)(object)companionTalk == (Object)null)
				{
					companionTalk = ((Component)companionSetup).GetComponent<CompanionTalk>();
				}
				CompanionAI component = ((Component)companionSetup).GetComponent<CompanionAI>();
				if ((Object)(object)component != (Object)null)
				{
					component.DirectedTargetLockTimer = 0f;
					component.CancelPendingCart();
					component.CancelMoveTarget();
					component.CancelPendingDeposit();
					if ((Object)(object)Player.m_localPlayer != (Object)null && companionSetup.GetFollow())
					{
						component.SetFollowTarget(((Component)Player.m_localPlayer).gameObject);
					}
					else if (!companionSetup.GetFollow())
					{
						component.SetFollowTarget(null);
					}
				}
				ZNetView component2 = ((Component)companionSetup).GetComponent<ZNetView>();
				if (((component2 != null) ? component2.GetZDO() : null) != null && component2.GetZDO().GetInt(CompanionSetup.ActionModeHash, 0) != 0)
				{
					component2.GetZDO().Set(CompanionSetup.ActionModeHash, 0, false);
				}
				HarvestController component3 = ((Component)companionSetup).GetComponent<HarvestController>();
				if ((Object)(object)component3 != (Object)null)
				{
					component3.NotifyActionModeChanged();
				}
				CompanionRest component4 = ((Component)companionSetup).GetComponent<CompanionRest>();
				if ((Object)(object)component4 != (Object)null)
				{
					component4.CancelDirected();
				}
				RepairController component5 = ((Component)companionSetup).GetComponent<RepairController>();
				if ((Object)(object)component5 != (Object)null && component5.IsActive)
				{
					component5.CancelDirected();
				}
				SmeltController component6 = ((Component)companionSetup).GetComponent<SmeltController>();
				if ((Object)(object)component6 != (Object)null)
				{
					component6.NotifyActionModeChanged();
				}
			}
			SayRandom(companionTalk, CancelLines, "Action");
			CompanionsPlugin.Log.LogDebug((object)"[Direct] Cancelled all directed commands");
		}

		private static void SayRandom(CompanionTalk talk, string[] pool, string audioCategory = null)
		{
			if (!((Object)(object)talk == (Object)null) && pool != null && pool.Length != 0)
			{
				talk.Say(pool[Random.Range(0, pool.Length)], audioCategory);
			}
		}

		private static bool IsOwned(CompanionSetup setup, string localId)
		{
			ZNetView component = ((Component)setup).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || component.GetZDO() == null)
			{
				return false;
			}
			return component.GetZDO().GetString(CompanionSetup.OwnerHash, "") == localId;
		}

		private static void CancelExistingActions(CompanionSetup setup, bool cancelRest = true)
		{
			string text = ((Component)setup).GetComponent<Character>()?.m_name ?? "?";
			if (cancelRest)
			{
				CompanionRest component = ((Component)setup).GetComponent<CompanionRest>();
				if ((Object)(object)component != (Object)null && (component.IsResting || component.IsNavigating))
				{
					CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling rest " + $"(resting={component.IsResting} nav={component.IsNavigating})"));
					component.CancelDirected();
				}
			}
			CompanionAI component2 = ((Component)setup).GetComponent<CompanionAI>();
			if ((Object)(object)component2 != (Object)null)
			{
				if ((Object)(object)component2.PendingCartAttach != (Object)null)
				{
					CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling cart nav"));
				}
				component2.CancelPendingCart();
				if (component2.PendingMoveTarget.HasValue)
				{
					CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling move-to"));
				}
				component2.CancelMoveTarget();
				if ((Object)(object)component2.PendingDepositContainer != (Object)null)
				{
					CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling deposit nav"));
				}
				component2.CancelPendingDeposit();
				component2.DirectedTargetLockTimer = 0f;
			}
			HarvestController component3 = ((Component)setup).GetComponent<HarvestController>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.CancelDirectedTarget();
			}
			RepairController component4 = ((Component)setup).GetComponent<RepairController>();
			if ((Object)(object)component4 != (Object)null && component4.IsActive)
			{
				CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling active repair"));
				component4.CancelDirected();
			}
			SmeltController component5 = ((Component)setup).GetComponent<SmeltController>();
			if ((Object)(object)component5 != (Object)null && component5.IsActive)
			{
				CompanionsPlugin.Log.LogDebug((object)("[Direct] CancelExisting \"" + text + "\": cancelling active smelt"));
				component5.CancelDirected();
			}
		}

		internal static bool ShouldKeep(ItemData item, Humanoid humanoid)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Invalid comparison between Unknown and I4
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Invalid comparison between Unknown and I4
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Invalid comparison between Unknown and I4
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Invalid comparison between Unknown and I4
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Invalid comparison between Unknown and I4
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Invalid comparison between Unknown and I4
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return true;
			}
			if (humanoid.IsItemEquiped(item))
			{
				return true;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType == 2 && (item.m_shared.m_food > 0f || item.m_shared.m_foodStamina > 0f || item.m_shared.m_foodEitr > 0f))
			{
				return true;
			}
			if ((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22 || (int)itemType == 4 || (int)itemType == 15)
			{
				return true;
			}
			if ((int)itemType == 5 || (int)itemType == 6 || (int)itemType == 7 || (int)itemType == 11 || (int)itemType == 12 || (int)itemType == 17 || (int)itemType == 18)
			{
				return true;
			}
			return false;
		}

		private static GameObject GetHarvestable(Collider col)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Invalid comparison between Unknown and I4
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Invalid comparison between Unknown and I4
			TreeBase componentInParent = ((Component)col).GetComponentInParent<TreeBase>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return ((Component)componentInParent).gameObject;
			}
			TreeLog componentInParent2 = ((Component)col).GetComponentInParent<TreeLog>();
			if ((Object)(object)componentInParent2 != (Object)null)
			{
				return ((Component)componentInParent2).gameObject;
			}
			MineRock5 componentInParent3 = ((Component)col).GetComponentInParent<MineRock5>();
			if ((Object)(object)componentInParent3 != (Object)null)
			{
				return ((Component)componentInParent3).gameObject;
			}
			MineRock componentInParent4 = ((Component)col).GetComponentInParent<MineRock>();
			if ((Object)(object)componentInParent4 != (Object)null)
			{
				return ((Component)componentInParent4).gameObject;
			}
			Destructible componentInParent5 = ((Component)col).GetComponentInParent<Destructible>();
			if ((Object)(object)componentInParent5 != (Object)null)
			{
				if ((int)componentInParent5.m_damages.m_chop != 3 && ((Object)((Component)componentInParent5).gameObject).name.IndexOf("stub", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return ((Component)componentInParent5).gameObject;
				}
				if ((int)componentInParent5.m_damages.m_pickaxe != 3 && (int)componentInParent5.m_damages.m_chop == 3)
				{
					return ((Component)componentInParent5).gameObject;
				}
			}
			return null;
		}
	}
	public static class DurabilityPatches
	{
		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		private static class WeaponDurability_Patch
		{
			private static void Postfix(Humanoid __instance, bool __result)
			{
				if (!__result || (Object)(object)((Component)__instance).GetComponent<CompanionSetup>() == (Object)null)
				{
					return;
				}
				ItemData rightItem = ReflectionHelper.GetRightItem(__instance);
				if (rightItem != null && rightItem.m_shared.m_useDurability)
				{
					float durability = rightItem.m_durability;
					float useDurabilityDrain = rightItem.m_shared.m_useDurabilityDrain;
					rightItem.m_durability -= useDurabilityDrain;
					float maxDurability = rightItem.GetMaxDurability();
					float num = ((maxDurability > 0f) ? (rightItem.m_durability / maxDurability * 100f) : 0f);
					CompanionsPlugin.Log.LogDebug((object)("[Durability] Weapon drain — \"" + rightItem.m_shared.m_name + "\" " + $"durability {durability:F1} → {rightItem.m_durability:F1} / {maxDurability:F0} " + $"({num:F0}%) drain={useDurabilityDrain:F1} " + "companion=\"" + ((Character)__instance).m_name + "\""));
					if (rightItem.m_durability <= 0f)
					{
						rightItem.m_durability = 0f;
						__instance.UnequipItem(rightItem, false);
						CompanionsPlugin.Log.LogWarning((object)("[Durability] Weapon BROKEN — \"" + rightItem.m_shared.m_name + "\" unequipped, companion=\"" + ((Character)__instance).m_name + "\""));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class ArmorDurability_Patch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				CompanionSetup component = ((Component)__instance).GetComponent<CompanionSetup>();
				if (!((Object)(object)component == (Object)null))
				{
					float totalArmor = component.GetTotalArmor();
					if (totalArmor > 0f)
					{
						float totalDamage = hit.GetTotalDamage();
						hit.ApplyArmor(totalArmor);
						float totalDamage2 = hit.GetTotalDamage();
						CompanionsPlugin.Log.LogDebug((object)($"[Durability] Armor reduction — totalArmor={totalArmor:F1} " + $"dmg {totalDamage:F1} → {totalDamage2:F1} " + $"(blocked {totalDamage - totalDamage2:F1}) " + "companion=\"" + __instance.m_name + "\""));
					}
				}
			}

			private static void Postfix(Character __instance, HitData hit)
			{
				if ((Object)(object)((Component)__instance).GetComponent<CompanionSetup>() == (Object)null)
				{
					return;
				}
				Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				List<ItemData> list = new List<ItemData>(4);
				object? obj = _chestItem?.GetValue(val);
				AddIfValid(list, (ItemData)((obj is ItemData) ? obj : null));
				object? obj2 = _legItem?.GetValue(val);
				AddIfValid(list, (ItemData)((obj2 is ItemData) ? obj2 : null));
				object? obj3 = _helmetItem?.GetValue(val);
				AddIfValid(list, (ItemData)((obj3 is ItemData) ? obj3 : null));
				object? obj4 = _shoulderItem?.GetValue(val);
				AddIfValid(list, (ItemData)((obj4 is ItemData) ? obj4 : null));
				if (list.Count == 0)
				{
					CompanionsPlugin.Log.LogDebug((object)("[Durability] No armor with durability equipped — skipping drain companion=\"" + __instance.m_name + "\""));
					return;
				}
				float num = hit.GetTotalPhysicalDamage() + hit.GetTotalElementalDamage();
				if (!(num <= 0f))
				{
					ItemData val2 = list[Random.Range(0, list.Count)];
					float durability = val2.m_durability;
					val2.m_durability = Mathf.Max(0f, val2.m_durability - num);
					float maxDurability = val2.GetMaxDurability();
					float num2 = ((maxDurability > 0f) ? (val2.m_durability / maxDurability * 100f) : 0f);
					CompanionsPlugin.Log.LogDebug((object)("[Durability] Armor drain — \"" + val2.m_shared.m_name + "\" " + $"durability {durability:F1} → {val2.m_durability:F1} / {maxDurability:F0} " + $"({num2:F0}%) dmgTaken={num:F1} " + $"armorPieces={list.Count} companion=\"{__instance.m_name}\""));
					if (val2.m_durability <= 0f)
					{
						val.UnequipItem(val2, false);
						CompanionsPlugin.Log.LogWarning((object)("[Durability] Armor BROKEN — \"" + val2.m_shared.m_name + "\" unequipped, companion=\"" + __instance.m_name + "\""));
					}
				}
			}
		}

		private static readonly FieldInfo _chestItem = AccessTools.Field(typeof(Humanoid), "m_chestItem");

		private static readonly FieldInfo _legItem = AccessTools.Field(typeof(Humanoid), "m_legItem");

		private static readonly FieldInfo _helmetItem = AccessTools.Field(typeof(Humanoid), "m_helmetItem");

		private static readonly FieldInfo _shoulderItem = AccessTools.Field(typeof(Humanoid), "m_shoulderItem");

		private static void AddIfValid(List<ItemData> list, ItemData item)
		{
			if (item != null && item.m_shared != null && item.m_shared.m_useDurability)
			{
				list.Add(item);
			}
		}
	}
	public static class EnemyHudPatches
	{
		private struct CompanionBars
		{
			public GuiBar StaminaBar;

			public GuiBar WeightBar;
		}

		[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
		private static class ShowHud_Patch
		{
			private static void Postfix(EnemyHud __instance, Character c)
			{
				//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_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)c == (Object)null || (Object)(object)((Component)c).GetComponent<CompanionStamina>() == (Object)null)
				{
					return;
				}
				int instanceID = ((Object)c).GetInstanceID();
				if (_bars.ContainsKey(instanceID))
				{
					return;
				}
				IDictionary huds = GetHuds(__instance);
				if (huds == null || !huds.Contains(c))
				{
					return;
				}
				GameObject value = Traverse.Create(huds[c]).Field("m_gui").GetValue<GameObject>();
				if ((Object)(object)value == (Object)null)
				{
					return;
				}
				Transform val = value.transform.Find("Health");
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Rect rect = ((Component)val).GetComponent<RectTransform>().rect;
				float num = ((Rect)(ref rect)).height;
				if (num <= 0f)
				{
					num = 8f;
				}
				float num2 = num + 2f;
				GuiBar val2 = CreateBar(val, value.transform, "CompanionStamina", StaminaYellow, num2);
				float yOffset = num2 + num + 2f;
				GuiBar val3 = CreateBar(val, value.transform, "CompanionWeight", WeightBrown, yOffset);
				if ((Object)(object)val2 != (Object)null)
				{
					CompanionStamina component = ((Component)c).GetComponent<CompanionStamina>();
					val2.SetValue(component.GetStaminaPercentage());
				}
				if ((Object)(object)val3 != (Object)null)
				{
					Humanoid component2 = ((Component)c).GetComponent<Humanoid>();
					if ((Object)(object)component2 != (Object)null)
					{
						Inventory inventory = component2.GetInventory();
						float value2 = ((inventory != null) ? Mathf.Clamp01(inventory.GetTotalWeight() / 300f) : 0f);
						val3.SetValue(value2);
					}
				}
				_bars[instanceID] = new CompanionBars
				{
					StaminaBar = val2,
					WeightBar = val3
				};
				_barCharacters[instanceID] = c;
				CompanionsPlugin.Log.LogDebug((object)($"[HUD] Created companion bars — stamina={(Object)(object)val2 != (Object)null} " + $"weight={(Object)(object)val3 != (Object)null} companion=\"{c.m_name}\""));
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "OnDestroy")]
		private static class OnDestroy_Patch
		{
			private static void Postfix()
			{
				_bars.Clear();
				_barCharacters.Clear();
				_cachedHuds = null;
				_cachedHudInstance = null;
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		private static class UpdateHuds_Patch
		{
			private static void Postfix(EnemyHud __instance)
			{
				HideHudForRadialTarget(__instance);
				List<int> list = null;
				foreach (KeyValuePair<int, CompanionBars> bar in _bars)
				{
					_barCharacters.TryGetValue(bar.Key, out var value);
					if ((Object)(object)value == (Object)null || ((Object)(object)bar.Value.StaminaBar == (Object)null && (Object)(object)bar.Value.WeightBar == (Object)null))
					{
						if (list == null)
						{
							list = new List<int>();
						}
						list.Add(bar.Key);
						continue;
					}
					CompanionBars value2 = bar.Value;
					if ((Object)(object)value2.StaminaBar != (Object)null)
					{
						CompanionStamina component = ((Component)value).GetComponent<CompanionStamina>();
						if ((Object)(object)component != (Object)null)
						{
							value2.StaminaBar.SetValue(component.GetStaminaPercentage());
						}
					}
					if ((Object)(object)value2.WeightBar != (Object)null)
					{
						Humanoid val = (Humanoid)(object)((value is Humanoid) ? value : null);
						if ((Object)(object)val != (Object)null)
						{
							Inventory inventory = val.GetInventory();
							float value3 = ((inventory != null) ? Mathf.Clamp01(inventory.GetTotalWeight() / 300f) : 0f);
							value2.WeightBar.SetValue(value3);
						}
					}
				}
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						CompanionsPlugin.Log.LogDebug((object)"[HUD] Removing stale companion bars — character destroyed or null");
						_bars.Remove(list[i]);
						_barCharacters.Remove(list[i]);
					}
				}
			}
		}

		private static readonly Color StaminaYellow = new Color(1f, 0.8f, 0.1f, 1f);

		private static readonly Color WeightBrown = new Color(0.72f, 0.53f, 0.26f, 1f);

		private static readonly Dictionary<int, CompanionBars> _bars = new Dictionary<int, CompanionBars>();

		private static readonly Dictionary<int, Character> _barCharacters = new Dictionary<int, Character>();

		private static IDictionary _cachedHuds;

		private static EnemyHud _cachedHudInstance;

		private static IDictionary GetHuds(EnemyHud instance)
		{
			if ((Object)(object)_cachedHudInstance != (Object)(object)instance || _cachedHuds == null)
			{
				_cachedHuds = Traverse.Create((object)instance).Field("m_huds").GetValue() as IDictionary;
				_cachedHudInstance = instance;
			}
			return _cachedHuds;
		}

		private static GuiBar CreateBar(Transform healthTransform, Transform guiParent, string name, Color color, float yOffset)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)healthTransform).gameObject, guiParent);
			((Object)obj).name = name;
			RectTransform component = ((Component)healthTransform).GetComponent<RectTransform>();
			((Transform)obj.GetComponent<RectTransform>()).localPosition = ((Transform)component).localPosition - new Vector3(0f, yOffset, 0f);
			Transform val = obj.transform.Find("health_slow");
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).gameObject.SetActive(false);
			}
			Transform val2 = obj.transform.Find("health_fast_friendly");
			if ((Object)(object)val2 != (Object)null)
			{
				((Component)val2).gameObject.SetActive(false);
			}
			Transform val3 = obj.transform.Find("health_fast");
			if ((Object)(object)val3 == (Object)null)
			{
				return null;
			}
			GuiBar component2 = ((Component)val3).GetComponent<GuiBar>();
			if ((Object)(object)component2 == (Object)null)
			{
				return null;
			}
			component2.SetColor(color);
			return component2;
		}

		private static void HideHudForRadialTarget(EnemyHud instance)
		{
			if ((Object)(object)CompanionRadialMenu.Instance == (Object)null || !CompanionRadialMenu.Instance.IsVisible)
			{
				return;
			}
			CompanionSetup currentCompanion = CompanionRadialMenu.Instance.CurrentCompanion;
			if ((Object)(object)currentCompanion == (Object)null)
			{
				return;
			}
			Character component = ((Component)currentCompanion).GetComponent<Character>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			IDictionary huds = GetHuds(instance);
			if (huds != null && huds.Contains(component))
			{
				GameObject value = Traverse.Create(huds[component]).Field("m_gui").GetValue<GameObject>();
				if ((Object)(object)value != (Object)null)
				{
					value.SetActive(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "Follow")]
	internal static class Follow_RunDetection
	{
		private static void Postfix(BaseAI __instance, GameObject go)
		{
			//IL_0031: 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)
			if (!(__instance is CompanionAI))
			{
				return;
			}
			CompanionStamina component = ((Component)__instance).GetComponent<CompanionStamina>();
			if (!((Object)(object)component == (Object)null))
			{
				if ((Object)(object)go == (Object)null)
				{
					component.IsRunning = false;
					return;
				}
				float num = Vector3.Distance(go.transform.position, ((Component)__instance).transform.position);
				component.IsRunning = num > 10f;
			}
		}
	}
	public static class InventoryGuiPatches
	{
		[HarmonyPatch(typeof(InventoryGui), "Show")]
		private static class Show_Patch
		{
			private static void Postfix(Container container)
			{
				_companionContainerOpen = false;
				if ((Object)(object)container == (Object)null)
				{
					CompanionsPlugin.Log.LogDebug((object)"[InvGUI] Show — container=null, skipping");
					return;
				}
				if ((Object)(object)((Component)container).GetComponent<CompanionInteract>() == (Object)null)
				{
					CompanionsPlugin.Log.LogDebug((object)("[InvGUI] Show — container \"" + ((Object)container).name + "\" has no CompanionInteract, skipping"));
					return;
				}
				CompanionSetup component = ((Component)container).GetComponent<CompanionSetup>();
				if ((Object)(object)component == (Object)null)
				{
					CompanionsPlugin.Log.LogWarning((object)("[InvGUI] Show — container \"" + ((Object)container).name + "\" has CompanionInteract but no CompanionSetup!"));
					return;
				}
				_companionContainerOpen = true;
				CompanionsPlugin.Log.LogInfo((object)("[InvGUI] Show — COMPANION container opened for \"" + ((Object)container).name + "\", hiding vanilla panels"));
				HideVanillaPanels();
				CompanionInteractPanel.EnsureInstance();
				CompanionInteractPanel.Instance?.Show(component);
				CompanionsPlugin.Log.LogDebug((object)$"[InvGUI] Show — CompanionInteractPanel visible={CompanionInteractPanel.Instance?.IsVisible}");
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "Hide")]
		private static class Hide_Patch
		{
			private static void Postfix()
			{
				if (_companionContainerOpen)
				{
					CompanionsPlugin.Log.LogInfo((object)"[InvGUI] Hide — closing companion container session");
				}
				_companionContainerOpen = false;
				CompanionInteractPanel.Instance?.Hide();
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateContainer")]
		private static class UpdateContainer_Patch
		{
			private static float _lastLogTime;

			private static bool Prefix(Player player)
			{
				if (!_companionContainerOpen)
				{
					return true;
				}
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return true;
				}
				object? obj = _currentContainerField?.GetValue(instance);
				Container val = (Container)((obj is Container) ? obj : null);
				if ((Object)(object)val == (Object)null || !val.IsOwner())
				{
					CompanionsPlugin.Log.LogWarning((object)("[InvGUI] UpdateContainer — companion container lost " + $"(null={(Object)(object)val == (Object)null}, owner={((val != null) ? new bool?(val.IsOwner()) : null)}), " + "falling through to vanilla"));
					_companionContainerOpen = false;
					return true;
				}
				val.SetInUse(true);
				bool flag = (Object)(object)instance.m_container != (Object)null && ((Component)instance.m_container).gameObject.activeSelf;
				bool flag2 = (Object)(object)instance.m_crafting != (Object)null && ((Component)instance.m_crafting).gameObject.activeSelf;
				HideVanillaPanels();
				if ((flag || flag2) && Time.time - _lastLogTime > 2f)
				{
					_lastLogTime = Time.time;
					CompanionsPlugin.Log.LogWarning((object)("[InvGUI] UpdateContainer — vanilla panels were active " + $"(container={flag}, crafting={flag2}), " + "re-hidden"));
				}
				return false;
			}
		}

		private static bool _companionContainerOpen;

		private static readonly FieldInfo _currentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

		private static void HideVanillaPanels()
		{
			if (!((Object)(object)InventoryGui.instance == (Object)null))
			{
				if ((Object)(object)InventoryGui.instance.m_container != (Object)null)
				{
					((Component)InventoryGui.instance.m_container).gameObject.SetActive(false);
				}
				if ((Object)(object)InventoryGui.instance.m_crafting != (Object)null)
				{
					((Component)InventoryGui.instance.m_crafting).gameObject.SetActive(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	public static class PlayerHooks
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			CompanionManager.RestoreFollowTargets();
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class TakeInput_BlockForCompanionUI
	{
		private static void Postfix(ref bool __result)
		{
			if (!__result)
			{
				return;
			}
			CompanionInteractPanel instance = CompanionInteractPanel.Instance;
			if ((Object)(object)instance != (Object)null && instance.IsVisible)
			{
				__result = false;
				return;
			}
			CompanionRadialMenu instance2 = CompanionRadialMenu.Instance;
			if ((Object)(object)instance2 != (Object)null && instance2.IsVisible)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch]
	internal static class PortalPatches
	{
		private static readonly List<ZDOID> _pendingCompanions = new List<ZDOID>();

		[HarmonyPatch(typeof(Player), "TeleportTo")]
		[HarmonyPostfix]
		private static void TeleportTo_Postfix(Player __instance, bool __result)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			_pendingCompanions.Clear();
			string text = __instance.GetPlayerID().ToString();
			CompanionSetup[] array = Object.FindObjectsByType<CompanionSetup>((FindObjectsSortMode)0);
			for (int i = 0; i < array.Length; i++)
			{
				ZNetView component = ((Component)array[i]).GetComponent<ZNetView>();
				if (((component != null) ? component.GetZDO() : null) != null)
				{
					ZDO zDO = component.GetZDO();
					if (!(zDO.GetString(CompanionSetup.OwnerHash, "") != text) && zDO.GetInt(CompanionSetup.ActionModeHash, 0) != 4)
					{
						_pendingCompanions.Add(zDO.m_uid);
					}
				}
			}
			if (_pendingCompanions.Count > 0)
			{
				CompanionsPlugin.Log.LogInfo((object)$"[Portal] Player teleporting — {_pendingCompanions.Count} companion(s) queued");
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateTeleport")]
		[HarmonyPrefix]
		private static void UpdateTeleport_Prefix(Player __instance, out bool __state)
		{
			__state = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && ((Character)__instance).IsTeleporting();
		}

		[HarmonyPatch(typeof(Player), "UpdateTeleport")]
		[HarmonyPostfix]
		private static void UpdateTeleport_Postfix(Player __instance, bool __state)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//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)
			if (!__state || ((Character)__instance).IsTeleporting() || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || _pendingCompanions.Count == 0)
			{
				return;
			}
			Vector3 position = ((Component)__instance).transform.position;
			CompanionsPlugin.Log.LogInfo((object)$"[Portal] Teleport complete at {position:F1} — warping {_pendingCompanions.Count} companion(s)");
			foreach (ZDOID pendingCompanion in _pendingCompanions)
			{
				WarpCompanion(pendingCompanion, position);
			}
			_pendingCompanions.Clear();
		}

		private static void WarpCompanion(ZDOID zdoid, Vector3 targetPos)
		{
			//IL_0005: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = ZDOMan.instance.GetZDO(zdoid);
			if (zDO == null)
			{
				CompanionsPlugin.Log.LogWarning((object)$"[Portal] Companion ZDO {zdoid} not found — skipping");
				return;
			}
			Vector3 val = FindNearbyPosition(targetPos, 3f);
			zDO.SetPosition(val);
			ZNetScene instance = ZNetScene.instance;
			GameObject val2 = ((instance != null) ? instance.FindInstance(zdoid) : null);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.transform.position = val;
				Rigidbody component = val2.GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.position = val;
					component.velocity = Vector3.zero;
				}
				CompanionAI component2 = val2.GetComponent<CompanionAI>();
				if ((Object)(object)component2 != (Object)null)
				{
					Player localPlayer = Player.m_localPlayer;
					component2.SetFollowTarget((localPlayer != null) ? ((Component)localPlayer).gameObject : null);
				}
				CompanionsPlugin.Log.LogInfo((object)$"[Portal] Warped companion to {val:F1} (GameObject present)");
			}
			else
			{
				CompanionsPlugin.Log.LogInfo((object)$"[Portal] Updated companion ZDO position to {val:F1} (not loaded)");
			}
		}

		private static Vector3 FindNearbyPosition(Vector3 origin, float radius)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			float y = default(float);
			for (int i = 0; i < 20; i++)
			{
				Vector2 val = Random.insideUnitCircle * radius;
				Vector3 val2 = origin + new Vector3(val.x, 0f, val.y);
				if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(val2, ref y))
				{
					val2.y = y;
					return val2;
				}
			}
			return origin + Vector3.right * 2f;
		}
	}
	[HarmonyPatch(typeof(Projectile), "Setup")]
	internal static class ProjectileSetup_CompanionAccuracy
	{
		private static readonly FieldRef<Projectile, Vector3> _velRef = AccessTools.FieldRefAccess<Projectile, Vector3>("m_vel");

		private static float _lastLogTime;

		private static void Postfix(Projectile __instance, Character owner)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)owner == (Object)null || (Object)(object)((Component)owner).GetComponent<CompanionSetup>() == (Object)null)
			{
				return;
			}
			CompanionAI component = ((Component)owner).GetComponent<CompanionAI>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			Character targetCreature = component.m_targetCreature;
			if ((Object)(object)targetCreature == (Object)null || targetCreature.IsDead())
			{
				return;
			}
			Vector3 val = _velRef.Invoke(__instance);
			float num = ((Vector3)(ref val)).magnitude;
			if (!(num < 0.1f))
			{
				if (num < 60f)
				{
					num = 60f;
				}
				Vector3 position = ((Component)__instance).transform.position;
				Vector3 val2 = targetCreature.GetCenterPoint();
				float num2 = Vector3.Distance(position, val2);
				Vector3 velocity = targetCreature.GetVelocity();
				float num3 = num2 / num;
				if (((Vector3)(ref velocity)).magnitude > 0.5f)
				{
					val2 += velocity * num3;
				}
				float num4 = 4.905f * num3 * num3;
				val2.y += num4;
				Vector3 val3 = val2 - position;
				Vector3 normalized = ((Vector3)(ref val3)).normalized;
				_velRef.Invoke(__instance) = normalized * num;
				if (Time.time - _lastLogTime > 1f)
				{
					_lastLogTime = Time.time;
					CompanionsPlugin.Log.LogDebug((object)("[Projectile] Arrow redirected — target=\"" + targetCreature.m_name + "\" " + $"dist={num2:F1} speed={num:F0} travelTime={num3:F2}s " + $"gravDrop={num4:F2} leadMag={((Vector3)(ref velocity)).magnitude * num3:F1} " + $"targetVel={((Vector3)(ref velocity)).magnitude:F1}"));
				}
			}
		}
	}
	public static class StaminaPatches
	{
		[HarmonyPatch(typeof(Character), "UseStamina")]
		private static class UseStamina_Patch
		{
			private static bool Prefix(Character __instance, float stamina)
			{
				CompanionStamina component = ((Component)__instance).GetComponent<CompanionStamina>();
				if ((Object)(object)component == (Object)null)
				{
					return true;
				}
				float stamina2 = component.Stamina;
				component.Drain(stamina);
				CompanionsPlugin.Log.LogDebug((object)($"[Stamina] UseStamina — drained {stamina:F1} " + $"({stamina2:F1} → {component.Stamina:F1} / {component.MaxStamina:F1}) " + "companion=\"" + __instance.m_name + "\""));
				return false;
			}
		}

		[HarmonyPatch(typeof(Character), "HaveStamina")]
		private static class HaveStamina_Patch
		{
			private static bool Prefix(Character __instance, float amount, ref bool __result)
			{
				CompanionStamina component = ((Component)__instance).GetComponent<CompanionStamina>();
				if ((Object)(object)component == (Object)null)
				{
					return true;
				}
				__result = component.Stamina >= amount;
				if (!__result && Time.time - _lastLogTime > 2f)
				{
					_lastLogTime = Time.time;
					CompanionsPlugin.Log.LogDebug((object)($"[Stamina] HaveStamina FAILED — need {amount:F1} " + $"have {component.Stamina:F1} / {component.MaxStamina:F1} " + "companion=\"" + __instance.m_name + "\""));
				}
				return false;
			}
		}

		private static float _lastLogTime;

		private const float LogInterval = 2f;
	}
	public static class TraderUIPatches
	{
		[HarmonyPatch]
		private static class BuildUI_Patch
		{
			private static MethodBase TargetMethod()
			{
				MethodInfo? obj = Type.GetType("TraderOverhaul.TraderUI, TraderOverhaul")?.GetMethod("BuildUI", BindingFlags.Instance | BindingFlags.NonPublic);
				if (obj == null)
				{
					ManualLogSource log = CompanionsPlugin.Log;
					if (log == null)
					{
						return obj;
					}
					log.LogWarning((object)"[TraderUIPatches] BuildUI method not found — patch skipped.");
				}
				return obj;
			}

			private static void Postfix(object __instance)
			{
				try
				{
					CacheReflection();
					InjectCompanionTab(__instance);
				}
				catch (Exception arg)
				{
					CompanionsPlugin.Log.LogError((object)$"[TraderUIPatches] BuildUI postfix error: {arg}");
				}
			}
		}

		[HarmonyPatch]
		private static class SwitchTab_Patch
		{
			private static MethodBase TargetMethod()
			{
				MethodInfo? obj = Type.GetType("TraderOverhaul.TraderUI, TraderOverhaul")?.GetMethod("SwitchTab", BindingFlags.Instance | BindingFlags.NonPublic);
				if (obj == null)
				{
					ManualLogSource log = CompanionsPlugin.Log;
					if (log == null)
					{
						return obj;
					}
					log.LogWarning((object)"[TraderUIPatches] SwitchTab method not found — patch skipped.");
				}
				return obj;
			}