Decompiled source of HealthExchange v1.0.0

Zichen-HealthExchange.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Zichen-HealthExchange")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5431d17fbaba3009862f5717951e046f72b02bf3")]
[assembly: AssemblyProduct("Zichen-HealthExchange")]
[assembly: AssemblyTitle("Zichen-HealthExchange")]
[assembly: AssemblyVersion("1.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;
		}
	}
}
[BepInPlugin("zichen.healthexchange", "A.Health Exchange", "1.0.0")]
public sealed class WzcHealthExchangePlugin : BaseUnityPlugin
{
	public const string PluginGuid = "zichen.healthexchange";

	public const string PluginName = "A.Health Exchange";

	public const string PluginVersion = "1.0.0";

	private const string InfoSection = "模组信息";

	private const string ExchangeSection = "A.血量交换";

	private const string ModeDrain = "吸取队友血量";

	private const string ModeBalance = "平均分血量";

	private const string ModeOfficial = "传输队友血量";

	private const string ModeDisabled = "禁用传输";

	private static readonly FieldInfo PlayerHealthGrabStaticGrabObjectField = typeof(PlayerHealthGrab).GetField("staticGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthGrabPhysColliderField = typeof(PlayerHealthGrab).GetField("physCollider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthGrabColliderActiveField = typeof(PlayerHealthGrab).GetField("colliderActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthGrabGrabbingTimerField = typeof(PlayerHealthGrab).GetField("grabbingTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthGrabHideLerpField = typeof(PlayerHealthGrab).GetField("hideLerp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo StaticGrabObjectPlayerGrabbingField = typeof(StaticGrabObject).GetField("playerGrabbing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthHealthField = typeof(PlayerHealth).GetField("health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerHealthMaxHealthField = typeof(PlayerHealth).GetField("maxHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerAvatarIsTumblingField = typeof(PlayerAvatar).GetField("isTumbling", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerAvatarIsDisabledField = typeof(PlayerAvatar).GetField("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo RunManagerLevelIsShopField = typeof(RunManager).GetField("levelIsShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private Harmony harmony;

	private ConfigEntry<string> moduleNameInfo;

	private ConfigEntry<string> moduleVersionInfo;

	private ConfigEntry<string> contactInfo;

	private ConfigEntry<bool> featureEnabled;

	private ConfigEntry<string> exchangeMode;

	private ConfigEntry<string> transferRateMultiplier;

	public static WzcHealthExchangePlugin Instance { get; private set; }

	private void Awake()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		Instance = this;
		BindConfig();
		harmony = new Harmony("zichen.healthexchange.patch");
		harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerHealthGrab), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(WzcHealthExchangePlugin), "PlayerHealthGrabUpdatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-health-exchange loaded.");
	}

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

	private void BindConfig()
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Expected O, but got Unknown
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Expected O, but got Unknown
		//IL_0299: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a3: Expected O, but got Unknown
		moduleNameInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "模组名称", "Zichen_血量交换", new ConfigDescription("当前模组的中文名称。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 1000,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		moduleNameInfo.Value = "Health Exchange / 血量交换";
		moduleVersionInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "模组版本号", "1.0.0", new ConfigDescription("当前模组版本号。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 990,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		moduleVersionInfo.Value = "1.0.0";
		contactInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "REPO交流QQ群", "824639225", new ConfigDescription("REPO 游戏交流、BUG 反馈、优化建议、功能请求请加 QQ 群。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 980,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		contactInfo.Value = "824639225";
		featureEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("A.血量交换", "启用", true, ConfigDescriptionWithOrder("只需要主机安装即可生效。用于修改抓住队友后的血量交换逻辑。", 900));
		exchangeMode = ((BaseUnityPlugin)this).Config.Bind<string>("A.血量交换", "血量交换模式", "吸取队友血量", new ConfigDescription("选择抓住队友后使用哪种血量交换方式。", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "吸取队友血量", "平均分血量", "传输队友血量", "禁用传输" }), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 890
			}
		}));
		transferRateMultiplier = ((BaseUnityPlugin)this).Config.Bind<string>("A.血量交换", "血量传输倍率", "1", new ConfigDescription("按官方单次 10 血量为 1 倍进行放大。例如 5 倍时,单次最多传输 50 血量,实际仍以 10 为单位传输。只能选择 1 到 10 倍。", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[10] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 880
			}
		}));
	}

	private void DrawInfo(ConfigEntryBase entry)
	{
		GUILayout.Label(entry.BoxedValue?.ToString() ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
	}

	private static ConfigDescription ConfigDescriptionWithOrder(string description, int order)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		return new ConfigDescription(description, (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = order
			}
		});
	}

	private bool IsFeatureEnabled()
	{
		if (featureEnabled != null)
		{
			return featureEnabled.Value;
		}
		return false;
	}

	private int GetConfiguredTransferAmount()
	{
		int result = 1;
		if (transferRateMultiplier != null && !string.IsNullOrEmpty(transferRateMultiplier.Value))
		{
			int.TryParse(transferRateMultiplier.Value, out result);
		}
		return Mathf.Clamp(result, 1, 10) * 10;
	}

	private string GetMode()
	{
		if (exchangeMode != null)
		{
			return exchangeMode.Value;
		}
		return "传输队友血量";
	}

	private static bool PlayerHealthGrabUpdatePrefix(PlayerHealthGrab __instance)
	{
		WzcHealthExchangePlugin instance = Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)__instance == (Object)null)
		{
			return true;
		}
		if (!instance.IsFeatureEnabled())
		{
			return true;
		}
		instance.RunCustomHealthGrabUpdate(__instance);
		return false;
	}

	private void RunCustomHealthGrabUpdate(PlayerHealthGrab instance)
	{
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)instance.playerAvatar == (Object)null)
		{
			return;
		}
		float num = GetFieldValue(PlayerHealthGrabHideLerpField, instance, 0f);
		bool fieldValue = GetFieldValue(PlayerAvatarIsTumblingField, instance.playerAvatar, fallback: false);
		bool flag = (Object)(object)RunManager.instance != (Object)null && GetFieldValue(RunManagerLevelIsShopField, RunManager.instance, fallback: false);
		if (fieldValue || SemiFunc.RunIsShop() || flag || SemiFunc.RunIsArena())
		{
			if (num < 1f)
			{
				num += Time.deltaTime * 5f;
				num = Mathf.Clamp(num, 0f, 1f);
				instance.hideTransform.localScale = new Vector3(1f, instance.hideCurve.Evaluate(num), 1f);
				if (num >= 1f)
				{
					((Component)instance.hideTransform).gameObject.SetActive(false);
				}
				SetFieldValue(PlayerHealthGrabHideLerpField, instance, num);
			}
		}
		else if (num > 0f)
		{
			if (!((Component)instance.hideTransform).gameObject.activeSelf)
			{
				((Component)instance.hideTransform).gameObject.SetActive(true);
			}
			num -= Time.deltaTime * 2f;
			num = Mathf.Clamp(num, 0f, 1f);
			instance.hideTransform.localScale = new Vector3(1f, instance.hideCurve.Evaluate(num), 1f);
			SetFieldValue(PlayerHealthGrabHideLerpField, instance, num);
		}
		bool flag2 = !GetFieldValue(PlayerAvatarIsDisabledField, instance.playerAvatar, fallback: false) && num <= 0f;
		bool flag3 = GetFieldValue(PlayerHealthGrabColliderActiveField, instance, fallback: true);
		if (flag3 != flag2)
		{
			flag3 = flag2;
			SetFieldValue(PlayerHealthGrabColliderActiveField, instance, flag3);
			object? obj = PlayerHealthGrabPhysColliderField?.GetValue(instance);
			Collider val = (Collider)((obj is Collider) ? obj : null);
			if ((Object)(object)val != (Object)null)
			{
				val.enabled = flag3;
			}
		}
		((Component)instance).transform.position = instance.followTransform.position;
		((Component)instance).transform.rotation = instance.followTransform.rotation;
		if (!flag3 || (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient))
		{
			return;
		}
		object? obj2 = PlayerHealthGrabStaticGrabObjectField?.GetValue(instance);
		StaticGrabObject val2 = (StaticGrabObject)((obj2 is StaticGrabObject) ? obj2 : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		if (!(StaticGrabObjectPlayerGrabbingField?.GetValue(val2) is IList list) || list.Count == 0)
		{
			SetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, 0f);
			return;
		}
		float num2 = GetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, 0f) + Time.deltaTime;
		if (num2 >= 1f)
		{
			for (int i = 0; i < list.Count; i++)
			{
				object? obj3 = list[i];
				PhysGrabber val3 = (PhysGrabber)((obj3 is PhysGrabber) ? obj3 : null);
				if (!((Object)(object)val3?.playerAvatar == (Object)null))
				{
					ProcessExchange(instance.playerAvatar, val3.playerAvatar);
				}
			}
			num2 = 0f;
		}
		SetFieldValue(PlayerHealthGrabGrabbingTimerField, instance, num2);
	}

	private void ProcessExchange(PlayerAvatar targetAvatar, PlayerAvatar sourceAvatar)
	{
		if ((Object)(object)targetAvatar == (Object)null || (Object)(object)sourceAvatar == (Object)null || (Object)(object)targetAvatar.playerHealth == (Object)null || (Object)(object)sourceAvatar.playerHealth == (Object)null)
		{
			return;
		}
		int configuredTransferAmount = GetConfiguredTransferAmount();
		string mode = GetMode();
		if (string.Equals(mode, "禁用传输", StringComparison.Ordinal))
		{
			return;
		}
		if (string.Equals(mode, "吸取队友血量", StringComparison.Ordinal))
		{
			TransferHealth(targetAvatar, sourceAvatar, configuredTransferAmount, notifyDonor: false);
		}
		else if (string.Equals(mode, "平均分血量", StringComparison.Ordinal))
		{
			int health = GetHealth(targetAvatar.playerHealth);
			int health2 = GetHealth(sourceAvatar.playerHealth);
			int num = NormalizeHealthForBalance(health);
			int num2 = NormalizeHealthForBalance(health2);
			if (num == num2)
			{
				return;
			}
			int balanceTransferAmount = GetBalanceTransferAmount(Mathf.Abs(num2 - num), configuredTransferAmount);
			if (balanceTransferAmount >= 10)
			{
				if (num2 > num)
				{
					TransferHealth(sourceAvatar, targetAvatar, balanceTransferAmount, notifyDonor: true);
				}
				else if (num > num2)
				{
					TransferHealth(targetAvatar, sourceAvatar, balanceTransferAmount, notifyDonor: false);
				}
			}
		}
		else
		{
			TransferHealth(sourceAvatar, targetAvatar, configuredTransferAmount, notifyDonor: true);
		}
	}

	private static void TransferHealth(PlayerAvatar donorAvatar, PlayerAvatar receiverAvatar, int desiredAmount, bool notifyDonor)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)donorAvatar == (Object)null || (Object)(object)receiverAvatar == (Object)null || (Object)(object)donorAvatar.playerHealth == (Object)null || (Object)(object)receiverAvatar.playerHealth == (Object)null)
		{
			return;
		}
		int health = GetHealth(donorAvatar.playerHealth);
		int health2 = GetHealth(receiverAvatar.playerHealth);
		int maxHealth = GetMaxHealth(receiverAvatar.playerHealth);
		if (health <= 1 || health2 >= maxHealth)
		{
			return;
		}
		int num = NormalizeTransferAmount(Math.Min(desiredAmount, Math.Min(maxHealth - health2, health - 1)));
		if (num >= 10)
		{
			receiverAvatar.playerHealth.HealOther(num, true);
			donorAvatar.playerHealth.HurtOther(num, Vector3.zero, false, -1, false);
			if (notifyDonor)
			{
				donorAvatar.HealedOther();
			}
		}
	}

	private static int GetHealth(PlayerHealth playerHealth)
	{
		return GetFieldValue(PlayerHealthHealthField, playerHealth, 0);
	}

	private static int GetMaxHealth(PlayerHealth playerHealth)
	{
		return GetFieldValue(PlayerHealthMaxHealthField, playerHealth, 100);
	}

	private static int GetBalanceTransferAmount(int healthDifference, int maxTransferAmount)
	{
		int val = healthDifference / 2;
		return NormalizeTransferAmount(Math.Min(maxTransferAmount, val));
	}

	private static int NormalizeTransferAmount(int amount)
	{
		if (amount < 10)
		{
			return 0;
		}
		return amount / 10 * 10;
	}

	private static int NormalizeHealthForBalance(int health)
	{
		if (health < 10)
		{
			return health;
		}
		return health / 10 * 10;
	}

	private static T GetFieldValue<T>(FieldInfo field, object instance, T fallback)
	{
		try
		{
			if (field != null)
			{
				object value = field.GetValue(instance);
				if (value is T)
				{
					return (T)value;
				}
			}
		}
		catch
		{
		}
		return fallback;
	}

	private static void SetFieldValue<T>(FieldInfo field, object instance, T value)
	{
		try
		{
			field?.SetValue(instance, value);
		}
		catch
		{
		}
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public bool? ShowRangeAsPercent;

	public Action<ConfigEntryBase> CustomDrawer;

	public bool? Browsable;

	public string Category;

	public object DefaultValue;

	public bool? HideDefaultButton;

	public bool? HideSettingName;

	public string Description;

	public string DispName;

	public int? Order;

	public bool? ReadOnly;

	public bool? IsAdvanced;
}