using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Peak.Afflictions; using Photon.Pun; using Photon.Realtime; 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("youxia173")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.4.0")] [assembly: AssemblyInformationalVersion("1.2.4")] [assembly: AssemblyProduct("CampfireFaerieAura")] [assembly: AssemblyTitle("youxia173.CampfireFaerieAura")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.4.0")] [module: UnverifiableCode] [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 CampfireFaerieAura { internal static class AuraHeal { private const float TeammateHealInterval = 0.1f; private const float PetrifyMinChunk = 0.01f; private const float StoveBuffRefreshInterval = 2f; private static float _teammateHealAcc; private static float _petrifyLocalAcc; private static float _petrifyRpcAcc; private static float _stoveBuffLastApplied; private static readonly Affliction_NoHunger StoveCampfireBuff = new Affliction_NoHunger { totalTime = 3f }; internal static readonly STATUSTYPE[] HealTypes; public static bool ShouldSkipSelfHealAsClient() { if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { return HostAuraSync.HostIsProvidingTeammateAura(); } return false; } private static float GetStatusAmount(CharacterAfflictions aff, STATUSTYPE type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) if ((int)type == 13) { CharacterData val = aff.character?.data; if (!((Object)(object)val != (Object)null)) { return 0f; } return (float)val.petrifyAmount / 100f; } return aff.GetCurrentStatus(type); } private static float TakePetrifyChunk(ref float acc, float add) { if (add <= 0f) { return 0f; } acc += add; if (acc < 0.01f) { return 0f; } float num = Mathf.Floor(acc * 100f) / 100f; acc -= num; return num; } public static void HealLocal(CharacterAfflictions? aff, float dt, bool applyModCold) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)aff == (Object)null) { return; } STATUSTYPE[] healTypes = HealTypes; foreach (STATUSTYPE val in healTypes) { float healAmount = Plugin.GetHealAmount(val, dt, applyModCold); if (healAmount <= 0f) { continue; } if ((int)val == 13) { if (GetStatusAmount(aff, val) <= 0f) { _petrifyLocalAcc = 0f; continue; } float num = TakePetrifyChunk(ref _petrifyLocalAcc, healAmount); if (num > 0f) { aff.SubtractStatus(val, num, false, false); } } else if (!(GetStatusAmount(aff, val) <= 0f)) { aff.SubtractStatus(val, healAmount, false, false); } } } public static void ApplyAura(IEnumerable inRangeCharacters, float dt, bool applyModCold, bool restoreExtraStamina = false) { if (dt <= 0f) { return; } Character localCharacter = Character.localCharacter; bool flag = ShouldSkipSelfHealAsClient(); foreach (Character inRangeCharacter in inRangeCharacters) { if (!((Object)(object)inRangeCharacter == (Object)null) && !((Object)(object)inRangeCharacter.data == (Object)null) && !inRangeCharacter.data.dead && (Object)(object)inRangeCharacter == (Object)(object)localCharacter) { if (!flag) { HealLocal(inRangeCharacter.refs?.afflictions, dt, applyModCold); } if (restoreExtraStamina) { RestoreLocalExtraStamina(localCharacter, dt); } } } if (!HostAuraSync.IsProvidingTeammateAura() || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) { return; } _teammateHealAcc += dt; if (_teammateHealAcc < 0.1f) { return; } float teammateHealAcc = _teammateHealAcc; _teammateHealAcc = 0f; float[] array = BuildHealDeltas(teammateHealAcc, applyModCold); if (array == null) { return; } foreach (Character inRangeCharacter2 in inRangeCharacters) { if (!((Object)(object)inRangeCharacter2 == (Object)null) && !((Object)(object)inRangeCharacter2 == (Object)(object)localCharacter) && !((Object)(object)inRangeCharacter2.data == (Object)null) && !inRangeCharacter2.data.dead) { CharacterAfflictions val = inRangeCharacter2.refs?.afflictions; if (!((Object)(object)val == (Object)null) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null)) { ((MonoBehaviourPun)val).photonView.RPC("RPC_ApplyStatusesFromFloatArray", (RpcTarget)0, new object[1] { array }); } } } } private static void RestoreLocalExtraStamina(Character? local, float dt) { if ((Object)(object)local == (Object)null || !local.IsLocal || !Plugin.RestoreExtraStamina.Value) { return; } float num = Plugin.ExtraStaminaPerSecond.Value * dt; if (num <= 0f) { return; } float num2 = 1f - (float)local.data.petrifyAmount * 0.01f; float num3 = Mathf.Clamp(Plugin.ExtraStaminaCap.Value, 0f, 1f); float num4 = Mathf.Min(num2, num3); float extraStamina = local.data.extraStamina; if (extraStamina >= num4 - 0.0001f) { return; } num = Mathf.Min(num, num4 - extraStamina); if (!(num <= 0f)) { local.AddExtraStamina(num); if ((Object)(object)GUIManager.instance?.bar != (Object)null) { GUIManager.instance.bar.ChangeBar(); } } } private static float[]? BuildHealDeltas(float dt, bool applyModCold) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 float[] array = new float[CharacterAfflictions.NumStatusTypes]; bool flag = false; STATUSTYPE[] healTypes = HealTypes; foreach (STATUSTYPE val in healTypes) { float healAmount = Plugin.GetHealAmount(val, dt, applyModCold); if (healAmount <= 0f) { continue; } int num = (int)val; if (num < 0 || num >= array.Length) { continue; } if ((int)val == 13) { float num2 = TakePetrifyChunk(ref _petrifyRpcAcc, healAmount); if (!(num2 <= 0f)) { array[num] = 0f - num2; flag = true; } } else { array[num] = 0f - healAmount; flag = true; } } if (!flag) { return null; } return array; } public static bool IsPortableStoveColdEmitter(StatusEmitter emitter) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((Object)(object)emitter == (Object)null) { return false; } if ((int)emitter.statusType != 2) { return false; } if (emitter.amount >= 0f) { return false; } Transform val = ((Component)emitter).transform; while ((Object)(object)val != (Object)null) { string name = ((Object)val).name; if (name.IndexOf("PortableStove", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("PortableStovetop", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Portable Stove", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } val = val.parent; } return false; } public static bool IsPortableStoveFireLit(StatusEmitter emitter) { if ((Object)(object)emitter == (Object)null) { return false; } Campfire componentInParent = ((Component)emitter).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { if (!componentInParent.Lit) { return false; } if ((Object)(object)componentInParent.enableWhenLit != (Object)null && !componentInParent.enableWhenLit.activeInHierarchy) { return false; } return true; } return ((Behaviour)emitter).isActiveAndEnabled; } public static void RefreshStoveCampfireIconForLocal(bool localInStoveRange) { if (!Plugin.ShowCampfireIconOnStove.Value || !localInStoveRange) { return; } CharacterAfflictions val = Character.localCharacter?.refs?.afflictions; if (!((Object)(object)val == (Object)null)) { float totalTime = Mathf.Max(1f, Plugin.PortableStoveBuffDuration.Value); ((Affliction)StoveCampfireBuff).totalTime = totalTime; if (val.canGetHungry || Time.time - _stoveBuffLastApplied > 2f) { val.AddAffliction((Affliction)(object)StoveCampfireBuff, false); _stoveBuffLastApplied = Time.time; } } } public static void ClearStoveCampfireIconForLocal() { Character localCharacter = Character.localCharacter; CharacterAfflictions val = localCharacter?.refs?.afflictions; if (!((Object)(object)val == (Object)null) && !((Object)(object)localCharacter == (Object)null) && !IsLocalNearLitCampfire(localCharacter)) { if (!val.canGetHungry) { val.RemoveAffliction((AfflictionType)21); } _stoveBuffLastApplied = 0f; } } private static bool IsLocalNearLitCampfire(Character local) { if ((Object)(object)local == (Object)null) { return false; } if (!(AccessTools.Field(typeof(Campfire), "ALL_CAMPFIRES")?.GetValue(null) is List list)) { return false; } foreach (Campfire item in list) { if (!((Object)(object)item == (Object)null) && item.Lit) { List list2 = item.PlayerCharactersInRadius(item.moraleBoostRadius); if (list2 != null && list2.Contains(local)) { return true; } } } return false; } static AuraHeal() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown STATUSTYPE[] array = new STATUSTYPE[9]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); HealTypes = (STATUSTYPE[])(object)array; } } internal static class HostAuraSync { public const string RoomPropertyKey = "yx173_CFA_HTA"; private static bool? _lastSyncedProviding; public static bool IsProvidingTeammateAura() { if (Plugin.Enabled.Value) { return Plugin.AffectTeammatesAsHost.Value; } return false; } public static void SyncIfHost() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_005b: Expected O, but got Unknown if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) { if (!PhotonNetwork.InRoom) { _lastSyncedProviding = null; } return; } bool flag = IsProvidingTeammateAura(); if (_lastSyncedProviding != flag) { Hashtable val = new Hashtable(); ((Dictionary)val).Add((object)"yx173_CFA_HTA", (object)flag); Hashtable val2 = val; PhotonNetwork.CurrentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null); _lastSyncedProviding = flag; Plugin.Log.LogInfo((object)$"已同步主机队友光环状态到房间: {flag}"); } } public static bool HostIsProvidingTeammateAura() { if (!PhotonNetwork.InRoom) { return false; } Room currentRoom = PhotonNetwork.CurrentRoom; if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) == null) { return false; } if (((Dictionary)(object)((RoomInfo)currentRoom).CustomProperties).TryGetValue((object)"yx173_CFA_HTA", out object value) && value is bool) { return (bool)value; } return false; } public static void ResetLocalCache() { _lastSyncedProviding = null; } } [HarmonyPatch(typeof(Campfire), "ApplyCampfireProtection")] internal static class Campfire_ApplyCampfireProtection_Patch { [HarmonyPostfix] private static void Postfix(Campfire __instance) { if (!Plugin.Enabled.Value || (Object)(object)__instance == (Object)null || !__instance.Lit) { return; } float deltaTime = Time.deltaTime; if (!(deltaTime <= 0f)) { List list = __instance.PlayerCharactersInRadius(__instance.moraleBoostRadius); if (list != null && list.Count != 0) { AuraHeal.ApplyAura(list, deltaTime, applyModCold: true, restoreExtraStamina: true); } } } } [HarmonyPatch(typeof(StatusEmitter), "Update")] internal static class StatusEmitter_Update_Patch { [HarmonyPostfix] private static void Postfix(StatusEmitter __instance) { if (Plugin.Enabled.Value && Plugin.EnablePortableStove.Value && !((Object)(object)__instance == (Object)null) && ((Behaviour)__instance).isActiveAndEnabled && !__instance.emitterDisabledByWind && AuraHeal.IsPortableStoveColdEmitter(__instance) && AuraHeal.IsPortableStoveFireLit(__instance)) { StoveAuraTracker.NotifyLitEmitter(__instance); } } } [BepInPlugin("youxia173.CampfireFaerieAura", "CampfireFaerieAura", "1.2.4")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; public static ConfigEntry Enabled; public static ConfigEntry AffectTeammatesAsHost; public static ConfigEntry EnablePortableStove; public static ConfigEntry HealHot; public static ConfigEntry HealCold; public static ConfigEntry HealPoison; public static ConfigEntry HealDrowsy; public static ConfigEntry HealInjury; public static ConfigEntry HealSpores; public static ConfigEntry HealHunger; public static ConfigEntry HealPetrify; public static ConfigEntry HealCurse; public static ConfigEntry RestoreExtraStamina; public static ConfigEntry ShowCampfireIconOnStove; public static ConfigEntry PortableStoveBuffDuration; public static ConfigEntry HotPerSecond; public static ConfigEntry ColdPerSecond; public static ConfigEntry PoisonPerSecond; public static ConfigEntry DrowsyPerSecond; public static ConfigEntry InjuryPerSecond; public static ConfigEntry SporesPerSecond; public static ConfigEntry HungerRestorePerSecond; public static ConfigEntry PetrifyPerSecond; public static ConfigEntry CursePerSecond; public static ConfigEntry ExtraStaminaPerSecond; public static ConfigEntry ExtraStaminaCap; private Harmony? _harmony; private void Awake() { //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Expected O, but got Unknown //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Expected O, but got Unknown //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Expected O, but got Unknown //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Expected O, but got Unknown //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Expected O, but got Unknown //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Expected O, but got Unknown //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Expected O, but got Unknown //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03eb: Expected O, but got Unknown //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Expected O, but got Unknown //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Expected O, but got Unknown //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Expected O, but got Unknown //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0509: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "总开关。关闭后完全不生效(含主机队友光环)。 / Master switch. When off, the mod does nothing (including host teammate aura)."); AffectTeammatesAsHost = ((BaseUnityPlugin)this).Config.Bind("General", "AffectTeammatesAsHost", false, "仅主机:篝火/便携炉范围内队友也享受疗愈(队友可不装本模组)。写入房间属性;装了本模组的客机会跳过自疗避免双倍。 / Host only: also heal teammates in campfire/stove range (they need not install this mod). Writes a room property; clients with this mod skip self-heal to avoid double healing."); EnablePortableStove = ((BaseUnityPlugin)this).Config.Bind("General", "EnablePortableStove", true, "便携炉范围内也套用本模组疗愈。寒冷仍只走原版炉子(不叠加模组减寒);篝火寒冷用 HealCold / ColdPerSecond。 / Apply mod heals near portable stoves. Cold still uses vanilla stove only (no mod cold stack); campfire cold uses HealCold / ColdPerSecond."); HealHot = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealHot", false, "是否疗愈灼热。 / Heal Hot."); HealCold = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealCold", true, "是否疗愈寒冷(仅篝火;便携炉用原版)。 / Heal Cold (campfire only; stove uses vanilla)."); HealPoison = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealPoison", true, "是否疗愈中毒。 / Heal Poison."); HealDrowsy = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealDrowsy", true, "是否疗愈昏睡。 / Heal Drowsy."); HealInjury = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealInjury", true, "是否疗愈受伤。 / Heal Injury."); HealSpores = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealSpores", true, "是否疗愈孢子。 / Heal Spores."); HealHunger = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealHunger", true, "是否降低饥饿(恢复饱食)。 / Reduce Hunger (restore satiety)."); HealPetrify = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealPetrify", true, "是否疗愈石化。 / Heal Petrify."); HealCurse = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "HealCurse", true, "是否疗愈诅咒。 / Heal Curse."); RestoreExtraStamina = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "RestoreExtraStamina", true, "篝火/便携炉范围内是否缓慢恢复额外体力条(便携炉还需 EnablePortableStove)。 / Slowly restore bonus/extra stamina near lit campfire or portable stove (stove also needs EnablePortableStove)."); ShowCampfireIconOnStove = ((BaseUnityPlugin)this).Config.Bind("HealToggles", "ShowCampfireIconOnStove", true, "便携炉疗愈生效时点亮体力条篝火图标(与原版篝火相同)。 / Light the stamina-bar campfire icon while portable-stove aura is active."); PortableStoveBuffDuration = ((BaseUnityPlugin)this).Config.Bind("General", "PortableStoveBuffDuration", 5f, new ConfigDescription("便携炉熄灭后,若仍站在炉旁(原减寒范围),模组疗愈与图标再持续的秒数。炉子燃烧期间不受此限制;不影响原版减寒。0 = 熄灭立刻结束。 / Seconds our stove heal+icon continues after the stove extinguishes while still in cold-emitter range. Unlimited while the stove is lit; does not affect vanilla cold. 0 = end immediately on extinguish.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 120f), Array.Empty())); HotPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "HotPerSecond", 0.005f, new ConfigDescription("每秒减少的状态量(0~1)。例如 0.005 ≈ 每秒 0.5%。0 = 关闭。默认约仙子提灯的 1/5。 / Status removed per second (0~1). e.g. 0.005 ≈ 0.5%/s. 0 = off. Default ~1/5 of Faerie lantern.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ColdPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "ColdPerSecond", 0.05f, new ConfigDescription("每秒减少的寒冷(0~1)。原版便携炉约 0.05/秒。0 = 关闭。 / Cold removed per second (0~1). Vanilla stove is about 0.05/s. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PoisonPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "PoisonPerSecond", 0.005f, new ConfigDescription("每秒减少的状态量(0~1)。例如 0.005 ≈ 每秒 0.5%。0 = 关闭。默认约仙子提灯的 1/5。 / Status removed per second (0~1). e.g. 0.005 ≈ 0.5%/s. 0 = off. Default ~1/5 of Faerie lantern.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DrowsyPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "DrowsyPerSecond", 0.01f, new ConfigDescription("每秒减少的状态量(0~1)。例如 0.005 ≈ 每秒 0.5%。0 = 关闭。默认约仙子提灯的 1/5。 / Status removed per second (0~1). e.g. 0.005 ≈ 0.5%/s. 0 = off. Default ~1/5 of Faerie lantern.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); InjuryPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "InjuryPerSecond", 0.005f, new ConfigDescription("每秒减少的状态量(0~1)。例如 0.005 ≈ 每秒 0.5%。0 = 关闭。默认约仙子提灯的 1/5。 / Status removed per second (0~1). e.g. 0.005 ≈ 0.5%/s. 0 = off. Default ~1/5 of Faerie lantern.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); SporesPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "SporesPerSecond", 0.005f, new ConfigDescription("每秒减少的状态量(0~1)。例如 0.005 ≈ 每秒 0.5%。0 = 关闭。默认约仙子提灯的 1/5。 / Status removed per second (0~1). e.g. 0.005 ≈ 0.5%/s. 0 = off. Default ~1/5 of Faerie lantern.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); HungerRestorePerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "HungerPerSecond", 0.005f, new ConfigDescription("每秒减少的饥饿。篝火原版只会暂停饥饿上涨;此项会主动降低 Hunger。 / Hunger reduced per second. Vanilla campfire only pauses hunger gain; this actively lowers it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PetrifyPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "PetrifyPerSecond", 0.005f, new ConfigDescription("每秒减少的石化(0~1)。引擎按 1% 步进取整,需累计到 ≥0.01。 / Petrify removed per second (0~1). Engine floors to 1% steps; accumulates until >= 0.01.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); CursePerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "CursePerSecond", 0.005f, new ConfigDescription("每秒减少的诅咒(0~1)。默认与石化相同。 / Curse removed per second (0~1). Default same as Petrify.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ExtraStaminaPerSecond = ((BaseUnityPlugin)this).Config.Bind("HealRates", "ExtraStaminaPerSecond", 0.01f, new ConfigDescription("篝火/便携炉旁每秒增加的额外体力(0~1)。0.01 ≈ 每秒 1%。0 = 关闭。 / Extra stamina gained per second near campfire/stove (0~1). 0.01 ≈ 1%/s. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ExtraStaminaCap = ((BaseUnityPlugin)this).Config.Bind("HealRates", "ExtraStaminaCap", 0.5f, new ConfigDescription("本模组恢复额外体力的上限(0~1)。默认 0.5 = 最多回满到 50%;已高于上限则不再回。仍受石化对额外体力上限的限制。 / Cap for mod-restored extra stamina (0~1). Default 0.5 = stop at 50%. Won't restore if already above. Still limited by petrify.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); Enabled.SettingChanged += delegate { HostAuraSync.ResetLocalCache(); }; AffectTeammatesAsHost.SettingChanged += delegate { HostAuraSync.ResetLocalCache(); }; _harmony = new Harmony("youxia173.CampfireFaerieAura"); _harmony.PatchAll(); Log.LogInfo((object)("CampfireFaerieAura v1.2.4 by youxia173 loaded. " + $"Enabled={Enabled.Value} AffectTeammatesAsHost={AffectTeammatesAsHost.Value}")); } private void Update() { HostAuraSync.SyncIfHost(); StoveAuraTracker.Tick(Time.deltaTime); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static float GetHealAmount(STATUSTYPE type, float dt, bool applyModCold = true) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected I4, but got Unknown if (dt <= 0f) { return 0f; } switch ((int)type) { case 8: if (!HealHot.Value) { return 0f; } return HotPerSecond.Value * dt; case 2: if (!applyModCold || !HealCold.Value) { return 0f; } return ColdPerSecond.Value * dt; case 3: if (!HealPoison.Value) { return 0f; } return PoisonPerSecond.Value * dt; case 6: if (!HealDrowsy.Value) { return 0f; } return DrowsyPerSecond.Value * dt; case 0: if (!HealInjury.Value) { return 0f; } return InjuryPerSecond.Value * dt; case 10: if (!HealSpores.Value) { return 0f; } return SporesPerSecond.Value * dt; case 1: if (!HealHunger.Value) { return 0f; } return HungerRestorePerSecond.Value * dt; case 13: if (!HealPetrify.Value) { return 0f; } return PetrifyPerSecond.Value * dt; case 5: if (!HealCurse.Value) { return 0f; } return CursePerSecond.Value * dt; default: return 0f; } } } internal static class StoveAuraTracker { private sealed class StoveState { public StatusEmitter? Emitter; public Vector3 LastPos; public float Range; public bool WasLit; public float ExtinguishedAt = -1f; public bool LingerActive; } private static readonly Dictionary _stoves = new Dictionary(); private static bool _iconForcedOff; public static void NotifyLitEmitter(StatusEmitter emitter) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)emitter == (Object)null)) { int instanceID = ((Object)emitter).GetInstanceID(); if (!_stoves.TryGetValue(instanceID, out StoveState value)) { value = new StoveState(); _stoves[instanceID] = value; } value.Emitter = emitter; value.LastPos = ((Component)emitter).transform.position; value.Range = emitter.radius + emitter.outerFade; value.WasLit = true; value.ExtinguishedAt = -1f; value.LingerActive = false; } } public static void Tick(float dt) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value || !Plugin.EnablePortableStove.Value) { if (_stoves.Count > 0) { ClearAllAndExtinguishIcon(); } } else { if (dt <= 0f) { return; } float num = Mathf.Max(0f, Plugin.PortableStoveBuffDuration.Value); Character localCharacter = Character.localCharacter; bool flag = false; List list = new List(); foreach (KeyValuePair stofe in _stoves) { StoveState value = stofe.Value; bool flag2 = IsEmitterLit(value); if (flag2) { if ((Object)(object)value.Emitter != (Object)null) { value.LastPos = ((Component)value.Emitter).transform.position; value.Range = value.Emitter.radius + value.Emitter.outerFade; } value.WasLit = true; value.ExtinguishedAt = -1f; value.LingerActive = false; } else if (value.WasLit) { value.WasLit = false; if (!((Object)(object)localCharacter != (Object)null) || !IsNear(localCharacter, value) || !(num > 0f)) { list.Add(stofe.Key); continue; } value.ExtinguishedAt = Time.time; value.LingerActive = true; } else { if (!value.LingerActive) { list.Add(stofe.Key); continue; } if (Time.time - value.ExtinguishedAt >= num || (Object)(object)localCharacter == (Object)null || !IsNear(localCharacter, value)) { list.Add(stofe.Key); continue; } } if (!flag2 && !value.LingerActive) { list.Add(stofe.Key); continue; } List list2 = CollectInRange(value); if (list2.Count == 0) { if (value.LingerActive) { list.Add(stofe.Key); } continue; } AuraHeal.ApplyAura(list2, dt, applyModCold: false, restoreExtraStamina: true); if ((Object)(object)localCharacter != (Object)null && list2.Contains(localCharacter)) { flag = true; } } foreach (int item in list) { _stoves.Remove(item); } if (flag) { _iconForcedOff = false; AuraHeal.RefreshStoveCampfireIconForLocal(localInStoveRange: true); } else if (!_iconForcedOff) { AuraHeal.ClearStoveCampfireIconForLocal(); _iconForcedOff = true; } } } private static bool IsEmitterLit(StoveState state) { StatusEmitter emitter = state.Emitter; if ((Object)(object)emitter == (Object)null) { return false; } if (!Object.op_Implicit((Object)(object)emitter)) { state.Emitter = null; return false; } if (emitter.emitterDisabledByWind) { return false; } if (!AuraHeal.IsPortableStoveColdEmitter(emitter)) { return false; } if (!AuraHeal.IsPortableStoveFireLit(emitter)) { return false; } return true; } private static bool IsNear(Character c, StoveState state) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c?.data == (Object)null || c.data.dead) { return false; } return Vector3.Distance(state.LastPos, c.Center) <= state.Range; } private static List CollectInRange(StoveState state) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!((Object)(object)allPlayerCharacter == (Object)null) && !((Object)(object)allPlayerCharacter.data == (Object)null) && !allPlayerCharacter.data.dead && !(Vector3.Distance(state.LastPos, allPlayerCharacter.Center) > state.Range)) { list.Add(allPlayerCharacter); } } return list; } private static void ClearAllAndExtinguishIcon() { _stoves.Clear(); AuraHeal.ClearStoveCampfireIconForLocal(); _iconForcedOff = true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "youxia173.CampfireFaerieAura"; public const string PLUGIN_NAME = "CampfireFaerieAura"; public const string PLUGIN_VERSION = "1.2.4"; } }