Decompiled source of RepairDrone v0.1.1

plugins/RepairDrone/RepairDrone.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using REPOLib.Modules;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("saya")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1")]
[assembly: AssemblyProduct("RepairDrone")]
[assembly: AssemblyTitle("RepairDrone")]
[assembly: AssemblyVersion("0.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RepairDrone
{
	[BepInPlugin("saya.RepairDrone", "RepairDrone", "0.1.1")]
	[BepInDependency("REPOLib", "4.2.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "saya.RepairDrone";

		public const string PluginName = "RepairDrone";

		public const string PluginVersion = "0.1.1";

		private readonly Harmony _harmony = new Harmony("saya.RepairDrone");

		private void Awake()
		{
			((Component)this).transform.SetParent((Transform)null);
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			try
			{
				RepairSettings settings = new RepairSettings(((BaseUnityPlugin)this).Config);
				new RepairService(((BaseUnityPlugin)this).Logger, settings);
				_harmony.PatchAll(typeof(RepairBatteryValuePatch));
				_harmony.PatchAll(typeof(RepairBatteryBarsPatch));
				_harmony.PatchAll(typeof(RepairChargingStationPatch));
				_harmony.PatchAll(typeof(RepairChargeConditionPatch));
				_harmony.PatchAll(typeof(RepairShopStockPatch));
				_harmony.PatchAll(typeof(RepairInventoryIconPatch));
				_harmony.PatchAll(typeof(RepairBatteryRestorePatch));
				_harmony.PatchAll(typeof(RepairBatteryCapturePatch));
				_harmony.PatchAll(typeof(RepairStatsPatch));
				RepairShopRegistration.Register();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"RepairDrone v0.1.1 loaded.");
			}
			catch (Exception arg)
			{
				_harmony.UnpatchSelf();
				((Behaviour)this).enabled = false;
				((BaseUnityPlugin)this).Logger.LogError((object)$"Repair Drone initialization failed: {arg}");
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
	internal static class RepairAmount
	{
		internal static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		internal static float Request(float current, float original, float budget)
		{
			if (!IsFinite(current) || !IsFinite(original) || !IsFinite(budget) || current < 0f || original <= current || budget <= 0f)
			{
				return 0f;
			}
			double num = (double)original - (double)current;
			if (!(num <= (double)budget))
			{
				return (float)Math.Floor(budget);
			}
			return (float)Math.Ceiling(num);
		}

		internal static float Applied(float before, float after)
		{
			if (!IsFinite(before) || !IsFinite(after) || before < 0f || after <= before)
			{
				return 0f;
			}
			return after - before;
		}
	}
	[HarmonyPatch(typeof(ItemBattery), "BatteryFullPercentChangeLogic")]
	internal static class RepairBatteryValuePatch
	{
		private static void Prefix(ItemBattery __instance, out float? __state)
		{
			__state = ((SemiFunc.IsMasterClientOrSingleplayer() && Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<RepairDroneController>())) ? new float?(__instance.batteryLife) : ((float?)null));
		}

		private static void Postfix(ItemBattery __instance, float? __state)
		{
			if (__state.HasValue)
			{
				__instance.batteryLife = __state.Value;
			}
		}
	}
	[HarmonyPatch]
	internal static class RepairBatteryBarsPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.DeclaredMethod(typeof(ItemBattery), "Update", (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(typeof(ItemBattery), "TryVisualUpdate", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo round = AccessTools.Method(typeof(Mathf), "Round", new Type[1] { typeof(float) }, (Type[])null);
			MethodInfo replacement = AccessTools.Method(typeof(RepairBatteryBarsPatch), "Round", (Type[])null, (Type[])null);
			MethodInfo chargeLimit = AccessTools.Method(typeof(RepairBatteryBarsPatch), "ChargeLimit", (Type[])null, (Type[])null);
			int replacements = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, round))
				{
					yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Ldarg_0, (object)null), instruction), instruction);
					yield return new CodeInstruction(OpCodes.Call, (object)replacement);
					replacements++;
					continue;
				}
				yield return instruction;
				if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num && num == 99f)
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)chargeLimit);
				}
			}
			if (replacements == 0)
			{
				throw new MissingMethodException("No battery rounding calls were found.");
			}
		}

		private static float Round(float value, ItemBattery battery)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || !Object.op_Implicit((Object)(object)((Component)battery).GetComponent<RepairDroneController>()))
			{
				return Mathf.Round(value);
			}
			return RepairEnergy.DisplayBars(value);
		}

		private static float ChargeLimit(float value, ItemBattery battery)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || !Object.op_Implicit((Object)(object)((Component)battery).GetComponent<RepairDroneController>()))
			{
				return value;
			}
			return 100f;
		}
	}
	[HarmonyPatch]
	internal static class RepairChargingStationPatch
	{
		private static readonly FieldRef<ItemBattery, int> ReadBars = AccessTools.FieldRefAccess<ItemBattery, int>("batteryLifeInt");

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.DeclaredMethod(typeof(ChargingStation), "UpdateItemsInRange", (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(typeof(ChargingStation), "ChargeAreaCheck", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			FieldInfo field = AccessTools.Field(typeof(ItemBattery), "batteryLifeInt");
			MethodInfo replacement = AccessTools.Method(typeof(RepairChargingStationPatch), "ChargeBars", (Type[])null, (Type[])null);
			int replacements = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.LoadsField(instruction, field, false))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = replacement;
					replacements++;
				}
				yield return instruction;
			}
			if (replacements == 0)
			{
				throw new MissingFieldException("No charging station battery comparisons were found.");
			}
		}

		private static int ChargeBars(ItemBattery battery)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && Object.op_Implicit((Object)(object)((Component)battery).GetComponent<RepairDroneController>()))
			{
				if (!RepairEnergy.NeedsCharge(battery.batteryLife))
				{
					return battery.batteryBars;
				}
				return battery.batteryBars - 1;
			}
			return ReadBars.Invoke(battery);
		}
	}
	[HarmonyPatch(typeof(SemiFunc), "BatteryChargeCondition")]
	internal static class RepairChargeConditionPatch
	{
		private static void Postfix(ItemBattery battery, ref bool __result)
		{
			if (Object.op_Implicit((Object)(object)battery) && SemiFunc.IsMasterClientOrSingleplayer() && Object.op_Implicit((Object)(object)((Component)battery).GetComponent<RepairDroneController>()))
			{
				__result = !battery.isUnchargable && RepairEnergy.NeedsCharge(battery.batteryLife);
			}
		}
	}
	internal static class RepairBatterySave
	{
		internal const string DictionaryKey = "sayaRepairDroneBatteryV1";

		internal static void Capture(IDictionary<string, int> records, string name, float percent)
		{
			if (!string.IsNullOrEmpty(name) && RepairAmount.IsFinite(percent) && !(percent < 0f) && !(percent > 100f))
			{
				records[name] = BitConverter.SingleToInt32Bits(percent);
			}
		}

		internal static bool TryRestore(IDictionary<string, int> records, string name, out float percent)
		{
			percent = 0f;
			if (string.IsNullOrEmpty(name) || !records.TryGetValue(name, out var value))
			{
				return false;
			}
			float num = BitConverter.Int32BitsToSingle(value);
			if (!RepairAmount.IsFinite(num) || num < 0f || num > 100f)
			{
				return false;
			}
			percent = num;
			return true;
		}
	}
	internal sealed class RepairDroneController : MonoBehaviour, ITargetingCondition
	{
		private static readonly FieldRef<ItemDrone, PhysGrabObject> Target = AccessTools.FieldRefAccess<ItemDrone, PhysGrabObject>("magnetTargetPhysGrabObject");

		private static readonly FieldRef<ItemEquippable, bool> Equipped = AccessTools.FieldRefAccess<ItemEquippable, bool>("isEquipped");

		private static readonly FieldRef<ItemDrone, bool> MagnetActive = AccessTools.FieldRefAccess<ItemDrone, bool>("magnetActive");

		private static readonly FieldRef<ItemBattery, int> BatteryBars = AccessTools.FieldRefAccess<ItemBattery, int>("batteryLifeInt");

		private readonly RepairPulseClock _clock = new RepairPulseClock();

		private readonly RepairEnergy _energy = new RepairEnergy();

		private RepairService _owner;

		private ManualLogSource _log;

		private ItemDrone _drone;

		private ItemEquippable _equippable;

		private ItemToggle _toggle;

		private PhysGrabObject _body;

		private ItemBattery _battery;

		private PhysGrabObject? _previousTarget;

		private void Start()
		{
			if (_owner == null && Object.op_Implicit((Object)(object)((Component)this).GetComponent<RepairShopItem>()))
			{
				RepairService instance = RepairService.Instance;
				if (instance != null)
				{
					Initialize(instance, instance.Log);
				}
			}
		}

		internal void Initialize(RepairService owner, ManualLogSource log)
		{
			_owner = owner;
			_log = log;
			_drone = ((Component)this).GetComponent<ItemDrone>();
			_equippable = ((Component)this).GetComponent<ItemEquippable>();
			_toggle = ((Component)this).GetComponent<ItemToggle>();
			_body = ((Component)this).GetComponent<PhysGrabObject>();
			_battery = ((Component)this).GetComponent<ItemBattery>();
			if (!Object.op_Implicit((Object)(object)_drone) || !Object.op_Implicit((Object)(object)_equippable) || !Object.op_Implicit((Object)(object)_toggle) || !Object.op_Implicit((Object)(object)_body) || !Object.op_Implicit((Object)(object)_battery))
			{
				throw new InvalidOperationException("The repair drone is missing a required vanilla component.");
			}
			_drone.targetValuables = true;
			_drone.targetPlayers = (_drone.targetEnemies = (_drone.targetNonValuables = false));
			_battery.autoDrain = false;
		}

		public bool CustomTargetingCondition(GameObject target)
		{
			if (_owner != null && _owner.DroneEnabled && Object.op_Implicit((Object)(object)target))
			{
				return RepairService.NeedsRepair(target.GetComponent<PhysGrabObject>());
			}
			return false;
		}

		private void Update()
		{
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Invalid comparison between Unknown and I4
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Invalid comparison between Unknown and I4
			if (_owner == null || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			try
			{
				_energy.Observe(_battery.batteryLife);
				RepairShopItem component = ((Component)this).GetComponent<RepairShopItem>();
				if (Object.op_Implicit((Object)(object)component) && !component.BatteryReady)
				{
					ResetProgress();
					return;
				}
				if (!_owner.DroneEnabled || Equipped.Invoke(_equippable) || !_toggle.toggleState || _body.dead)
				{
					ResetProgress();
					if (!_owner.DroneEnabled && _toggle.toggleState)
					{
						_toggle.ToggleItem(false, -1);
					}
					return;
				}
				if (_energy.Percent <= 0.0)
				{
					StopRepair("Battery is empty.");
					return;
				}
				_body.OverrideZeroGravity(0.1f);
				_body.OverrideDrag(1f, 0.1f);
				_body.OverrideAngularDrag(10f, 0.1f);
				PhysGrabObject val = Target.Invoke(_drone);
				bool flag = Object.op_Implicit((Object)(object)val) && !val.dead && ((Component)val).gameObject.activeInHierarchy && MagnetActive.Invoke(_drone) && ((int)_drone.currentState == 3 || (int)_drone.currentState == 2);
				bool flag2 = _clock.AdvanceConnection(Object.op_Implicit((Object)(object)val) ? ((Object)val).GetInstanceID() : 0, flag, Time.deltaTime, _owner.DroneInterval);
				if (!flag)
				{
					_previousTarget = null;
					return;
				}
				if ((Object)(object)_previousTarget != (Object)(object)val)
				{
					_previousTarget = val;
					_log.LogDebug((object)"Repair drone connected to a valuable.");
				}
				if (!RepairService.NeedsRepair(val))
				{
					StopRepair("Target is fully repaired.");
				}
				else if (flag2)
				{
					float fullChargeRepairValue = _owner.FullChargeRepairValue;
					float budget = _energy.Budget(fullChargeRepairValue, _owner.DroneAmount);
					float num = _owner.Repair(val, budget);
					_battery.batteryLife = _energy.Consume(num, fullChargeRepairValue);
					if (Object.op_Implicit((Object)(object)component))
					{
						component.Capture();
					}
					_log.LogDebug((object)FormattableString.Invariant($"Repair battery: applied={num:R} remaining={_energy.Percent:F6}% capacity={fullChargeRepairValue:R}."));
					if (_energy.Percent <= 0.0)
					{
						StopRepair("Battery is empty.");
					}
					else if (num <= 0f)
					{
						StopRepair("No repair was observed; stopped without retrying.");
					}
					else if (!RepairService.NeedsRepair(val))
					{
						StopRepair("Target is fully repaired.");
					}
				}
			}
			catch (Exception arg)
			{
				((Behaviour)this).enabled = false;
				_log.LogError((object)$"Repair drone disabled: {arg}");
				_toggle.ToggleItem(false, -1);
			}
		}

		private void StopRepair(string reason)
		{
			ResetProgress();
			_toggle.ToggleItem(false, -1);
			_log.LogDebug((object)("Repair drone stopped: " + reason));
		}

		private void ResetProgress()
		{
			_clock.Reset();
			_previousTarget = null;
		}
	}
	internal sealed class RepairEnergy
	{
		private float _published = 100f;

		internal double Percent { get; private set; } = 100.0;

		internal void Observe(float batteryPercent)
		{
			if (batteryPercent != _published)
			{
				Percent = (RepairAmount.IsFinite(batteryPercent) ? Math.Max(0f, Math.Min(100f, batteryPercent)) : 0f);
				_published = (float)Percent;
			}
		}

		internal float Budget(float capacity, float pulse)
		{
			if (!RepairAmount.IsFinite(capacity) || !RepairAmount.IsFinite(pulse) || capacity <= 0f || pulse <= 0f)
			{
				return 0f;
			}
			return (float)Math.Min(pulse, Percent * (double)capacity / 100.0);
		}

		internal float Consume(float applied, float capacity)
		{
			if (RepairAmount.IsFinite(applied) && applied > 0f && RepairAmount.IsFinite(capacity) && capacity > 0f)
			{
				Percent = Math.Max(0.0, Percent - (double)applied * 100.0 / (double)capacity);
			}
			if (RepairAmount.IsFinite(capacity) && capacity > 0f && Percent * (double)capacity / 100.0 < 0.999999)
			{
				Percent = 0.0;
			}
			if (Percent < 1E-10)
			{
				Percent = 0.0;
			}
			_published = (float)Percent;
			return _published;
		}

		internal static float DisplayBars(float scaledBars)
		{
			if (!RepairAmount.IsFinite(scaledBars))
			{
				return 0f;
			}
			return (float)Math.Ceiling(Math.Max(0f, scaledBars - 1E-06f));
		}

		internal static bool NeedsCharge(float percent)
		{
			if (RepairAmount.IsFinite(percent) && percent >= 0f)
			{
				return percent < 100f;
			}
			return false;
		}
	}
	internal sealed class RepairInventoryIcon : MonoBehaviour
	{
		private static readonly FieldRef<ItemAttributes, bool> HasIcon = AccessTools.FieldRefAccess<ItemAttributes, bool>("hasIcon");

		private Texture2D? _renderedTexture;

		internal IEnumerator Generate(ItemAttributes attributes)
		{
			yield return null;
			SemiIconMaker componentInChildren = ((Component)this).GetComponentInChildren<SemiIconMaker>(true);
			BatteryVisualLogic componentInChildren2 = ((Component)this).GetComponentInChildren<BatteryVisualLogic>(true);
			bool active = Object.op_Implicit((Object)(object)componentInChildren2) && ((Component)componentInChildren2).gameObject.activeSelf;
			try
			{
				if (Object.op_Implicit((Object)(object)componentInChildren2))
				{
					((Component)componentInChildren2).gameObject.SetActive(false);
				}
				Sprite val = componentInChildren.CreateIconFromRenderTexture((string)null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					RepairService? instance = RepairService.Instance;
					if (instance != null)
					{
						instance.Log.LogError((object)"Repair Drone inventory icon rendering failed.");
					}
				}
				else
				{
					attributes.icon = val;
					_renderedTexture = val.texture;
					HasIcon.Invoke(attributes) = true;
				}
			}
			finally
			{
				if (Object.op_Implicit((Object)(object)componentInChildren2))
				{
					((Component)componentInChildren2).gameObject.SetActive(active);
				}
				if (Object.op_Implicit((Object)(object)componentInChildren))
				{
					((Component)componentInChildren).gameObject.SetActive(false);
				}
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)_renderedTexture))
			{
				Object.Destroy((Object)(object)_renderedTexture);
			}
		}
	}
	[HarmonyPatch(typeof(ItemAttributes), "GenerateIcon")]
	internal static class RepairInventoryIconPatch
	{
		private static bool Prefix(ItemAttributes __instance, ref IEnumerator __result)
		{
			RepairInventoryIcon component = ((Component)__instance).GetComponent<RepairInventoryIcon>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return true;
			}
			__result = component.Generate(__instance);
			return false;
		}
	}
	internal sealed class RepairPulseClock
	{
		private float _elapsed;

		private int? _target;

		internal void Reset()
		{
			_elapsed = 0f;
			_target = null;
		}

		internal bool AdvanceConnection(int target, bool connected, float delta, float interval)
		{
			if (!connected)
			{
				Reset();
				return false;
			}
			if (_target != target)
			{
				Reset();
				_target = target;
			}
			return Advance(delta, interval);
		}

		internal bool Advance(float delta, float interval)
		{
			if (!RepairAmount.IsFinite(delta) || !RepairAmount.IsFinite(interval) || delta < 0f || interval <= 0f)
			{
				Reset();
				return false;
			}
			_elapsed += delta;
			if (_elapsed < interval)
			{
				return false;
			}
			_elapsed = 0f;
			return true;
		}
	}
	internal sealed class RepairService
	{
		private static readonly FieldRef<ValuableObject, float> Current = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueCurrent");

		private static readonly FieldRef<ValuableObject, float> Original = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueOriginal");

		private static readonly Action<PhysGrabObjectImpactDetector, float, Vector3> HealLocal = AccessTools.MethodDelegate<Action<PhysGrabObjectImpactDetector, float, Vector3>>(AccessTools.DeclaredMethod(typeof(PhysGrabObjectImpactDetector), "HealLogic", new Type[2]
		{
			typeof(float),
			typeof(Vector3)
		}, (Type[])null), (object)null, true);

		internal static RepairService? Instance { get; private set; }

		internal ManualLogSource Log { get; }

		internal RepairSettings Settings { get; }

		internal bool DroneEnabled => true;

		internal float DroneAmount => Settings.AmountPerPulse;

		internal float DroneInterval => Settings.PulseSeconds;

		internal float FullChargeRepairValue => Settings.FullChargeRepairValue;

		internal bool PreferInShop => Settings.PreferInShop;

		internal RepairService(ManualLogSource log, RepairSettings settings)
		{
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(PhysGrabObjectImpactDetector), "HealRPC", new Type[3]
			{
				typeof(float),
				typeof(Vector3),
				typeof(PhotonMessageInfo)
			}, (Type[])null);
			if ((object)methodInfo == null || !methodInfo.IsDefined(typeof(PunRPC), inherit: false))
			{
				throw new MissingMethodException("The vanilla HealRPC signature is incompatible.");
			}
			Log = log;
			Settings = settings;
			Instance = this;
		}

		internal static bool NeedsRepair(PhysGrabObject target)
		{
			if (!Object.op_Implicit((Object)(object)target) || target.dead || !((Component)target).gameObject.activeInHierarchy)
			{
				return false;
			}
			ValuableObject component = ((Component)target).GetComponent<ValuableObject>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return RepairAmount.Request(Current.Invoke(component), Original.Invoke(component), 1f) > 0f;
			}
			return false;
		}

		internal static string DescribeValue(PhysGrabObject target)
		{
			ValuableObject val = (Object.op_Implicit((Object)(object)target) ? ((Component)target).GetComponent<ValuableObject>() : null);
			if (val == null || !Object.op_Implicit((Object)(object)val))
			{
				return "value=unavailable";
			}
			return FormattableString.Invariant($"current={Current.Invoke(val):R} original={Original.Invoke(val):R}");
		}

		internal float Repair(PhysGrabObject target, float budget)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)target) || target.dead || (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient))
			{
				return 0f;
			}
			ValuableObject component = ((Component)target).GetComponent<ValuableObject>();
			PhysGrabObjectImpactDetector component2 = ((Component)target).GetComponent<PhysGrabObjectImpactDetector>();
			if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !component2.isValuable)
			{
				return 0f;
			}
			float num = Current.Invoke(component);
			float num2 = Original.Invoke(component);
			float num3 = RepairAmount.Request(num, num2, budget);
			PhotonView component3 = ((Component)component2).GetComponent<PhotonView>();
			if (GameManager.Multiplayer() && (!Object.op_Implicit((Object)(object)component3) || component3.ViewID == 0))
			{
				Log.LogWarning((object)"Repair: the target valuable has no registered PhotonView.");
				return 0f;
			}
			if (num3 > 0f)
			{
				if (GameManager.Multiplayer())
				{
					component3.RPC("HealRPC", (RpcTarget)0, new object[2] { num3, target.centerPoint });
				}
				else
				{
					HealLocal(component2, num3, target.centerPoint);
				}
			}
			float num4 = Current.Invoke(component);
			float num5 = RepairAmount.Applied(num, num4);
			Log.LogDebug((object)FormattableString.Invariant($"Repair: view={(Object.op_Implicit((Object)(object)component3) ? component3.ViewID : 0)} before={num:R} original={num2:R} requested={num3:R} after={num4:R} applied={num5:R}."));
			if (num3 > 0f && num5 <= 0f)
			{
				Log.LogWarning((object)"Repair: no local increase observed; no success or resource charge recorded.");
			}
			return num5;
		}
	}
	internal sealed class RepairSettings
	{
		internal float AmountPerPulse => 500f;

		internal float PulseSeconds => 1f;

		internal float FullChargeRepairValue => 12000f;

		internal bool PreferInShop => false;

		internal RepairSettings(ConfigFile config)
		{
		}
	}
	internal sealed class RepairShopItem : MonoBehaviour
	{
		private static readonly FieldRef<ItemAttributes, string> InstanceName = AccessTools.FieldRefAccess<ItemAttributes, string>("instanceName");

		private static readonly FieldRef<StatsManager, Dictionary<string, int>> OwnedItems = AccessTools.FieldRefAccess<StatsManager, Dictionary<string, int>>("item");

		private static readonly Action<ItemBattery, int, bool> PublishBars = AccessTools.MethodDelegate<Action<ItemBattery, int, bool>>(AccessTools.DeclaredMethod(typeof(ItemBattery), "BatteryFullPercentChange", (Type[])null, (Type[])null), (object)null, true);

		private ItemAttributes _attributes;

		private ItemBattery _battery;

		internal bool BatteryReady { get; private set; }

		private void Awake()
		{
			_attributes = ((Component)this).GetComponent<ItemAttributes>();
			_battery = ((Component)this).GetComponent<ItemBattery>();
		}

		internal void Restore()
		{
			BatteryReady = true;
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				string text = InstanceName.Invoke(_attributes);
				if (!SemiFunc.RunIsShop() && !SemiFunc.RunIsArena() && RepairBatterySave.TryRestore(RepairStatsPatch.Records(StatsManager.instance), text, out var percent))
				{
					_battery.batteryLife = percent;
					PublishBars(_battery, (int)RepairEnergy.DisplayBars(percent / (100f / (float)_battery.batteryBars)), arg3: false);
					RepairService.Instance.Log.LogInfo((object)FormattableString.Invariant($"Restored repair battery: item={text} remaining={percent:R}%."));
				}
				Capture();
			}
		}

		internal void Capture()
		{
			if (BatteryReady && Object.op_Implicit((Object)(object)StatsManager.instance) && SemiFunc.IsMasterClientOrSingleplayer() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena())
			{
				string text = InstanceName.Invoke(_attributes);
				if (!string.IsNullOrEmpty(text) && OwnedItems.Invoke(StatsManager.instance).ContainsKey(text))
				{
					RepairBatterySave.Capture(RepairStatsPatch.Records(StatsManager.instance), text, _battery.batteryLife);
				}
			}
		}

		private void OnDisable()
		{
			Capture();
		}
	}
	[HarmonyPatch(typeof(ItemBattery), "SetBatteryLife")]
	internal static class RepairBatteryRestorePatch
	{
		private static void Postfix(ItemBattery __instance)
		{
			RepairShopItem component = ((Component)__instance).GetComponent<RepairShopItem>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.Restore();
			}
		}
	}
	[HarmonyPatch(typeof(ItemBattery), "Update")]
	internal static class RepairBatteryCapturePatch
	{
		private static void Postfix(ItemBattery __instance)
		{
			RepairShopItem component = ((Component)__instance).GetComponent<RepairShopItem>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.Capture();
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager))]
	internal static class RepairStatsPatch
	{
		private static readonly FieldRef<StatsManager, SortedDictionary<string, Dictionary<string, int>>> Dictionaries = AccessTools.FieldRefAccess<StatsManager, SortedDictionary<string, Dictionary<string, int>>>("dictionaryOfDictionaries");

		internal static Dictionary<string, int> Records(StatsManager stats)
		{
			SortedDictionary<string, Dictionary<string, int>> sortedDictionary = Dictionaries.Invoke(stats);
			if (!sortedDictionary.TryGetValue("sayaRepairDroneBatteryV1", out var value))
			{
				value = new Dictionary<string, int>();
				sortedDictionary.Add("sayaRepairDroneBatteryV1", value);
			}
			return value;
		}

		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void Initialize(StatsManager __instance)
		{
			Records(__instance);
		}

		[HarmonyPatch("SaveGame")]
		[HarmonyPrefix]
		private static void Flush()
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				RepairShopItem[] array = Object.FindObjectsOfType<RepairShopItem>(true);
				for (int i = 0; i < array.Length; i++)
				{
					array[i].Capture();
				}
			}
		}

		[HarmonyPatch("ItemRemove")]
		[HarmonyPostfix]
		private static void Remove(StatsManager __instance, string instanceName)
		{
			if (!SemiFunc.RunIsArena() && SemiFunc.IsMasterClientOrSingleplayer())
			{
				Records(__instance).Remove(instanceName);
			}
		}
	}
	internal static class RepairShopRegistration
	{
		internal const string ItemId = "Item Drone Repair";

		internal const string PrefabId = "Items/Item Drone Repair";

		private static GameObject? _templates;

		internal static Item? Item { get; private set; }

		internal static GameObject? Template { get; private set; }

		internal static void Register()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)Item))
			{
				GameObject val = Resources.Load<GameObject>("items/Item Drone Indestructible");
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.GetComponent<ItemAttributes>()) || !Object.op_Implicit((Object)(object)val.GetComponent<ItemDroneIndestructible>()))
				{
					throw new InvalidOperationException("The vanilla drone template is unavailable.");
				}
				_templates = new GameObject("Repair Drone Templates");
				((Object)_templates).hideFlags = (HideFlags)61;
				_templates.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_templates);
				GameObject val2 = Object.Instantiate<GameObject>(val, _templates.transform);
				((Object)val2).name = "Item Drone Repair";
				val2.SetActive(true);
				((Behaviour)val2.GetComponent<ItemDroneIndestructible>()).enabled = false;
				val2.GetComponent<ItemBattery>().autoDrain = false;
				val2.AddComponent<RepairShopItem>();
				val2.AddComponent<RepairDroneController>();
				ItemAttributes component = val2.GetComponent<ItemAttributes>();
				Item val3 = Object.Instantiate<Item>(component.item);
				((Object)val3).name = "Item Drone Repair";
				val3.itemName = "Repair Drone";
				val3.itemNameLocalized = null;
				val3.description = "Repairs damaged valuables using battery power.";
				val3.disabled = false;
				val3.maxAmount = (val3.maxAmountInShop = 1);
				val3.minPlayerCount = 1;
				val3.maxPurchase = false;
				val3.itemSecretShopType = (itemSecretShopType)0;
				val3.value = Object.Instantiate<Value>(val3.value);
				val3.value.valueMin = 3500f;
				val3.value.valueMax = 4500f;
				component.item = val3;
				RepairVisuals.Apply(val2);
				if (Items.RegisterItem(component) == null)
				{
					throw new InvalidOperationException("REPOLib rejected the repair drone item.");
				}
				Template = val2;
				Item = val3;
				RepairService.Instance.Log.LogInfo((object)"Registered Repair Drone: vanilla shop pricing; full-charge repair=$12000 by default; all players require this item.");
			}
		}
	}
	[HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")]
	internal static class RepairShopStockPatch
	{
		private static void Postfix(ShopManager __instance)
		{
			Item item = RepairShopRegistration.Item;
			if (item == null || !Object.op_Implicit((Object)(object)item) || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			RepairService? instance = RepairService.Instance;
			if (instance != null && instance.PreferInShop && StatsManager.instance.GetItemPurchased(item) < item.maxAmountInShop)
			{
				__instance.potentialItems.RemoveAll((Item candidate) => (Object)(object)candidate == (Object)(object)item);
				__instance.potentialItems.Add(item);
			}
		}
	}
	internal static class RepairVisuals
	{
		internal const string BundleName = "repairdrone.visuals";

		private const string WrenchPath = "assets/repairdrone/repair-wrench.png";

		internal static void Apply(GameObject item)
		{
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			Transform val = item.transform.Find("Drone");
			Transform val2 = item.transform.Find("Drone Icon");
			ItemAttributes component = item.GetComponent<ItemAttributes>();
			ItemDrone component2 = item.GetComponent<ItemDrone>();
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)((Component)val).GetComponent<MeshRenderer>()) || !Object.op_Implicit((Object)(object)((Component)val2).GetComponent<MeshRenderer>()) || !Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component.item) || !Object.op_Implicit((Object)(object)component.item.colorPreset) || !Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)item.GetComponentInChildren<SemiIconMaker>(true)))
			{
				throw new InvalidOperationException("The vanilla drone visual hierarchy is unavailable.");
			}
			AssetBundle val3 = AssetBundle.LoadFromFile(Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location), "repairdrone.visuals"));
			if (!Object.op_Implicit((Object)(object)val3))
			{
				throw new InvalidOperationException("Repair Drone visual bundle is missing or invalid.");
			}
			try
			{
				Texture2D val4 = val3.LoadAsset<Texture2D>("assets/repairdrone/repair-wrench.png");
				if (!Object.op_Implicit((Object)(object)val4))
				{
					throw new InvalidOperationException("Repair Drone appearance textures are missing.");
				}
				ColorPresets val5 = Object.Instantiate<ColorPresets>(component.item.colorPreset);
				((Object)val5).name = "Repair Drone Colors";
				val5.colorMain = new Color(0.08f, 0.85f, 0.35f);
				val5.colorLight = new Color(0.37f, 1f, 0.64f);
				val5.colorDark = new Color(0.04f, 0.65f, 0.24f);
				component.item.colorPreset = val5;
				component2.droneIcon = (Texture)(object)val4;
				Dictionary<Material, Material> copies = new Dictionary<Material, Material>();
				MeshRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<MeshRenderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					CloneMaterials(componentsInChildren[i], copies, val5.colorMain);
				}
				MeshRenderer component3 = ((Component)val2).GetComponent<MeshRenderer>();
				CloneMaterials(component3, copies, val5.colorMain);
				((Renderer)component3).sharedMaterial.SetTexture("_EmissionMap", (Texture)(object)val4);
				item.AddComponent<RepairInventoryIcon>();
				Validate(item);
				RepairService.Instance.Log.LogInfo((object)"Loaded Repair Drone appearance: vanilla shell, green lighting, wrench symbol, and green beam.");
			}
			finally
			{
				val3.Unload(false);
			}
		}

		private static void CloneMaterials(MeshRenderer renderer, Dictionary<Material, Material> copies, Color color)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				if (!copies.TryGetValue(val, out Material value))
				{
					value = new Material(val);
					if (value.HasProperty("_EmissionColor"))
					{
						value.SetColor("_EmissionColor", color);
					}
					copies.Add(val, value);
				}
				sharedMaterials[i] = value;
			}
			((Renderer)renderer).sharedMaterials = sharedMaterials;
		}

		internal static void Validate(GameObject item)
		{
			string[] array = new string[2] { "Drone", "Drone Icon" };
			foreach (string text in array)
			{
				Transform val = item.transform.Find(text);
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((Component)val).GetComponent<MeshRenderer>()) || !((Renderer)((Component)val).GetComponent<MeshRenderer>()).enabled)
				{
					throw new InvalidOperationException("Repair Drone must retain the visible vanilla shell.");
				}
			}
			if (!Object.op_Implicit((Object)(object)item.GetComponent<ItemDrone>().droneIcon) || !Object.op_Implicit((Object)(object)item.GetComponent<RepairInventoryIcon>()))
			{
				throw new InvalidOperationException("Repair Drone appearance references did not survive cloning.");
			}
		}
	}
}