Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EasyAutoHeal v1.0.1
plugins/EasyAutoHeal/EasyAutoHeal.dll
Decompiled 6 days agousing 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); } } }