Decompiled source of EasyAutoHeal v1.0.1

plugins/EasyAutoHeal/EasyAutoHeal.dll

Decompiled 6 days ago
using System;
using System.Diagnostics;
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 UnityEngine;

[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: AssemblyCompany("EasyAutoHeal")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("EasyAutoHeal")]
[assembly: AssemblyTitle("EasyAutoHeal")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EasyAutoHeal
{
	[BepInPlugin("easyautoheal.client.only", "EasyAutoHeal", "1.0.1")]
	public class EasyAutoHealPlugin : BaseUnityPlugin
	{
		public const string Guid = "easyautoheal.client.only";

		public const string PluginName = "EasyAutoHeal";

		public const string PluginVersion = "1.0.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> FixedThreshold;

		internal static ConfigEntry<float> PercentThreshold;

		internal static ConfigEntry<int> MinFoods;

		internal static ConfigEntry<float> CheckInterval;

		internal static ConfigEntry<float> CooldownSeconds;

		internal static ConfigEntry<bool> StrongestFirst;

		internal static ConfigEntry<bool> ShowMessages;

		private float _checkTimer;

		private float _cooldownTimer;

		private static MethodInfo _miMessage;

		private static Type _messageTypeType;

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Auto Heal", "Enabled", true, "自动喝治疗蜜酒总开关。");
			FixedThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Auto Heal", "FixedHpThreshold", 20f, new ConfigDescription("固定血量阈值:当前生命值低于该值就触发(与百分比条件任一满足即可)。\n20 = 原版玩家基础血量 10 点的 2 倍、1 颗食物血量的一半左右,属于「快死了」的范围。\n注:满血时无论如何都不会触发(硬保护)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>()));
			PercentThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Auto Heal", "PercentHpThreshold", 10f, new ConfigDescription("百分比血量阈值:当前生命值 ÷ 生命上限(含食物加成)低于该百分比就触发。\n填 10 就是 10%(当前血量不足上限的 10% 时才喝),范围 1–100。\n对高血量角色(多食物上限几百滴)来说,这条往往比固定阈值更早生效。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			MinFoods = ((BaseUnityPlugin)this).Config.Bind<int>("1 - Auto Heal", "MinFoods", 2, new ConfigDescription("至少要吃下几个食物才允许自动喝药(0-3)。\n默认 2:空肚或只吃 1 个食物时绝不触发,避免刚出生/清空食物栏时浪费蜜酒。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			CheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Auto Heal", "CheckInterval", 0.5f, new ConfigDescription("检查血量的间隔(秒)。0.5 秒对救命来说足够快,也几乎不耗性能。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			CooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Auto Heal", "CooldownSeconds", 10f, new ConfigDescription("自动喝药之后的保护冷却(秒)。冷却期内不再触发;\n另外蜜酒回复效果没过期时游戏本身也不允许再喝(双保险)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			StrongestFirst = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Auto Heal", "StrongestFirst", true, "背包里同时有多种治疗蜜酒时先喝哪种:\ntrue = 先喝最强的(次级/中级/缓愈中回复量最大的);\nfalse = 先喝最弱的,把好蜜酒留到更危险的时候。");
			ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Auto Heal", "ShowMessage", true, "自动喝药时在屏幕左上角弹一条提示(含喝掉的名字)。");
			_miMessage = AccessTools.Method(typeof(Character), "Message", (Type[])null, (Type[])null);
			if (_miMessage != null)
			{
				_messageTypeType = _miMessage.GetParameters()[0].ParameterType;
			}
			Log.LogInfo((object)"EasyAutoHeal 1.0.1 已加载(纯客户端,无 Harmony 补丁,走 Update 轮询)。");
		}

		private void Update()
		{
			if (!Enabled.Value)
			{
				return;
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (_cooldownTimer > 0f)
			{
				_cooldownTimer -= unscaledDeltaTime;
				return;
			}
			_checkTimer += unscaledDeltaTime;
			if (_checkTimer < CheckInterval.Value)
			{
				return;
			}
			_checkTimer = 0f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return;
			}
			try
			{
				ItemData val = AutoHealEngine.TryDrink(localPlayer, this);
				if (val != null)
				{
					_cooldownTimer = CooldownSeconds.Value;
					if (ShowMessages.Value)
					{
						ShowTopLeftMessage(val);
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("自动喝药检查出错(本 tick 已跳过):" + ex.Message));
			}
		}

		private void ShowTopLeftMessage(ItemData item)
		{
			try
			{
				if (!(_miMessage == null) && !(_messageTypeType == null) && !((Object)(object)Player.m_localPlayer == (Object)null))
				{
					object obj = Enum.ToObject(_messageTypeType, 2);
					string name = item.m_shared.m_name;
					_miMessage.Invoke(Player.m_localPlayer, new object[5]
					{
						obj,
						"自动喝下 " + name,
						0,
						null,
						null
					});
				}
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("屏幕提示发送失败(不影响功能):" + ex.Message));
			}
		}
	}
	internal static class AutoHealEngine
	{
		public static ItemData TryDrink(Player player, EasyAutoHealPlugin plugin)
		{
			float health = ((Character)player).GetHealth();
			if (health <= 0f)
			{
				return null;
			}
			float maxHealth = ((Character)player).GetMaxHealth();
			if (maxHealth <= 0f)
			{
				return null;
			}
			if (health >= maxHealth - 0.5f)
			{
				return null;
			}
			float num = health / maxHealth * 100f;
			float value = EasyAutoHealPlugin.FixedThreshold.Value;
			float value2 = EasyAutoHealPlugin.PercentThreshold.Value;
			bool flag = health < value;
			bool flag2 = num < value2;
			if (!flag && !flag2)
			{
				return null;
			}
			int num2 = player.GetFoods()?.Count ?? 0;
			if (num2 < EasyAutoHealPlugin.MinFoods.Value)
			{
				return null;
			}
			int candidates;
			ItemData val = PickMead(player, EasyAutoHealPlugin.StrongestFirst.Value, out candidates);
			if (val == null)
			{
				EasyAutoHealPlugin.Log.LogDebug((object)($"低血量触发(hp={health:0.#}/{maxHealth:0.#} = {num:0.0}%)但背包里没有能喝的治疗蜜酒" + $"(候选 0);食物 {num2} 个"));
				return null;
			}
			if (((Humanoid)player).ConsumeItem(((Humanoid)player).GetInventory(), val, false))
			{
				EasyAutoHealPlugin.Log.LogInfo((object)($"低血量触发(hp={health:0.#}/{maxHealth:0.#} = {num:0.0}%;" + string.Format("阈值 固定<{0:0.#}{1} / 百分比<{2:0.#}{3};", value, flag ? " ✓" : "", value2, flag2 ? " ✓" : "") + $"食物 {num2} 个)→ 自动喝下 {val.m_shared.m_name}(剩余候选 {candidates - 1})"));
				return val;
			}
			return null;
		}

		private static ItemData PickMead(Player player, bool strongestFirst, out int candidates)
		{
			candidates = 0;
			ItemData val = null;
			float num = 0f;
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if (allItem?.m_shared != null && IsHealingMead(allItem.m_shared))
				{
					candidates++;
					float num2 = HealScore(allItem.m_shared.m_consumeStatusEffect);
					if (val == null || (strongestFirst && num2 > num) || (!strongestFirst && num2 < num))
					{
						val = allItem;
						num = num2;
					}
				}
			}
			return val;
		}

		private static bool IsHealingMead(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)shared.m_itemType != 2)
			{
				return false;
			}
			StatusEffect consumeStatusEffect = shared.m_consumeStatusEffect;
			if ((Object)(object)consumeStatusEffect == (Object)null)
			{
				return false;
			}
			SE_Stats val = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (!(val.m_healthUpFront > 0f) && !(val.m_healthOverTime > 0f))
			{
				return val.m_healthPerTick > 0f;
			}
			return true;
		}

		private static float HealScore(StatusEffect se)
		{
			SE_Stats val = (SE_Stats)(object)((se is SE_Stats) ? se : null);
			if ((Object)(object)val == (Object)null)
			{
				return 0f;
			}
			float num = val.m_healthOverTime * Mathf.Max(val.m_healthOverTimeDuration, 0f);
			float num2 = val.m_healthPerTick * Mathf.Max(val.m_healthOverTimeDuration / Mathf.Max(val.m_tickInterval, 0.01f), 0f);
			return val.m_healthUpFront + Mathf.Max(num, num2);
		}
	}
}