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;
}