using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BlackPeakRemix; using ExitGames.Client.Photon; using HarmonyLib; using Lantern_ShootZombies_Night.Patches; using Microsoft.CodeAnalysis; using Peak.Network; using Photon.Pun; using Photon.Realtime; using ShootZombies; using TMPro; using UnityEngine; using UnityEngine.UI; using Zorro.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Lantern&ShootZombies&Night")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("P R C")] [assembly: AssemblyProduct("Lantern&ShootZombies&Night")] [assembly: AssemblyCopyright("Copyright © P R C 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("5959893d-12ff-4c20-a094-f57630a341a9")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } } namespace Lantern_ShootZombies_Night { public enum ConfigPreset { Custom, Casual, Balanced, Hardcore } internal static class BugleRecaller { [CompilerGenerated] private sealed class d__1 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private BugleSFX[] 5__2; private int 5__3; private List 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: { <>1__state = -1; 5__2 = Object.FindObjectsByType((FindObjectsSortMode)0); 5__3 = 0; 5__4 = new List(); BugleSFX[] array = 5__2; foreach (BugleSFX val2 in array) { if ((Object)(object)val2 == (Object)null || (Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) { continue; } int viewID = ((MonoBehaviourPun)val2).photonView.ViewID; PhotonView photonView = ((MonoBehaviourPun)val2).photonView; try { if (photonView.IsMine) { PhotonNetwork.Destroy(((Component)val2).gameObject); 5__3++; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[BugleRecall] phase1 destroyed ViewID={viewID}"); } continue; } Player owner2 = photonView.Owner; string arg2 = ((owner2 == null) ? "null(scene?)" : string.Format("{0}#{1}{2}", owner2.NickName, owner2.ActorNumber, owner2.IsInactive ? "[INACTIVE]" : "")); photonView.TransferOwnership(PhotonNetwork.LocalPlayer); 5__4.Add(viewID); ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[BugleRecall] phase1 requested ownership ViewID={viewID}, currentOwner={arg2}"); } } catch (Exception ex2) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)$"[BugleRecall] phase1 failed ViewID={viewID}: {ex2.Message}"); } } } if (5__4.Count == 0) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[BugleRecall] done, destroyed={5__3}/{5__2.Length}"); } return false; } <>2__current = (object)new WaitForSeconds(0.8f); <>1__state = 1; return true; } case 1: { <>1__state = -1; int num = 0; int num2 = 0; foreach (int item in 5__4) { PhotonView val = PhotonView.Find(item); if ((Object)(object)val == (Object)null) { continue; } try { if (val.IsMine) { PhotonNetwork.Destroy(((Component)val).gameObject); num++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[BugleRecall] phase2 destroyed ViewID={item}"); } continue; } num2++; Player owner = val.Owner; string arg = ((owner == null) ? "null(scene?)" : string.Format("{0}#{1}{2}", owner.NickName, owner.ActorNumber, owner.IsInactive ? "[INACTIVE]" : "")); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"[BugleRecall] phase2 still not owned ViewID={item}, currentOwner={arg}, giving up this round"); } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)$"[BugleRecall] phase2 failed ViewID={item}: {ex.Message}"); } } } int num3 = 5__3 + num; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[BugleRecall] done, destroyed={num3}/{5__2.Length} (phase1={5__3}, phase2={num}, stillForeign={num2})"); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static void TryRecall() { if (!PhotonNetwork.IsConnected) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[BugleRecall] skipped: not connected"); } } else if (!PhotonNetwork.IsMasterClient) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[BugleRecall] skipped: not master (only host can recall bugles)"); } } else if ((Object)(object)Plugin.Instance == (Object)null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[BugleRecall] Plugin.Instance null, cannot start coroutine"); } } else { ((MonoBehaviour)Plugin.Instance).StartCoroutine(RecallRoutine()); } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator RecallRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0); } } internal static class ConfigStepHelper { public static ConfigEntry WithStep(this ConfigEntry entry, float step) { entry.SettingChanged += delegate { float value2 = entry.Value; float num2 = Snap(value2, step, entry); if (Mathf.Abs(value2 - num2) > step * 0.001f) { entry.Value = num2; } }; float value = entry.Value; float num = Snap(value, step, entry); if (Mathf.Abs(value - num) > step * 0.001f) { entry.Value = num; } return entry; } private static float Snap(float value, float step, ConfigEntry entry) { float num = Mathf.Round(value / step) * step; ConfigDescription description = ((ConfigEntryBase)entry).Description; if (((description != null) ? description.AcceptableValues : null) is AcceptableValueRange val) { num = Mathf.Clamp(num, val.MinValue, val.MaxValue); } return num; } } internal static class DayNightTracker { public static int CurrentDay; public static bool IsDaytime; public static float TimeOfDay; private static bool _prevDaytime = true; public static bool IsBprDark; private static float _lastBprCheckTime = -999f; private const float BprCheckInterval = 0.25f; public static void Tick() { DayNightManager instance = DayNightManager.instance; if ((Object)(object)instance == (Object)null) { CurrentDay = 0; IsDaytime = true; TimeOfDay = 0f; IsBprDark = false; return; } CurrentDay = instance.dayCount; IsDaytime = instance.isDay > 0.5f; TimeOfDay = instance.timeOfDay; if (_prevDaytime != IsDaytime) { _prevDaytime = IsDaytime; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[DEBUG] [DayNight] Transition → {0} (day={1}, time={2:F1})", IsDaytime ? "DAY" : "NIGHT", CurrentDay, TimeOfDay)); } } if (ModIntegration.IsBprLoaded && Time.time - _lastBprCheckTime >= 0.25f) { _lastBprCheckTime = Time.time; IsBprDark = ModIntegration.IsBprDark(); } } public static string FormatForHud(bool zh) { if (CurrentDay <= 0) { return ""; } string periodName = GetPeriodName(TimeOfDay, zh); int num = Mathf.FloorToInt(TimeOfDay) % 24; int num2 = Mathf.FloorToInt((TimeOfDay - Mathf.Floor(TimeOfDay)) * 60f); string text = $"{num:D2}:{num2:D2}"; string text2 = (IsDaytime ? "#FFFF88" : "#8888FF"); string text3 = ""; if (IsBprDark) { text3 = (zh ? " (暗)" : " (Dark)"); } return $"D{CurrentDay} {periodName} {text}{text3}"; } private static string GetPeriodName(float time, bool zh) { if (time >= 5f && time < 11.5f) { if (!zh) { return "Morn"; } return "早晨"; } if (time >= 11.5f && time < 17.5f) { if (!zh) { return "Aftn"; } return "下午"; } if (time >= 17.5f && time < 21f) { if (!zh) { return "Eve"; } return "傍晚"; } if (!zh) { return "Night"; } return "深夜"; } } internal static class ExtraLanternPurger { private struct Candidate { public Item WorldItem; public float Fuel; public bool IsTempSlot; public string Location; public byte? PlayerSlotId; } private const float ScanInterval = 1f; private const float GraceBuffer = 0.3f; private const float HeartbeatLogInterval = 30f; private static float _lastScanTime = -999f; private static float _firstOverCountTime = -1f; private static float _lastHeartbeatLogTime = -999f; private static readonly HashSet _warnedMissingGuids = new HashSet(); private static readonly HashSet _warnedNoPermissionViewIds = new HashSet(); public static void Tick() { if (Plugin.PurgeExtraLanterns == null || !Plugin.PurgeExtraLanterns.Value || Time.time - _lastScanTime < 1f) { return; } _lastScanTime = Time.time; Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.player == (Object)null) { if (Time.time - _lastHeartbeatLogTime > 30f) { _lastHeartbeatLogTime = Time.time; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [ExtraLanternPurger] skip: localCharacter/player null"); } } return; } if ((Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead) { if (Time.time - _lastHeartbeatLogTime > 30f) { _lastHeartbeatLogTime = Time.time; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[DEBUG] [ExtraLanternPurger] skip: data null or dead"); } } return; } List list = CollectPlayerLanterns(localCharacter); int num = CountSceneLanterns(); if (list.Count <= 1) { if (_firstOverCountTime >= 0f) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] count back to {list.Count}, grace cleared"); } } _firstOverCountTime = -1f; if (Time.time - _lastHeartbeatLogTime > 30f) { _lastHeartbeatLogTime = Time.time; string text = localCharacter.characterName ?? "unknown"; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] heartbeat: {list.Count} lantern(s) on player '{text}', scene total={num}, isMaster={PhotonNetwork.IsMasterClient}"); } } } else if (_firstOverCountTime < 0f) { _firstOverCountTime = Time.time; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] detected {list.Count} lanterns (scene total={num}), grace {0.3f:F1}s"); } for (int i = 0; i < list.Count; i++) { Candidate candidate = list[i]; bool flag = (Object)(object)candidate.WorldItem != (Object)null && (Object)(object)((MonoBehaviourPun)candidate.WorldItem).photonView != (Object)null && ((MonoBehaviourPun)candidate.WorldItem).photonView.IsMine; int num2 = (((Object)(object)candidate.WorldItem != (Object)null && (Object)(object)((MonoBehaviourPun)candidate.WorldItem).photonView != (Object)null) ? ((MonoBehaviourPun)candidate.WorldItem).photonView.ViewID : (-1)); ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] #{i}: loc={candidate.Location}, fuel={candidate.Fuel:F1}, temp={candidate.IsTempSlot}, mine={flag}, viewID={num2}"); } } } else { if (Time.time - _firstOverCountTime < 0.3f) { return; } list.Sort((Candidate a, Candidate b) => (a.IsTempSlot != b.IsTempSlot) ? b.IsTempSlot.CompareTo(a.IsTempSlot) : b.Fuel.CompareTo(a.Fuel)); int num3 = list.Count - 1; int num4 = 0; Player player = localCharacter.player; for (int j = 0; j < num3; j++) { Candidate c = list[j]; if ((Object)(object)c.WorldItem == (Object)null) { continue; } int num5 = (((Object)(object)((MonoBehaviourPun)c.WorldItem).photonView != (Object)null) ? ((MonoBehaviourPun)c.WorldItem).photonView.ViewID : (-1)); bool flag2 = (Object)(object)((MonoBehaviourPun)c.WorldItem).photonView != (Object)null && ((MonoBehaviourPun)c.WorldItem).photonView.IsMine; bool isMasterClient = PhotonNetwork.IsMasterClient; if (!TryDestroy(c, player)) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)$"[ExtraLanternPurger] cannot destroy lantern (loc={c.Location}, fuel={c.Fuel:F1}, viewID={num5}, mine={flag2}, master={isMasterClient})"); } continue; } string text2 = ((!c.PlayerSlotId.HasValue) ? "Backpack" : ((c.PlayerSlotId.Value == 250) ? "TempFull" : $"Hand#{c.PlayerSlotId.Value}")); ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[ExtraLanternPurger] destroyed extra lantern: loc={c.Location}, fuel={c.Fuel:F1}, temp={c.IsTempSlot}, viewID={num5}, mine={flag2}, master={isMasterClient}, path={text2}"); } num4++; } _firstOverCountTime = -1f; if (num4 > 0) { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)$"[ExtraLanternPurger] purged {num4} extra lantern(s)"); } } } } private static int CountSceneLanterns() { int num = 0; Lantern[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Lantern val in array) { if (!((Object)(object)val == (Object)null) && !LanternHelper.IsSpecialLantern(val)) { num++; } } return num; } private static List CollectPlayerLanterns(Character character) { List list = new List(); Player player = character.player; if (player.itemSlots != null) { for (int i = 0; i < player.itemSlots.Length; i++) { TryAdd(player.itemSlots[i], isTemp: false, $"Hand#{i}", (byte)i, list); } } TryAdd(player.tempFullSlot, isTemp: true, "TempFull", 250, list); BackpackData val = default(BackpackData); if (player.backpackSlot != null && player.backpackSlot.hasBackpack && ((ItemSlot)player.backpackSlot).data != null && ((ItemSlot)player.backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val) && val != null && val.itemSlots != null) { for (int j = 0; j < val.itemSlots.Length; j++) { TryAdd(val.itemSlots[j], isTemp: false, $"Backpack#{j}", null, list); } } return list; } private static void TryAdd(ItemSlot slot, bool isTemp, string location, byte? playerSlotId, List list) { if (slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null || slot.data == null || ((Object)slot.prefab).name != "Lantern" || ((Object)slot.prefab).name.Contains("Faerie")) { return; } Item val = LanternHelper.FindWorldItemByGuid(slot.data.guid); if ((Object)(object)val == (Object)null) { if (_warnedMissingGuids.Add(slot.data.guid)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] skip {location}: worldItem not found for guid={slot.data.guid} (silenced permanently for this guid)"); } } return; } Lantern component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null) && !LanternHelper.IsSpecialLantern(component)) { float fuel = 0f; FloatItemData val2 = default(FloatItemData); if (val.data.TryGetDataEntry((DataEntryKey)10, ref val2)) { fuel = val2.Value; } list.Add(new Candidate { WorldItem = val, Fuel = fuel, IsTempSlot = isTemp, Location = location, PlayerSlotId = playerSlotId }); } } private static bool TryDestroy(Candidate c, Player player) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) Item worldItem = c.WorldItem; if ((Object)(object)worldItem == (Object)null || (Object)(object)((MonoBehaviourPun)worldItem).photonView == (Object)null) { return false; } if (!((MonoBehaviourPun)worldItem).photonView.IsMine && !PhotonNetwork.IsMasterClient) { int viewID = ((MonoBehaviourPun)worldItem).photonView.ViewID; if (_warnedNoPermissionViewIds.Add(viewID)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] skip destroy viewID={viewID} loc={c.Location}: not mine and not master (silenced for this viewID)"); } } return false; } try { bool num = !worldItem.backpackReference.IsNone; worldItem.ClearDataFromBackpack(); if (num) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] ClearDataFromBackpack invoked (loc={c.Location}, viewID={((MonoBehaviourPun)worldItem).photonView.ViewID})"); } } if (c.PlayerSlotId.HasValue && (Object)(object)player != (Object)null) { try { player.EmptySlot(Optionable.Some(c.PlayerSlotId.Value)); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] [ExtraLanternPurger] Player.EmptySlot({c.PlayerSlotId.Value}) invoked (loc={c.Location}, SyncInventoryRPC broadcast)"); } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)$"[ExtraLanternPurger] EmptySlot({c.PlayerSlotId.Value}) failed: {ex.Message}"); } } } PhotonNetwork.Destroy(((Component)worldItem).gameObject); return true; } catch (Exception ex2) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[ExtraLanternPurger] Destroy failed: " + ex2.Message)); } return false; } } } internal static class FlashlightDrainMonitor { private const string DrainSourceKey = "flashlight"; private static float _lastTickTime = -999f; private const float TickInterval = 0.2f; private static bool _wasFlashlight; public static void Tick() { if (!ModIntegration.IsBprLoaded || Time.time - _lastTickTime < 0.2f) { return; } _lastTickTime = Time.time; bool flag = false; Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null) { CharacterData data = localCharacter.data; Item val = (((Object)(object)data != (Object)null) ? data.currentItem : null); if ((Object)(object)val != (Object)null && val.itemID == 42) { flag = ModIntegration.IsFlashlightActive(val); } else { ModIntegration.ClearSyncHelperCache(); } } float value = Plugin.FlashlightDrainMultiplier.Value; if (flag && value > 1f) { LanternHelper.SetDrainSource("flashlight", value); } else { LanternHelper.RemoveDrainSource("flashlight"); } if (flag == _wasFlashlight) { return; } _wasFlashlight = flag; float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; if (flag) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [FlashlightDrain] Flashlight ON → drain source set to {value:F2}x (final={fuelDrainMultiplier:F2}x)"); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [FlashlightDrain] Flashlight OFF → drain source removed (final={fuelDrainMultiplier:F2}x)"); } } } } internal static class ItemSpawnHelper { private static float _lastSpawnTime = -999f; private const float SpawnCooldown = 2f; public static void TrySpawnMissingItems() { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead || !PhotonNetwork.IsConnected || Time.time - _lastSpawnTime < 2f) { return; } bool flag = false; if (!HasNonFaerieLantern(localCharacter)) { if (SpawnEmptyLantern()) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[F8] Spawned empty lantern"); } flag = true; } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[DEBUG] [ItemSpawn] lantern already present on this player"); } } if (!HasItemAnywhere(localCharacter, "Backpack") && SpawnAndPickup("Backpack")) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[F8] Spawned Backpack for player"); } flag = true; } if (PhotonNetwork.IsMasterClient) { if (!AnyBugleInWorld()) { if (SpawnAndPickup("Bugle")) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[F8] Spawned Bugle (master, world-wide none)"); } flag = true; } } else { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"[DEBUG] [ItemSpawn] Bugle already exists somewhere, skip"); } } } else { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)"[DEBUG] [ItemSpawn] not master client, bugle spawn skipped"); } } if (flag) { _lastSpawnTime = Time.time; } } public static bool HasNonFaerieLantern(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character.player == (Object)null) { return false; } Player player = character.player; if (player.itemSlots != null) { ItemSlot[] itemSlots = player.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (IsNonFaerieLantern(itemSlots[i])) { return true; } } } if (IsNonFaerieLantern(player.tempFullSlot)) { return true; } BackpackData val = default(BackpackData); if (player.backpackSlot != null && player.backpackSlot.hasBackpack && ((ItemSlot)player.backpackSlot).data != null && ((ItemSlot)player.backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val) && val != null && val.itemSlots != null) { ItemSlot[] itemSlots = val.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (IsNonFaerieLantern(itemSlots[i])) { return true; } } } return false; } public static bool HasItemAnywhere(Character character, string itemName) { if ((Object)(object)character == (Object)null || (Object)(object)character.player == (Object)null) { return false; } Player player = character.player; if (itemName == "Backpack") { if (player.backpackSlot != null) { return player.backpackSlot.hasBackpack; } return false; } if (player.itemSlots != null) { ItemSlot[] itemSlots = player.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (IsSlotItem(itemSlots[i], itemName)) { return true; } } } if (IsSlotItem(player.tempFullSlot, itemName)) { return true; } BackpackData val = default(BackpackData); if (player.backpackSlot != null && player.backpackSlot.hasBackpack && ((ItemSlot)player.backpackSlot).data != null && ((ItemSlot)player.backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val) && val != null && val.itemSlots != null) { ItemSlot[] itemSlots = val.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (IsSlotItem(itemSlots[i], itemName)) { return true; } } } return false; } public static bool AnyBugleInWorld() { if (Character.AllCharacters != null) { foreach (Character allCharacter in Character.AllCharacters) { if (!((Object)(object)allCharacter == (Object)null) && HasItemAnywhere(allCharacter, "Bugle")) { return true; } } } BugleSFX[] array = Object.FindObjectsByType((FindObjectsSortMode)0); if (array != null && array.Length != 0) { return true; } return false; } private static bool SpawnEmptyLantern() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return false; } Item val = default(Item); if (!ItemDatabase.TryGetItem("Lantern", ref val)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[DEBUG] [ItemSpawn] 'Lantern' not found in ItemDatabase"); } return false; } Vector3 val2 = localCharacter.Center + Vector3.up * 0.3f; GameObject val3 = PhotonNetwork.Instantiate("0_Items/" + ((Object)val).name, val2, Quaternion.identity, (byte)0, (object[])null); if ((Object)(object)val3 == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"[ItemSpawn] PhotonNetwork.Instantiate returned null for Lantern (inRoom={PhotonNetwork.InRoom}, isMaster={PhotonNetwork.IsMasterClient})"); } return false; } Item component = val3.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } int num = (((Object)(object)((MonoBehaviourPun)component).photonView != (Object)null) ? ((MonoBehaviourPun)component).photonView.ViewID : (-1)); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[ItemSpawn] Spawned Lantern: viewID={num}, pos={val2}, isMaster={PhotonNetwork.IsMasterClient}"); } if (component.data != null) { FloatItemData val4 = default(FloatItemData); if (component.data.TryGetDataEntry((DataEntryKey)10, ref val4)) { val4.Value = 0f; } else { FloatItemData val5 = component.data.RegisterNewEntry((DataEntryKey)10); if (val5 != null) { val5.Value = 0f; } } if ((Object)(object)((MonoBehaviourPun)component).photonView != (Object)null) { ((MonoBehaviourPun)component).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { component.data }); } } Lantern component2 = ((Component)component).GetComponent(); if ((Object)(object)component2 != (Object)null) { ReflectionCache.SetFuel(component2, 0f); if (ReflectionCache.GetLit(component2) && (Object)(object)((MonoBehaviourPun)component2).photonView != (Object)null && ((MonoBehaviourPun)component2).photonView.IsMine) { ((MonoBehaviourPun)component2).photonView.RPC("LightLanternRPC", (RpcTarget)0, new object[1] { false }); } } PhotonView component3 = ((Component)localCharacter).GetComponent(); if ((Object)(object)component3 != (Object)null) { component.RequestPickup(component3); } return true; } private static bool SpawnAndPickup(string itemName) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return false; } Item val = default(Item); if (!ItemDatabase.TryGetItem(itemName, ref val)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DEBUG] [ItemSpawn] '" + itemName + "' not found in ItemDatabase")); } return false; } Vector3 val2 = localCharacter.Center + Vector3.up * 0.3f; GameObject val3 = PhotonNetwork.Instantiate("0_Items/" + ((Object)val).name, val2, Quaternion.identity, (byte)0, (object[])null); if ((Object)(object)val3 == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"[ItemSpawn] PhotonNetwork.Instantiate returned null for '{itemName}' (inRoom={PhotonNetwork.InRoom}, isMaster={PhotonNetwork.IsMasterClient})"); } return false; } Item component = val3.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } int num = (((Object)(object)((MonoBehaviourPun)component).photonView != (Object)null) ? ((MonoBehaviourPun)component).photonView.ViewID : (-1)); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[ItemSpawn] Spawned '{itemName}': viewID={num}, pos={val2}, isMaster={PhotonNetwork.IsMasterClient}"); } PhotonView component2 = ((Component)localCharacter).GetComponent(); if ((Object)(object)component2 != (Object)null) { component.RequestPickup(component2); } return true; } private static bool IsSlotItem(ItemSlot slot, string itemName) { if (slot != null && !slot.IsEmpty() && (Object)(object)slot.prefab != (Object)null) { return ((Object)slot.prefab).name == itemName; } return false; } private static bool IsNonFaerieLantern(ItemSlot slot) { if (slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null) { return false; } if (((Object)slot.prefab).name != "Lantern") { return false; } if (((Object)slot.prefab).name.Contains("Faerie")) { return false; } if ((Object)(object)((Component)slot.prefab).gameObject != (Object)null && ((Object)((Component)slot.prefab).gameObject).name.Contains("Faerie")) { return false; } return true; } } internal static class LanguageHelper { private static bool _isChinese; public static bool IsChinese { get { return _isChinese; } set { _isChinese = value; } } public static string L(string en, string zh) { if (!_isChinese) { return en; } return zh; } public static bool DetectChineseLanguage() { //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Invalid comparison between Unknown and I4 //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Invalid comparison between Unknown and I4 //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Invalid comparison between Unknown and I4 ManualLogSource log = Plugin.Log; try { bool flag = PlayerPrefs.HasKey("LanguageSetting"); if (log != null) { log.LogInfo((object)$"[DEBUG] Lang: PlayerPrefs.HasKey('LanguageSetting')={flag}"); } if (flag) { int @int = PlayerPrefs.GetInt("LanguageSetting", -1); if (log != null) { log.LogInfo((object)$"[DEBUG] Lang: PlayerPrefs.GetInt={@int}"); } if (@int >= 0) { bool flag2 = @int == 9; if (log != null) { log.LogInfo((object)string.Format("[DEBUG] Lang: → {0} (PlayerPrefs int={1})", flag2 ? "Chinese" : "non-Chinese", @int)); } return flag2; } string @string = PlayerPrefs.GetString("LanguageSetting", string.Empty); if (log != null) { log.LogInfo((object)("[DEBUG] Lang: PlayerPrefs.GetString='" + @string + "'")); } if (!string.IsNullOrEmpty(@string)) { bool flag3 = IsChineseLanguageName(@string); if (log != null) { log.LogInfo((object)("[DEBUG] Lang: → " + (flag3 ? "Chinese" : "non-Chinese") + " (PlayerPrefs string='" + @string + "')")); } return flag3; } } } catch (Exception ex) { if (log != null) { log.LogError((object)("[DEBUG] Lang: Exception (M1): " + ex.Message)); } } try { if (ReflectionCache.LocalizedTextLanguageField != null) { object value = ReflectionCache.LocalizedTextLanguageField.GetValue(null); string text = value?.ToString() ?? string.Empty; if (log != null) { log.LogInfo((object)("[DEBUG] Lang: CURRENT_LANGUAGE='" + text + "' type=" + value?.GetType().Name)); } if (!string.IsNullOrEmpty(text)) { bool flag4 = IsChineseLanguageName(text); if (log != null) { log.LogInfo((object)("[DEBUG] Lang: → " + (flag4 ? "Chinese" : "non-Chinese") + " (LocalizedText='" + text + "')")); } return flag4; } } } catch (Exception ex2) { if (log != null) { log.LogError((object)("[DEBUG] Lang: Exception (M2): " + ex2.Message)); } } try { SystemLanguage systemLanguage = Application.systemLanguage; if (log != null) { log.LogInfo((object)$"[DEBUG] Lang: Application.systemLanguage={systemLanguage} (fallback)"); } if ((int)systemLanguage == 6 || (int)systemLanguage == 40 || (int)systemLanguage == 41) { if (log != null) { log.LogInfo((object)"[DEBUG] Lang: → Chinese (systemLanguage fallback)"); } return true; } } catch (Exception ex3) { if (log != null) { log.LogError((object)("[DEBUG] Lang: Exception (M3): " + ex3.Message)); } } if (log != null) { log.LogInfo((object)"[DEBUG] Lang: no method detected Chinese, defaulting to English"); } return false; } private static bool IsChineseLanguageName(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } if (name.IndexOf("SimplifiedChinese", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("TraditionalChinese", StringComparison.OrdinalIgnoreCase) < 0 && !name.StartsWith("zh", StringComparison.OrdinalIgnoreCase) && name.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } } internal class LanternFuelIndicator3D : MonoBehaviour { private static bool _globalEnabled; private const float OffsetY = 0.35f; private const float CanvasScale = 0.005f; private const float UpdateInterval = 0.25f; private const float PctThreshold = 0.5f; private static readonly Color ColorHigh = new Color(0.2f, 1f, 0.2f); private static readonly Color ColorMid = new Color(1f, 0.9f, 0.1f); private static readonly Color ColorLow = new Color(1f, 0.25f, 0.15f); private Lantern _lantern; private GameObject _canvasObj; private TextMeshProUGUI _text; private float _lastUpdateTime; private float _lastFuelPct = -1f; private static TMP_FontAsset _cachedFont; public static bool GlobalEnabled { get { return _globalEnabled; } set { _globalEnabled = value; } } private void Start() { _lantern = ((Component)this).GetComponent(); if ((Object)(object)_lantern == (Object)null) { Object.Destroy((Object)(object)this); return; } CreateUI(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [FuelIndicator3D] Attached to lantern (id={((Object)_lantern).GetInstanceID()})"); } } private void LateUpdate() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (!_globalEnabled || (Object)(object)_lantern == (Object)null) { SetVisible(visible: false); return; } if (!ReflectionCache.GetLit(_lantern)) { SetVisible(visible: false); return; } SetVisible(visible: true); Camera main = Camera.main; if ((Object)(object)main != (Object)null && (Object)(object)_canvasObj != (Object)null) { _canvasObj.transform.forward = ((Component)main).transform.forward; } if (!(Time.time - _lastUpdateTime < 0.25f)) { _lastUpdateTime = Time.time; float fuel = ReflectionCache.GetFuel(_lantern); float startingFuel = _lantern.startingFuel; float num = ((startingFuel > 0f) ? (fuel / startingFuel * 100f) : 0f); if (!(Mathf.Abs(num - _lastFuelPct) < 0.5f)) { _lastFuelPct = num; Color color = ((num > 50f) ? ColorHigh : ((!(num > 25f)) ? ColorLow : ColorMid)); ((Graphic)_text).color = color; ((TMP_Text)_text).text = $"{num:F0}%"; } } } private void OnDestroy() { if ((Object)(object)_canvasObj != (Object)null) { Object.Destroy((Object)(object)_canvasObj); } } private void CreateUI() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0041: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) _canvasObj = new GameObject("LSN_FuelIndicator"); _canvasObj.transform.SetParent(((Component)this).transform, false); _canvasObj.transform.localPosition = new Vector3(0f, 0.35f, 0f); Canvas obj = _canvasObj.AddComponent(); obj.renderMode = (RenderMode)2; obj.sortingOrder = 100; RectTransform component = _canvasObj.GetComponent(); component.sizeDelta = new Vector2(100f, 30f); ((Transform)component).localScale = new Vector3(0.005f, 0.005f, 0.005f); GameObject val = new GameObject("Text"); val.transform.SetParent(_canvasObj.transform, false); _text = val.AddComponent(); ((TMP_Text)_text).fontSize = 22f; ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ((TMP_Text)_text).fontStyle = (FontStyles)1; ((TMP_Text)_text).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)_text).overflowMode = (TextOverflowModes)0; ((Graphic)_text).color = ColorHigh; ((TMP_Text)_text).text = ""; if ((Object)(object)_cachedFont == (Object)null) { _cachedFont = FindGameFont(); } if ((Object)(object)_cachedFont != (Object)null) { ((TMP_Text)_text).font = _cachedFont; } RectTransform component2 = val.GetComponent(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; _canvasObj.SetActive(false); } private void SetVisible(bool visible) { if ((Object)(object)_canvasObj != (Object)null && _canvasObj.activeSelf != visible) { _canvasObj.SetActive(visible); } } private static TMP_FontAsset FindGameFont() { try { TextMeshProUGUI[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TextMeshProUGUI val in array) { if ((Object)(object)val != (Object)null && (Object)(object)((TMP_Text)val).font != (Object)null) { return ((TMP_Text)val).font; } } } catch { } return null; } } internal static class LanternHelper { private struct SlotCacheEntry { public ItemSlot Slot; public ItemInstanceData LiveData; public float Expiry; } private static float _lastFindLogTime = -999f; private const float FindLogInterval = 30f; private static Guid _worldItemCachedGuid; private static Item _worldItemCachedResult; private static float _worldItemCacheTime; private const float WorldItemCacheDuration = 2f; private static int _findSlotCachedFrame = -1; private static ItemSlot _findSlotCachedResult; private static ItemInstanceData _findSlotCachedLiveData; private static readonly Dictionary _slotCacheById = new Dictionary(); private const float CacheGracePeriod = 0.8f; private static bool _cacheHitLogged; private static float _reserveWarmth; private static readonly Dictionary _fuelSyncTime = new Dictionary(); private static readonly Dictionary _fuelSyncValue = new Dictionary(); private const float FuelSyncInterval = 2f; private const float FuelSyncThreshold = 0.05f; private static readonly Dictionary _drainSources = new Dictionary(); public static float FuelDrainMultiplier { get { if (_drainSources.Count == 0) { return 1f; } float num = 1f; foreach (float value in _drainSources.Values) { num *= value; } return num; } } public static float ReserveWarmth => _reserveWarmth; public static void SetDrainSource(string key, float multiplier) { if (Mathf.Approximately(multiplier, 1f)) { if (_drainSources.Remove(key)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [DrainSource] REMOVED '{key}' (≈1.0) → final={FuelDrainMultiplier:F2}x"); } } return; } float value; bool flag = _drainSources.TryGetValue(key, out value); _drainSources[key] = multiplier; if (!flag || !Mathf.Approximately(value, multiplier)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)string.Format("[DEBUG] [DrainSource] SET '{0}'={1}{2:F2}x → final={3:F2}x", key, flag ? $"{value:F2}→" : "", multiplier, FuelDrainMultiplier)); } } } public static void RemoveDrainSource(string key) { if (_drainSources.Remove(key)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [DrainSource] REMOVED '{key}' → final={FuelDrainMultiplier:F2}x"); } } } internal static bool IsSpecialLantern(Lantern instance) { if ((Object)(object)instance == (Object)null) { return false; } GameObject gameObject = ((Component)instance).gameObject; return ((gameObject != null) ? ((Object)gameObject).name : null)?.Contains("Faerie") ?? false; } public static float GetReserveMax(float lanternMax) { int value = (int)Plugin.ReserveWarmthMax.Value; return lanternMax * (float)value / 100f; } public static void AddOverflowToReserve(float overflow, float lanternMax) { if (overflow <= 0f) { return; } float reserveMax = GetReserveMax(lanternMax); if (!(reserveMax <= 0f)) { float reserveWarmth = _reserveWarmth; _reserveWarmth = Mathf.Min(_reserveWarmth + overflow, reserveMax); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [Reserve] +{overflow:F1} → {_reserveWarmth:F1}/{reserveMax:F1} (was {reserveWarmth:F1})"); } } } public static float ConsumeReserve(float amount) { if (_reserveWarmth <= 0f || amount <= 0f) { return 0f; } float reserveWarmth = _reserveWarmth; float num = Mathf.Min(amount, _reserveWarmth); _reserveWarmth -= num; if (num >= 0.1f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [Reserve] -{num:F1} → {_reserveWarmth:F1} (was {reserveWarmth:F1})"); } } return num; } public static bool AddPlayerLanternFuel(Character character, float fuelDelta) { float overflow; float maxFuelOut; return AddPlayerLanternFuel(character, fuelDelta, out overflow, out maxFuelOut); } public static bool AddPlayerLanternFuel(Character character, float fuelDelta, out float overflow, out float maxFuelOut) { overflow = 0f; maxFuelOut = 0f; if ((Object)(object)character == (Object)null || !character.IsLocal) { return false; } if (Mathf.Approximately(fuelDelta, 0f)) { return false; } ItemInstanceData liveData; ItemSlot val = FindLitLanternSlot(character, out liveData); if (val == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] AddPlayerLanternFuel: no lit lantern slot found"); } return false; } ItemInstanceData val2 = liveData ?? val.data; if (val2 == null) { return false; } Guid guid = val2.guid; if (guid == Guid.Empty) { return false; } Item val3 = FindWorldItemByGuid(guid); if ((Object)(object)val3 == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"[DEBUG] AddPlayerLanternFuel: world item not found for guid={guid}"); } return false; } Lantern component = ((Component)val3).GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[DEBUG] AddPlayerLanternFuel: world item has no Lantern component"); } return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry((DataEntryKey)10, ref val4)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"[DEBUG] AddPlayerLanternFuel: no Fuel data entry"); } return false; } float value = val4.Value; float num = (maxFuelOut = component.startingFuel); float num2 = value + fuelDelta; float num3 = Mathf.Clamp(num2, 0f, num); overflow = ((num2 > num) ? (num2 - num) : 0f); val4.Value = num3; if (num2 > num - 1f) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[DEBUG] [FuelMath] currentFuel={value:F3}, delta={fuelDelta:F3}, rawNew={num2:F3}, maxFuel={num:F3}, overflow={overflow:F3}, lanternInstID={((Object)component).GetInstanceID()}"); } } if ((Object)(object)((MonoBehaviourPun)val3).photonView != (Object)null) { int instanceID = ((Object)val3).GetInstanceID(); float value2 = 0f; if (!_fuelSyncTime.TryGetValue(instanceID, out var value3) || !_fuelSyncValue.TryGetValue(instanceID, out value2) || Mathf.Abs(num3 - value2) / num >= 0.05f || Time.time - value3 >= 2f) { ((MonoBehaviourPun)val3).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { val3.data }); _fuelSyncTime[instanceID] = Time.time; _fuelSyncValue[instanceID] = num3; } } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[DEBUG] AddPlayerLanternFuel: {value:F1} +{fuelDelta:F1} = {num3:F1} (max={num:F1})"); } return true; } public static bool RefillPlayerLanternFuel(Character character) { if ((Object)(object)character == (Object)null || !character.IsLocal) { return false; } ItemInstanceData liveData; ItemSlot val = FindLitLanternSlot(character, out liveData); if (val == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] RefillPlayerLanternFuel: no lit lantern slot found"); } return false; } ItemInstanceData val2 = liveData ?? val.data; if (val2 == null) { return false; } Guid guid = val2.guid; if (guid == Guid.Empty) { return false; } Item val3 = FindWorldItemByGuid(guid); if ((Object)(object)val3 == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[DEBUG] RefillPlayerLanternFuel: world item not found"); } return false; } Lantern component = ((Component)val3).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry((DataEntryKey)10, ref val4)) { return false; } float value = val4.Value; float num = (val4.Value = component.startingFuel); if ((Object)(object)((MonoBehaviourPun)val3).photonView != (Object)null) { ((MonoBehaviourPun)val3).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { val3.data }); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] RefillPlayerLanternFuel: {value:F1} → {num:F1} (FULL)"); } return true; } public static bool AddPlayerLanternFuelAnyState(Character character, float fuelDelta, out float overflow, out float maxFuelOut) { if (AddPlayerLanternFuel(character, fuelDelta, out overflow, out maxFuelOut)) { return true; } overflow = 0f; maxFuelOut = 0f; if ((Object)(object)character == (Object)null || !character.IsLocal) { return false; } if (Mathf.Approximately(fuelDelta, 0f)) { return false; } ItemInstanceData liveData; ItemSlot val = FindAnyLanternSlot(character, out liveData); if (val == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] AddPlayerLanternFuelAnyState: no lantern found (lit or unlit)"); } return false; } ItemInstanceData val2 = liveData ?? val.data; if (val2 == null) { return false; } Guid guid = val2.guid; if (guid == Guid.Empty) { return false; } Item val3 = FindWorldItemByGuid(guid); if ((Object)(object)val3 == (Object)null) { return false; } Lantern component = ((Component)val3).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry((DataEntryKey)10, ref val4)) { return false; } float value = val4.Value; float num = (maxFuelOut = component.startingFuel); float num2 = value + fuelDelta; float num3 = Mathf.Clamp(num2, 0f, num); overflow = ((num2 > num) ? (num2 - num) : 0f); val4.Value = num3; if ((Object)(object)((MonoBehaviourPun)val3).photonView != (Object)null) { ((MonoBehaviourPun)val3).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { val3.data }); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] AddPlayerLanternFuelAnyState (unlit): {value:F1} +{fuelDelta:F1} = {num3:F1} (max={num:F1}, overflow={overflow:F1})"); } return true; } public static bool RefillPlayerLanternFuelAnyState(Character character) { if (RefillPlayerLanternFuel(character)) { return true; } if ((Object)(object)character == (Object)null || !character.IsLocal) { return false; } ItemInstanceData liveData; ItemSlot val = FindAnyLanternSlot(character, out liveData); if (val == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] RefillPlayerLanternFuelAnyState: no lantern found (lit or unlit)"); } return false; } ItemInstanceData val2 = liveData ?? val.data; if (val2 == null) { return false; } Guid guid = val2.guid; if (guid == Guid.Empty) { return false; } Item val3 = FindWorldItemByGuid(guid); if ((Object)(object)val3 == (Object)null) { return false; } Lantern component = ((Component)val3).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry((DataEntryKey)10, ref val4)) { return false; } float value = val4.Value; float num = (val4.Value = component.startingFuel); if ((Object)(object)((MonoBehaviourPun)val3).photonView != (Object)null) { ((MonoBehaviourPun)val3).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { val3.data }); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] RefillPlayerLanternFuelAnyState (unlit): {value:F1} → {num:F1} (FULL)"); } return true; } private static ItemSlot FindAnyLanternSlot(Character character, out ItemInstanceData liveData) { liveData = null; if ((Object)(object)character == (Object)null || (Object)(object)character.player == (Object)null) { return null; } ItemSlot[] itemSlots = character.player.itemSlots; foreach (ItemSlot val in itemSlots) { if (val != null && !val.IsEmpty() && !((Object)(object)val.prefab == (Object)null) && !(((Object)val.prefab).name != "Lantern")) { Guid guid = ((val.data != null) ? val.data.guid : Guid.Empty); Item val2 = ((guid != Guid.Empty) ? FindWorldItemByGuid(guid) : null); liveData = (((Object)(object)val2 != (Object)null) ? val2.data : val.data); return val; } } ItemSlot tempFullSlot = character.player.tempFullSlot; if (tempFullSlot != null && !tempFullSlot.IsEmpty() && (Object)(object)tempFullSlot.prefab != (Object)null && ((Object)tempFullSlot.prefab).name == "Lantern") { Guid guid2 = ((tempFullSlot.data != null) ? tempFullSlot.data.guid : Guid.Empty); Item val3 = ((guid2 != Guid.Empty) ? FindWorldItemByGuid(guid2) : null); liveData = (((Object)(object)val3 != (Object)null) ? val3.data : tempFullSlot.data); return tempFullSlot; } BackpackSlot backpackSlot = character.player.backpackSlot; BackpackData val4 = default(BackpackData); if (backpackSlot != null && backpackSlot.hasBackpack && ((ItemSlot)backpackSlot).data != null && ((ItemSlot)backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val4) && val4 != null && val4.itemSlots != null) { itemSlots = val4.itemSlots; foreach (ItemSlot val5 in itemSlots) { if (val5 != null && !val5.IsEmpty() && !((Object)(object)val5.prefab == (Object)null) && !(((Object)val5.prefab).name != "Lantern")) { Guid guid3 = ((val5.data != null) ? val5.data.guid : Guid.Empty); Item val6 = ((guid3 != Guid.Empty) ? FindWorldItemByGuid(guid3) : null); liveData = (((Object)(object)val6 != (Object)null) ? val6.data : val5.data); return val5; } } } return null; } public static ItemSlot FindLitLanternSlot(Character character) { ItemInstanceData liveData; return FindLitLanternSlot(character, out liveData); } public static ItemSlot FindLitLanternSlot(Character character, out ItemInstanceData liveData) { liveData = null; if ((Object)(object)character == (Object)null || (Object)(object)character.player == (Object)null) { return null; } int frameCount = Time.frameCount; if (frameCount == _findSlotCachedFrame) { liveData = _findSlotCachedLiveData; return _findSlotCachedResult; } _findSlotCachedFrame = frameCount; ItemSlot[] itemSlots = character.player.itemSlots; foreach (ItemSlot val in itemSlots) { if (!IsLitLanternSlot(val)) { continue; } if (Time.time - _lastFindLogTime >= 30f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[DEBUG] Lit lantern found in HAND slot: " + ((Object)val.prefab).name)); } _lastFindLogTime = Time.time; } liveData = val.data; UpdateCache(val, liveData); return val; } itemSlots = character.player.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { ItemSlot val2 = CheckSlotWorldLit(itemSlots[i], "HAND(worldLit)", out liveData); if (val2 != null) { UpdateCache(val2, liveData); return val2; } } ItemSlot tempFullSlot = character.player.tempFullSlot; if (tempFullSlot != null) { if (IsLitLanternSlot(tempFullSlot)) { if (Time.time - _lastFindLogTime >= 30f) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[DEBUG] Lit lantern found in TEMP slot (slotID=250): " + ((Object)tempFullSlot.prefab).name)); } _lastFindLogTime = Time.time; } liveData = tempFullSlot.data; UpdateCache(tempFullSlot, liveData); return tempFullSlot; } ItemSlot val3 = CheckSlotWorldLit(tempFullSlot, "TEMP(worldLit,slotID=250)", out liveData); if (val3 != null) { UpdateCache(val3, liveData); return val3; } } BackpackSlot backpackSlot = character.player.backpackSlot; BackpackData val4 = default(BackpackData); if (backpackSlot != null && backpackSlot.hasBackpack && ((ItemSlot)backpackSlot).data != null && ((ItemSlot)backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val4) && val4 != null && val4.itemSlots != null) { itemSlots = val4.itemSlots; foreach (ItemSlot val5 in itemSlots) { if (val5 == null || val5.IsEmpty() || (Object)(object)val5.prefab == (Object)null || ((Object)val5.prefab).name != "Lantern") { continue; } Guid guid = ((val5.data != null) ? val5.data.guid : Guid.Empty); Item val6 = ((guid != Guid.Empty) ? FindWorldItemByGuid(guid) : null); if ((Object)(object)val6 != (Object)null) { Lantern component = ((Component)val6).GetComponent(); bool flag = false; if ((Object)(object)component != (Object)null) { flag = ReflectionCache.GetLit(component); } if (Time.time - _lastFindLogTime >= 30f) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { PhotonView component2 = ((Component)val6).GetComponent(); log3.LogInfo((object)$"[DEBUG] Lantern found in BACKPACK, world instance OK (ViewID={((component2 != null) ? new int?(component2.ViewID) : null)}, worldLit={flag})"); } _lastFindLogTime = Time.time; } liveData = val6.data; } else { if (Time.time - _lastFindLogTime >= 30f) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[DEBUG] Lantern found in BACKPACK, no world instance (guid={guid}), using slot data"); } _lastFindLogTime = Time.time; } liveData = val5.data; } UpdateCache(val5, liveData); return val5; } } foreach (KeyValuePair item in _slotCacheById) { SlotCacheEntry value = item.Value; if (Time.time >= value.Expiry || value.Slot == null || value.Slot.IsEmpty()) { continue; } Item prefab = value.Slot.prefab; if (((prefab != null) ? ((Object)prefab).name : null) != "Lantern") { continue; } Guid guid2 = value.Slot.data?.guid ?? Guid.Empty; if (guid2 != Guid.Empty) { Item val7 = FindWorldItemByGuid(guid2); liveData = (((Object)(object)val7 != (Object)null) ? val7.data : value.LiveData); } else { liveData = value.LiveData; } if (!_cacheHitLogged) { _cacheHitLogged = true; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[DEBUG] [FindLantern] Grace-period cache HIT (id={item.Key}, BPR mode switch?)"); } } _findSlotCachedResult = value.Slot; _findSlotCachedLiveData = liveData; return value.Slot; } _cacheHitLogged = false; _findSlotCachedResult = null; _findSlotCachedLiveData = null; return null; } private static void UpdateCache(ItemSlot slot, ItemInstanceData liveData) { int key = ((slot?.data != null) ? slot.data.guid.GetHashCode() : 0); _slotCacheById[key] = new SlotCacheEntry { Slot = slot, LiveData = liveData, Expiry = Time.time + 0.8f }; _cacheHitLogged = false; _findSlotCachedResult = slot; _findSlotCachedLiveData = liveData; } private static ItemSlot CheckSlotWorldLit(ItemSlot slot, string slotLabel, out ItemInstanceData liveData) { liveData = null; if (slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null) { return null; } if (((Object)slot.prefab).name != "Lantern") { return null; } Guid guid = ((slot.data != null) ? slot.data.guid : Guid.Empty); if (guid == Guid.Empty) { return null; } Item val = FindWorldItemByGuid(guid); if ((Object)(object)val == (Object)null) { return null; } Lantern component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { return null; } if (ReflectionCache.GetLit(component)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[DEBUG] Lit lantern found in " + slotLabel + " via world-instance fallback")); } liveData = val.data; return slot; } return null; } internal static Item FindWorldItemByGuid(Guid guid) { if (guid == _worldItemCachedGuid && (Object)(object)_worldItemCachedResult != (Object)null && Time.time - _worldItemCacheTime < 2f) { return _worldItemCachedResult; } Item val = null; foreach (Item aLL_ACTIVE_ITEM in Item.ALL_ACTIVE_ITEMS) { if ((Object)(object)aLL_ACTIVE_ITEM != (Object)null && aLL_ACTIVE_ITEM.data != null && aLL_ACTIVE_ITEM.data.guid == guid) { val = aLL_ACTIVE_ITEM; break; } } if ((Object)(object)val == (Object)null) { Lantern[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { Item component = ((Component)array[i]).GetComponent(); if ((Object)(object)component != (Object)null && component.data != null && component.data.guid == guid) { val = component; break; } } } _worldItemCachedGuid = guid; _worldItemCachedResult = val; _worldItemCacheTime = Time.time; return val; } private static bool IsLitLanternSlot(ItemSlot slot) { if (slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null) { return false; } if (((Object)slot.prefab).name != "Lantern") { return false; } BoolItemData val = default(BoolItemData); if (slot.data != null && slot.data.TryGetDataEntry((DataEntryKey)3, ref val)) { return val.Value; } return false; } } internal static class LanternHud { private static bool _active; private static HudPosition _activePos; private static HudSizePreset _activeSize; private static LanternHudPanel _panel; private static float _tickTimer; private const float TickInterval = 0.25f; public static void Tick() { bool value = Plugin.EnableHud.Value; HudPosition value2 = Plugin.HudPos.Value; HudSizePreset value3 = Plugin.HudSize.Value; if (value != _active) { if (!value) { DestroyAll(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [HUD] Disabled"); } return; } _active = true; _activePos = value2; _activeSize = value3; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[DEBUG] [HUD] Enabled — waiting for GUIManager..."); } } if (!_active) { return; } if (_panel == null || !_panel.IsCreated) { TryAttachToGameHud(); if (_panel == null || !_panel.IsCreated) { return; } } if (value2 != _activePos) { _activePos = value2; _panel.SetPosition(_activePos); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] [HUD] Position → {_activePos}"); } } if (value3 != _activeSize) { _activeSize = value3; DestroyPanel(); TryAttachToGameHud(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[DEBUG] [HUD] Size → {_activeSize}"); } } else { _tickTimer += Time.deltaTime; if (_tickTimer >= 0.25f) { _tickTimer = 0f; _panel.UpdateData(); } } } private static void TryAttachToGameHud() { GUIManager instance = GUIManager.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.hudCanvas == (Object)null)) { _panel = new LanternHudPanel(); _panel.Create(((Component)instance.hudCanvas).transform, _activeSize); _panel.SetPosition(_activePos); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [HUD] Panel attached to hudCanvas at {_activePos}, size={_activeSize}"); } } } private static void DestroyPanel() { if (_panel != null) { _panel.Destroy(); _panel = null; } _tickTimer = 0f; } private static void DestroyAll() { DestroyPanel(); _active = false; } } internal class LanternHudPanel { private GameObject _root; private RectTransform _rootRect; private CanvasGroup _canvasGroup; private Image _fuelBarFill; private RectTransform _fuelBarFillRect; private TextMeshProUGUI _fuelText; private TextMeshProUGUI _multiplierText; private TextMeshProUGUI _statusText; private TextMeshProUGUI _upgradeText; private const float Margin = 6f; private const float EdgeOffset = 10f; private static TMP_FontAsset _cachedFont; private float _cachedFuelPct = -1f; private string _cachedFuelStr = ""; private string _cachedMultStr = ""; private string _cachedStatusStr = ""; private string _cachedUpgradeStr = ""; private bool _cachedVisible = true; public bool IsCreated => (Object)(object)_root != (Object)null; public void Create(Transform parent, HudSizePreset sizePreset = HudSizePreset.Large) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { return; } GetPresetParams(sizePreset, out var panelW, out var panelH, out var barH, out var fontSize); if ((Object)(object)_cachedFont == (Object)null) { PlayerConnectionLog val = Object.FindAnyObjectByType(); if ((Object)(object)val != (Object)null && (Object)(object)val.text != (Object)null) { _cachedFont = ((TMP_Text)val.text).font; } if ((Object)(object)_cachedFont == (Object)null) { GUIManager instance = GUIManager.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.interactNameText != (Object)null) { _cachedFont = ((TMP_Text)instance.interactNameText).font; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[DEBUG] [HUD] Font resolved: " + (((Object)(object)_cachedFont != (Object)null) ? ((Object)_cachedFont).name : "NULL"))); } } _root = new GameObject("LSN_HudPanel"); _root.transform.SetParent(parent, false); _rootRect = _root.AddComponent(); _rootRect.sizeDelta = new Vector2(panelW, panelH); ((Graphic)_root.AddComponent()).color = new Color(0f, 0f, 0f, 0.6f); _canvasGroup = _root.AddComponent(); _canvasGroup.alpha = 1f; _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; float num = panelH / 2f - 6f - barH / 2f; float num2 = num - barH - 4f; float num3 = num2 - fontSize - 4f; float num4 = num3 - fontSize - 4f; GameObject obj = CreateChild(_root.transform, "FuelBarBG"); RectTransform obj2 = obj.AddComponent(); obj2.anchoredPosition = new Vector2(0f, num); obj2.sizeDelta = new Vector2(panelW - 12f, barH); ((Graphic)obj.AddComponent()).color = new Color(0.2f, 0.2f, 0.2f, 0.8f); GameObject val2 = CreateChild(obj.transform, "FuelBarFill"); RectTransform val3 = val2.AddComponent(); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.sizeDelta = Vector2.zero; val3.anchoredPosition = Vector2.zero; _fuelBarFillRect = val3; _fuelBarFill = val2.AddComponent(); ((Graphic)_fuelBarFill).color = Color.green; _fuelText = CreateTMP(_root.transform, "FuelText", new Vector2(0f, num), new Vector2(panelW - 12f, barH + 4f), (TextAlignmentOptions)516, fontSize - 2f, _cachedFont); ((Graphic)_fuelText).color = new Color(1f, 0.88f, 0.1f); _multiplierText = CreateTMP(_root.transform, "MultiplierText", new Vector2(0f, num2), new Vector2(panelW - 12f, fontSize + 4f), (TextAlignmentOptions)514, fontSize, _cachedFont); _statusText = CreateTMP(_root.transform, "StatusText", new Vector2(0f, num3), new Vector2(panelW - 12f, fontSize + 4f), (TextAlignmentOptions)514, fontSize, _cachedFont); _upgradeText = CreateTMP(_root.transform, "UpgradeText", new Vector2(0f, num4), new Vector2(panelW - 12f, fontSize + 4f), (TextAlignmentOptions)514, fontSize, _cachedFont); } public void SetPosition(HudPosition pos) { if (!((Object)(object)_rootRect == (Object)null)) { switch (pos) { case HudPosition.TopLeft: SetAnchor(0f, 1f, 0f, 1f, 10f, -10f); break; case HudPosition.Top: SetAnchor(0.5f, 1f, 0.5f, 1f, 0f, -10f); break; case HudPosition.TopRight: SetAnchor(1f, 1f, 1f, 1f, -10f, -10f); break; case HudPosition.Left: SetAnchor(0f, 0.5f, 0f, 0.5f, 10f, 0f); break; case HudPosition.Right: SetAnchor(1f, 0.5f, 1f, 0.5f, -10f, 0f); break; case HudPosition.BottomLeft: SetAnchor(0f, 0f, 0f, 0f, 10f, 10f); break; case HudPosition.Bottom: SetAnchor(0.5f, 0f, 0.5f, 0f, 0f, 10f); break; case HudPosition.BottomRight: SetAnchor(1f, 0f, 1f, 0f, -10f, 10f); break; } } } public void UpdateData() { //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root == (Object)null) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || ((Object)(object)localCharacter.data != (Object)null && localCharacter.data.dead)) { if (_cachedVisible) { _cachedVisible = false; _canvasGroup.alpha = 0f; } return; } if (!_cachedVisible) { _cachedVisible = true; _canvasGroup.alpha = 1f; } bool isChinese = LanguageHelper.IsChinese; ItemInstanceData liveData; ItemSlot val = LanternHelper.FindLitLanternSlot(localCharacter, out liveData); ItemInstanceData val2 = liveData ?? val?.data; FloatItemData val3 = default(FloatItemData); if (val != null && val2 != null && val2.TryGetDataEntry((DataEntryKey)10, ref val3)) { float value = val3.Value; float maxFuel = GetMaxFuel(val2); float num = ((maxFuel > 0f) ? Mathf.Clamp01(value / maxFuel) : 0.5f); if (!MathExtensions.Approximately(num, _cachedFuelPct) || Mathf.Abs(num - _cachedFuelPct) > 0.005f) { _cachedFuelPct = num; _fuelBarFillRect.anchorMax = new Vector2(num, 1f); ((Graphic)_fuelBarFill).color = FuelColor(num); } int num2 = Mathf.FloorToInt(value / 60f); int num3 = Mathf.FloorToInt(value % 60f); string text = ((num2 <= 0) ? (isChinese ? $"{value:F1}秒" : $"{value:F1}s") : (isChinese ? $"{num2}分{num3}秒" : $"{num2}m{num3}s")); if (text != _cachedFuelStr) { _cachedFuelStr = text; ((TMP_Text)_fuelText).text = text; } } else { if (_cachedFuelPct != 0f) { _cachedFuelPct = 0f; _fuelBarFillRect.anchorMax = new Vector2(0f, 1f); ((Graphic)_fuelBarFill).color = new Color(0.4f, 0.4f, 0.4f, 0.6f); } string text2 = (isChinese ? "无灯" : "No Lamp"); if (text2 != _cachedFuelStr) { _cachedFuelStr = text2; ((TMP_Text)_fuelText).text = text2; } } float value2 = Plugin.LanternWarmthMultiplier.Value; float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; string text3 = string.Format("{0} {1:F2}x", isChinese ? "回暖" : "Warm", value2); string text4 = (Mathf.Approximately(fuelDrainMultiplier, 1f) ? "" : string.Format(" | {0} {1:F1}x", isChinese ? "消耗" : "Drain", fuelDrainMultiplier)); string text5 = ""; string lastSource = RestoreTracker.LastSource; if (!string.IsNullOrEmpty(lastSource)) { float lastWarmth = RestoreTracker.LastWarmth; string restoreSourceName = GetRestoreSourceName(lastSource, isChinese); string text6 = ((lastWarmth > 0f) ? $"+{lastWarmth:F0}s" : (isChinese ? "满" : "MAX")); text5 = " ←" + restoreSourceName + text6 + ""; } string text7 = text3 + text4 + text5; if (text7 != _cachedMultStr) { _cachedMultStr = text7; ((TMP_Text)_multiplierText).text = text7; } float reserveWarmth = LanternHelper.ReserveWarmth; string text8 = ""; if (Plugin.ReserveWarmthMax.Value > ReserveWarmthRatio.Off) { text8 = ((reserveWarmth > 0f) ? string.Format("{0} {1:F1}s", isChinese ? "备用" : "Rsv", reserveWarmth) : ("" + (isChinese ? "备用" : "Rsv") + " 0")); } string text9 = ""; if (Plugin.ShowDayNightOnHud.Value) { text9 = DayNightTracker.FormatForHud(isChinese); } string text10 = ""; if (Plugin.AutoRefillEnabled != null && Plugin.AutoRefillEnabled.Value && Time.time - RestoreTracker.LastAutoRefillTime < 1f) { text10 = (isChinese ? "回血中…" : "Refill…"); } string text11 = ""; if (text8.Length > 0) { text11 = text8; } if (text10.Length > 0) { text11 = ((text11.Length > 0) ? (text11 + " " + text10) : text10); } if (text9.Length > 0) { text11 = ((text11.Length > 0) ? (text11 + " " + text9) : text9); } if (text11 != _cachedStatusStr) { _cachedStatusStr = text11; ((TMP_Text)_statusText).text = text11; } string text12 = ""; if (Plugin.EnableUpgradeSystem != null && Plugin.EnableUpgradeSystem.Value) { int capacityLevel = LanternUpgradeSystem.CapacityLevel; int efficiencyLevel = LanternUpgradeSystem.EfficiencyLevel; int points = LanternUpgradeSystem.Points; int capacityCost = LanternUpgradeSystem.GetCapacityCost(); int efficiencyCost = LanternUpgradeSystem.GetEfficiencyCost(); string text13 = ((capacityLevel >= 5) ? "MAX" : $"↑{capacityCost}"); string text14 = ((efficiencyLevel >= 5) ? "MAX" : $"↑{efficiencyCost}"); text12 = (isChinese ? $"容Lv{capacityLevel}({text13}) 效Lv{efficiencyLevel}({text14}) ★{points}" : $"Cap:{capacityLevel}({text13}) Eff:{efficiencyLevel}({text14}) ★{points}"); } if (text12 != _cachedUpgradeStr) { _cachedUpgradeStr = text12; ((TMP_Text)_upgradeText).text = text12; } } public void Destroy() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } } private void SetAnchor(float ax, float ay, float px, float py, float offX, float offY) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) _rootRect.anchorMin = new Vector2(ax, ay); _rootRect.anchorMax = new Vector2(ax, ay); _rootRect.pivot = new Vector2(px, py); _rootRect.anchoredPosition = new Vector2(offX, offY); } private static GameObject CreateChild(Transform parent, string name) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown GameObject val = new GameObject(name); val.transform.SetParent(parent, false); return val; } private static TextMeshProUGUI CreateTMP(Transform parent, string name, Vector2 anchoredPos, Vector2 size, TextAlignmentOptions align, float fontSize, TMP_FontAsset font = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) GameObject obj = CreateChild(parent, name); RectTransform obj2 = obj.AddComponent(); obj2.anchoredPosition = anchoredPos; obj2.sizeDelta = size; TextMeshProUGUI val = obj.AddComponent(); if ((Object)(object)font != (Object)null) { ((TMP_Text)val).font = font; } ((TMP_Text)val).fontSize = fontSize; ((TMP_Text)val).alignment = align; ((TMP_Text)val).richText = true; ((TMP_Text)val).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val).overflowMode = (TextOverflowModes)0; return val; } private static Color FuelColor(float pct) { //IL_0025: 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) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (pct > 0.5f) { return Color.Lerp(Color.yellow, Color.green, (pct - 0.5f) * 2f); } return Color.Lerp(Color.red, Color.yellow, pct * 2f); } private static float GetMaxFuel(ItemInstanceData data) { if (data == null) { return 0f; } Guid guid = data.guid; if (guid == Guid.Empty) { return 0f; } Item val = LanternHelper.FindWorldItemByGuid(guid); if ((Object)(object)val != (Object)null) { Lantern component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { return component.startingFuel; } } return 0f; } private static void GetPresetParams(HudSizePreset preset, out float panelW, out float panelH, out float barH, out float fontSize) { switch (preset) { case HudSizePreset.Small: panelW = 240f; panelH = 62f; barH = 8f; fontSize = 11f; break; case HudSizePreset.Medium: panelW = 300f; panelH = 76f; barH = 10f; fontSize = 14f; break; case HudSizePreset.Large: panelW = 360f; panelH = 90f; barH = 12f; fontSize = 17f; break; case HudSizePreset.ExtraLarge: panelW = 440f; panelH = 108f; barH = 14f; fontSize = 20f; break; default: panelW = 360f; panelH = 90f; barH = 12f; fontSize = 17f; break; } } private static string GetRestoreSourceName(string key, bool zh) { switch (key) { case "Hit": if (!zh) { return "Kill"; } return "击杀"; case "Bugle": if (!zh) { return "Bugle"; } return "号角"; case "Campfire": if (!zh) { return "Fire"; } return "篝火"; default: return key; } } } internal static class LanternUpgradeSystem { public const int MaxLevel = 5; internal const string KeyPoints = "LSN.UPts"; internal const string KeyCapLv = "LSN.UCap"; internal const string KeyEffLv = "LSN.UEff"; internal const byte UpgradeEventCode = 42; private const byte UpgradeCapacity = 0; private const byte UpgradeEfficiency = 1; private const byte UpgradeAuto = 2; private static int _capacityLevel; private static int _efficiencyLevel; private static int _points; private static bool _menuOpen; private static float _passiveTimer; private static string _cachedCostCsv; private static int[] _cachedCosts; private static string _cachedCapCsv; private static float[] _cachedCapBonus; private static string _cachedEffCsv; private static float[] _cachedEffBonus; private static int _lastRejectActor = -1; private static byte _lastRejectType = byte.MaxValue; private static int _lastRejectPtsBucket = -1; public static int CapacityLevel => _capacityLevel; public static int EfficiencyLevel => _efficiencyLevel; public static int Points => _points; public static bool IsMenuOpen => _menuOpen; public static float CapacityMultiplier { get { if (_capacityLevel <= 0) { return 1f; } float[] capacityBonus = GetCapacityBonus(); int num = Mathf.Clamp(_capacityLevel - 1, 0, capacityBonus.Length - 1); return 1f + capacityBonus[num]; } } public static float EfficiencyMultiplier { get { if (_efficiencyLevel <= 0) { return 1f; } float[] efficiencyBonus = GetEfficiencyBonus(); int num = Mathf.Clamp(_efficiencyLevel - 1, 0, efficiencyBonus.Length - 1); return Mathf.Max(0.01f, 1f - efficiencyBonus[num]); } } public static int GetCapacityCost() { if (_capacityLevel >= 5) { return int.MaxValue; } int[] levelCosts = GetLevelCosts(); if (_capacityLevel >= levelCosts.Length) { return int.MaxValue; } return levelCosts[_capacityLevel]; } public static int GetEfficiencyCost() { if (_efficiencyLevel >= 5) { return int.MaxValue; } int[] levelCosts = GetLevelCosts(); if (_efficiencyLevel >= levelCosts.Length) { return int.MaxValue; } return levelCosts[_efficiencyLevel]; } private static int[] GetLevelCosts() { string text = ((Plugin.UpgradeLevelCostsCsv != null) ? Plugin.UpgradeLevelCostsCsv.Value : "50,100,150,200,250"); if (text != _cachedCostCsv || _cachedCosts == null) { _cachedCostCsv = text; _cachedCosts = ParseIntCsv(text, new int[5] { 50, 100, 150, 200, 250 }); } return _cachedCosts; } private static float[] GetCapacityBonus() { string text = ((Plugin.UpgradeCapacityBonusCsv != null) ? Plugin.UpgradeCapacityBonusCsv.Value : "0.2,0.4,0.6,0.8,1.0"); if (text != _cachedCapCsv || _cachedCapBonus == null) { _cachedCapCsv = text; _cachedCapBonus = ParseFloatCsv(text, new float[5] { 0.2f, 0.4f, 0.6f, 0.8f, 1f }); } return _cachedCapBonus; } private static float[] GetEfficiencyBonus() { string text = ((Plugin.UpgradeEfficiencyBonusCsv != null) ? Plugin.UpgradeEfficiencyBonusCsv.Value : "0.1,0.2,0.3,0.4,0.5"); if (text != _cachedEffCsv || _cachedEffBonus == null) { _cachedEffCsv = text; _cachedEffBonus = ParseFloatCsv(text, new float[5] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f }); } return _cachedEffBonus; } private static int[] ParseIntCsv(string csv, int[] fallback) { if (string.IsNullOrWhiteSpace(csv)) { return fallback; } string[] array = csv.Split(new char[1] { ',' }); int[] array2 = new int[Mathf.Max(array.Length, fallback.Length)]; for (int i = 0; i < array2.Length; i++) { array2[i] = ((i < fallback.Length) ? fallback[i] : 999999); } for (int j = 0; j < array.Length && j < array2.Length; j++) { if (int.TryParse(array[j].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { array2[j] = result; } } return array2; } private static float[] ParseFloatCsv(string csv, float[] fallback) { if (string.IsNullOrWhiteSpace(csv)) { return fallback; } string[] array = csv.Split(new char[1] { ',' }); float[] array2 = new float[Mathf.Max(array.Length, fallback.Length)]; for (int i = 0; i < array2.Length; i++) { array2[i] = ((i < fallback.Length) ? fallback[i] : 0f); } for (int j = 0; j < array.Length && j < array2.Length; j++) { if (float.TryParse(array[j].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { array2[j] = result; } } return array2; } public static void Tick() { if (Plugin.EnableUpgradeSystem == null || !Plugin.EnableUpgradeSystem.Value) { return; } float num = ((Plugin.UpgradePassiveTickInterval != null) ? Plugin.UpgradePassiveTickInterval.Value : 60f); if (num <= 0f) { return; } _passiveTimer += Time.deltaTime; if (!(_passiveTimer < num)) { _passiveTimer = 0f; int num2 = ((Plugin.UpgradePassivePointsPerTick == null) ? 1 : Plugin.UpgradePassivePointsPerTick.Value); if (num2 > 0) { AddPoints(num2, "Passive"); } } } public static void AddEvent(string source) { if (Plugin.EnableUpgradeSystem != null && Plugin.EnableUpgradeSystem.Value) { int num = 0; switch (source) { case "Hit": num = ((Plugin.UpgradeHitPoints == null) ? 1 : Plugin.UpgradeHitPoints.Value); break; case "Campfire": num = ((Plugin.UpgradeCampfirePoints != null) ? Plugin.UpgradeCampfirePoints.Value : 5); break; case "Bugle": num = ((Plugin.UpgradeBuglePoints != null) ? Plugin.UpgradeBuglePoints.Value : 3); break; } if (num > 0) { AddPoints(num, source); } } } public static void AddPoints(int amount, string source = null) { if (amount <= 0 || Plugin.EnableUpgradeSystem == null || !Plugin.EnableUpgradeSystem.Value) { return; } int points = _points; _points += amount; string text = source ?? "n/a"; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [Upgrade] AddPoints +{amount} (src={text}) → {_points} (was {points})"); } SyncToNetwork(); if (IsHostAuthoritative()) { SendUpgradeRequest(2); return; } bool flag = true; while (flag) { flag = false; if (_capacityLevel <= _efficiencyLevel && _capacityLevel < 5 && _points >= GetCapacityCost()) { flag = TryUpgradeCapacity(); } else if (_efficiencyLevel < 5 && _points >= GetEfficiencyCost()) { flag = TryUpgradeEfficiency(); } } } public static bool TryUpgradeCapacity() { if (_capacityLevel >= 5) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [Upgrade] Capacity upgrade BLOCKED: already MAX level"); } return false; } int capacityCost = GetCapacityCost(); if (_points < capacityCost) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [Upgrade] Capacity upgrade BLOCKED: points={_points} < cost={capacityCost}"); } return false; } _points -= capacityCost; _capacityLevel++; ApplyEfficiencyDrainSource(); ApplyCapacityToExistingLanterns(); SyncToNetwork(); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[Upgrade] Capacity → Lv{_capacityLevel} (×{CapacityMultiplier:F1})"); } return true; } public static bool TryUpgradeEfficiency() { if (_efficiencyLevel >= 5) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [Upgrade] Efficiency upgrade BLOCKED: already MAX level"); } return false; } int efficiencyCost = GetEfficiencyCost(); if (_points < efficiencyCost) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [Upgrade] Efficiency upgrade BLOCKED: points={_points} < cost={efficiencyCost}"); } return false; } _points -= efficiencyCost; _efficiencyLevel++; ApplyEfficiencyDrainSource(); SyncToNetwork(); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[Upgrade] Efficiency → Lv{_efficiencyLevel} (×{EfficiencyMultiplier:F2})"); } return true; } public static void ToggleMenu() { _menuOpen = !_menuOpen; } public static bool IsHostAuthoritative() { if (PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode) { return !PhotonNetwork.IsMasterClient; } return false; } public static void RequestUpgrade(byte upgradeType) { if (!IsHostAuthoritative()) { switch (upgradeType) { case 0: TryUpgradeCapacity(); break; case 1: TryUpgradeEfficiency(); break; } } else { SendUpgradeRequest(upgradeType); } } private static void SendUpgradeRequest(byte upgradeType) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0022: Expected O, but got Unknown PhotonNetwork.RaiseEvent((byte)42, (object)new byte[1] { upgradeType }, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [Upgrade] Sent upgrade request type={upgradeType} to master"); } } internal static void HandleUpgradeEvent(EventData photonEvent) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Expected O, but got Unknown //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Expected O, but got Unknown //IL_0285: Expected O, but got Unknown if (!PhotonNetwork.IsMasterClient || photonEvent.Code != 42) { return; } int sender = photonEvent.Sender; if (!(photonEvent.CustomData is byte[] array) || array.Length == 0) { return; } byte b = array[0]; Player value = null; Room currentRoom = PhotonNetwork.CurrentRoom; if (((currentRoom != null) ? currentRoom.Players : null) != null) { PhotonNetwork.CurrentRoom.Players.TryGetValue(sender, out value); } if (value == null) { return; } Hashtable val = (Hashtable)(((object)value.CustomProperties) ?? ((object)new Hashtable())); int num = ((((Dictionary)(object)val).ContainsKey((object)"LSN.UPts") && val[(object)"LSN.UPts"] is int) ? ((int)val[(object)"LSN.UPts"]) : 0); int num2 = ((((Dictionary)(object)val).ContainsKey((object)"LSN.UCap") && val[(object)"LSN.UCap"] is int) ? ((int)val[(object)"LSN.UCap"]) : 0); int num3 = ((((Dictionary)(object)val).ContainsKey((object)"LSN.UEff") && val[(object)"LSN.UEff"] is int) ? ((int)val[(object)"LSN.UEff"]) : 0); num2 = Mathf.Clamp(num2, 0, 5); num3 = Mathf.Clamp(num3, 0, 5); bool flag = false; switch (b) { case 2: { bool flag2 = true; while (flag2) { flag2 = false; int[] levelCosts3 = GetLevelCosts(); int num6 = ((num2 < levelCosts3.Length) ? levelCosts3[num2] : int.MaxValue); int num7 = ((num3 < levelCosts3.Length) ? levelCosts3[num3] : int.MaxValue); if (num2 <= num3 && num2 < 5 && num >= num6) { num -= num6; num2++; flag2 = true; flag = true; } else if (num3 < 5 && num >= num7) { num -= num7; num3++; flag2 = true; flag = true; } } break; } case 0: { int[] levelCosts2 = GetLevelCosts(); int num5 = ((num2 < levelCosts2.Length) ? levelCosts2[num2] : int.MaxValue); if (num2 < 5 && num >= num5) { num -= num5; num2++; flag = true; } break; } case 1: { int[] levelCosts = GetLevelCosts(); int num4 = ((num3 < levelCosts.Length) ? levelCosts[num3] : int.MaxValue); if (num3 < 5 && num >= num4) { num -= num4; num3++; flag = true; } break; } } if (flag) { Player obj = value; Hashtable val2 = new Hashtable(); ((Dictionary)val2).Add((object)"LSN.UPts", (object)num); ((Dictionary)val2).Add((object)"LSN.UCap", (object)num2); ((Dictionary)val2).Add((object)"LSN.UEff", (object)num3); obj.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null); _lastRejectActor = -1; _lastRejectType = byte.MaxValue; _lastRejectPtsBucket = -1; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Upgrade] Master approved upgrade for #{sender}: pts={num}, cap=Lv{num2}, eff=Lv{num3}"); } return; } int num8 = num / 10; if (_lastRejectActor != sender || _lastRejectType != b || _lastRejectPtsBucket != num8) { _lastRejectActor = sender; _lastRejectType = b; _lastRejectPtsBucket = num8; int num9 = (num8 + 1) * 10 - num; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Upgrade] Master rejected upgrade for #{sender} type={b}: insufficient pts={num} (silenced until +{num9} more pts or upgrade succeeds)"); } } } public static void SyncFromNetworkIfNeeded() { if (!IsHostAuthoritative()) { return; } Player localPlayer = PhotonNetwork.LocalPlayer; if (((localPlayer != null) ? localPlayer.CustomProperties : null) == null) { return; } Hashtable customProperties = PhotonNetwork.LocalPlayer.CustomProperties; int points = _points; int capacityLevel = _capacityLevel; int efficiencyLevel = _efficiencyLevel; if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UPts", out object value) && value is int) { _points = (int)value; } if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UCap", out value) && value is int) { int num = Mathf.Clamp((int)value, 0, 5); if (num != _capacityLevel) { _capacityLevel = num; ApplyEfficiencyDrainSource(); ApplyCapacityToExistingLanterns(); } } if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UEff", out value) && value is int) { int num2 = Mathf.Clamp((int)value, 0, 5); if (num2 != _efficiencyLevel) { _efficiencyLevel = num2; ApplyEfficiencyDrainSource(); } } bool num3 = capacityLevel != _capacityLevel || efficiencyLevel != _efficiencyLevel; int num4 = Mathf.Abs(_points - points); if (num3 || num4 >= 3) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Upgrade] Client synced from master: pts={points}→{_points}, cap=Lv{capacityLevel}→Lv{_capacityLevel}, eff=Lv{efficiencyLevel}→Lv{_efficiencyLevel}"); } } else if (num4 > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [Upgrade] Client synced (minor pts jitter): {points}→{_points}"); } } } public static void ApplyEfficiencyDrainSource() { if (_efficiencyLevel > 0) { LanternHelper.SetDrainSource("upgrade_efficiency", EfficiencyMultiplier); } else { LanternHelper.RemoveDrainSource("upgrade_efficiency"); } } public static void ApplyCapacityToExistingLanterns() { try { int num = (int)((Plugin.LanternMaxFuel != null) ? Plugin.LanternMaxFuel.Value : LanternFuelOption.GameDefault); if (num == 0) { return; } float num2 = ((num > 0) ? ((float)num) : 99999f) * CapacityMultiplier; Lantern[] array = Object.FindObjectsByType((FindObjectsSortMode)0); if (array == null || array.Length == 0) { return; } int num3 = 0; Lantern[] array2 = array; foreach (Lantern val in array2) { if ((Object)(object)val == (Object)null || LanternHelper.IsSpecialLantern(val) || ((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null && !((MonoBehaviourPun)val).photonView.IsMine)) { continue; } float startingFuel = val.startingFuel; if (!Mathf.Approximately(startingFuel, num2)) { val.startingFuel = num2; num3++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [Upgrade] Refresh lantern startingFuel: {startingFuel:F1} → {num2:F1} (capLv={_capacityLevel})"); } } } if (num3 > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Upgrade] Capacity applied to {num3} lantern(s), newMax={num2:F1}"); } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[DEBUG] [Upgrade] ApplyCapacityToExistingLanterns failed: " + ex.Message)); } } } private static void SyncToNetwork() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0055: Expected O, but got Unknown try { if (PhotonNetwork.IsConnected && PhotonNetwork.LocalPlayer != null) { Hashtable val = new Hashtable(); ((Dictionary)val).Add((object)"LSN.UPts", (object)_points); ((Dictionary)val).Add((object)"LSN.UCap", (object)_capacityLevel); ((Dictionary)val).Add((object)"LSN.UEff", (object)_efficiencyLevel); Hashtable val2 = val; PhotonNetwork.LocalPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DEBUG] [Upgrade] SyncToNetwork failed: " + ex.Message)); } } } public static void LoadFromNetwork() { try { if (!PhotonNetwork.IsConnected || PhotonNetwork.LocalPlayer == null) { return; } Hashtable customProperties = PhotonNetwork.LocalPlayer.CustomProperties; if (customProperties != null) { if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UPts", out object value) && value is int) { _points = (int)value; } if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UCap", out value) && value is int) { _capacityLevel = Mathf.Clamp((int)value, 0, 5); } if (((Dictionary)(object)customProperties).TryGetValue((object)"LSN.UEff", out value) && value is int) { _efficiencyLevel = Mathf.Clamp((int)value, 0, 5); } ApplyEfficiencyDrainSource(); if (_capacityLevel > 0) { ApplyCapacityToExistingLanterns(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Upgrade] Loaded from network: pts={_points}, cap=Lv{_capacityLevel}, eff=Lv{_efficiencyLevel}"); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[DEBUG] [Upgrade] LoadFromNetwork failed: " + ex.Message)); } } } public static void Reset() { _capacityLevel = 0; _efficiencyLevel = 0; _points = 0; _menuOpen = false; LanternHelper.RemoveDrainSource("upgrade_efficiency"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [Upgrade] Reset: all upgrade state cleared"); } } } internal class LanternUpgradeUI : MonoBehaviour { private GUIStyle _boxStyle; private GUIStyle _titleStyle; private GUIStyle _labelStyle; private GUIStyle _buttonStyle; private GUIStyle _pointsStyle; private GUIStyle _hintStyle; private Texture2D _bgTex; private bool _stylesInit; private void OnGUI() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) if (LanternUpgradeSystem.IsMenuOpen && Plugin.EnableUpgradeSystem != null && Plugin.EnableUpgradeSystem.Value) { EnsureStyles(); bool isChinese = LanguageHelper.IsChinese; bool flag = LanternUpgradeSystem.IsHostAuthoritative(); float num = 280f; float num2 = (flag ? 250f : 220f); float num3 = ((float)Screen.width - num) * 0.5f; float num4 = ((float)Screen.height - num2) * 0.5f; GUI.DrawTexture(new Rect(num3, num4, num, num2), (Texture)(object)_bgTex); float num5 = num3 + 15f; float num6 = num4 + 10f; float num7 = 26f; float num8 = num - 30f; GUI.Label(new Rect(num5, num6, num8, num7), isChinese ? "灯笼升级" : "Lantern Upgrade", _titleStyle); num6 += num7 + 4f; GUI.Label(new Rect(num5, num6, num8, num7), string.Format(isChinese ? "升级点数: {0}" : "Points: {0}", LanternUpgradeSystem.Points), _pointsStyle); num6 += num7 + 8f; int capacityLevel = LanternUpgradeSystem.CapacityLevel; bool flag2 = capacityLevel >= 5; string text = (flag2 ? string.Format(isChinese ? "容量 Lv{0} (MAX) ×{1:F1}" : "Capacity Lv{0} (MAX) ×{1:F1}", capacityLevel, LanternUpgradeSystem.CapacityMultiplier) : string.Format(isChinese ? "容量 Lv{0} → Lv{1} (×{2:F1}) 费用: {3}" : "Capacity Lv{0}→{1} (×{2:F1}) Cost: {3}", capacityLevel, capacityLevel + 1, LanternUpgradeSystem.CapacityMultiplier + 0.2f, LanternUpgradeSystem.GetCapacityCost())); GUI.Label(new Rect(num5, num6, num8, num7), text, _labelStyle); num6 += num7; GUI.enabled = !flag2 && LanternUpgradeSystem.Points >= LanternUpgradeSystem.GetCapacityCost() && !flag; if (GUI.Button(new Rect(num5, num6, num8, 28f), isChinese ? "升级容量" : "Upgrade Capacity", _buttonStyle)) { LanternUpgradeSystem.RequestUpgrade(0); } GUI.enabled = true; num6 += 34f; int efficiencyLevel = LanternUpgradeSystem.EfficiencyLevel; bool flag3 = efficiencyLevel >= 5; string text2 = (flag3 ? string.Format(isChinese ? "效率 Lv{0} (MAX) ×{1:F2}" : "Efficiency Lv{0} (MAX) ×{1:F2}", efficiencyLevel, LanternUpgradeSystem.EfficiencyMultiplier) : string.Format(isChinese ? "效率 Lv{0} → Lv{1} (×{2:F2}) 费用: {3}" : "Efficiency Lv{0}→{1} (×{2:F2}) Cost: {3}", efficiencyLevel, efficiencyLevel + 1, LanternUpgradeSystem.EfficiencyMultiplier - 0.08f, LanternUpgradeSystem.GetEfficiencyCost())); GUI.Label(new Rect(num5, num6, num8, num7), text2, _labelStyle); num6 += num7; GUI.enabled = !flag3 && LanternUpgradeSystem.Points >= LanternUpgradeSystem.GetEfficiencyCost() && !flag; if (GUI.Button(new Rect(num5, num6, num8, 28f), isChinese ? "升级效率" : "Upgrade Efficiency", _buttonStyle)) { LanternUpgradeSystem.RequestUpgrade(1); } GUI.enabled = true; if (flag) { num6 += 34f; GUI.Label(new Rect(num5, num6, num8, num7), isChinese ? "⚠ 仅房主可升级" : "⚠ Host only", _hintStyle); } } } private void EnsureStyles() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0150: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown if (!_stylesInit) { _stylesInit = true; _bgTex = new Texture2D(1, 1); _bgTex.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.1f, 0.88f)); _bgTex.Apply(); _titleStyle = new GUIStyle(GUI.skin.label) { fontSize = 18, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _titleStyle.normal.textColor = new Color(1f, 0.85f, 0.3f); _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, alignment = (TextAnchor)3 }; _labelStyle.normal.textColor = Color.white; _pointsStyle = new GUIStyle(GUI.skin.label) { fontSize = 15, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _pointsStyle.normal.textColor = new Color(0.4f, 1f, 0.4f); _hintStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)2, alignment = (TextAnchor)4 }; _hintStyle.normal.textColor = new Color(1f, 0.6f, 0.3f); _buttonStyle = new GUIStyle(GUI.skin.button) { fontSize = 14 }; _boxStyle = new GUIStyle(GUI.skin.box); } } private void OnDestroy() { if ((Object)(object)_bgTex != (Object)null) { Object.Destroy((Object)(object)_bgTex); _bgTex = null; } } } internal static class MathExtensions { public const float Epsilon = 0.001f; public static bool Approximately(float a, float b) { float num = a - b; if (num > -0.001f) { return num < 0.001f; } return false; } public static bool IsZero(float value) { if (value > -0.001f) { return value < 0.001f; } return false; } } internal static class ModConfigLocalization { [CompilerGenerated] private sealed class d__8 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Transform root; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; LocalizeTextInHierarchy(root); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly FieldInfo _descriptionBackingField = typeof(ConfigDescription).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); private static Type _tmpTextType; private static PropertyInfo _tmpTextProp; public static void PatchDisplayNames(Harmony harmony) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("com.github.PEAKModding.PEAKLib.ModConfig", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return; } Assembly assembly = ((object)value.Instance).GetType().Assembly; HarmonyMethod val = new HarmonyMethod(typeof(ModConfigLocalization).GetMethod("ModConfigDisplayNamePostfix", BindingFlags.Static | BindingFlags.NonPublic)); int num = 0; foreach (Type item in from t in assembly.GetTypes() where t != null && !t.IsAbstract && !t.IsInterface && t.FullName != null && t.FullName.StartsWith("PEAKLib.ModConfig.SettingOptions.BepInEx", StringComparison.Ordinal) select t) { MethodInfo methodInfo = AccessTools.Method(item, "GetDisplayName", Type.EmptyTypes, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] Patched {num} ModConfig GetDisplayName methods"); } HarmonyMethod val2 = new HarmonyMethod(typeof(ModConfigLocalization).GetMethod("ModConfigUiChangedPostfix", BindingFlags.Static | BindingFlags.NonPublic)); Type type = assembly.GetType("PEAKLib.ModConfig.Components.ModdedSettingsMenu"); if (!(type != null)) { return; } string[] array = new string[4] { "OnEnable", "ShowSettings", "SetSection", "UpdateSectionTabs" }; foreach (string methodName in array) { MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == methodName); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[DEBUG] Patched ModdedSettingsMenu." + methodName + " for UI localization")); } } } } public static void RefreshCache() { try { if (!Chainloader.PluginInfos.TryGetValue("com.github.PEAKModding.PEAKLib.ModConfig", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return; } Type type = ((object)value.Instance).GetType(); string[] array = new string[3] { "EntriesProcessed", "ModdedKeys", "GetValidKeyPaths" }; foreach (string name in array) { if (type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) is IList list) { list.Clear(); } } array = new string[3] { "GenerateValidKeyPaths", "ProcessModEntries", "LoadModSettings" }; foreach (string name2 in array) { type.GetMethod(name2, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(null, null); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] ModConfig cache refreshed for localization"); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[DEBUG] RefreshModConfigCache failed: " + ex.Message)); } } } public static void ApplyLocalizedDescriptions() { try { SetDescription((ConfigEntryBase)(object)Plugin.LanternMaxFuel, LanguageHelper.L("Fuel capacity. GameDefault=unchanged, others=seconds, Infinite=never burns out.", "燃料上限。默认=不改,其他=秒数,无限=永不熄灭。")); SetDescription((ConfigEntryBase)(object)Plugin.EnableWarmthReduction, LanguageHelper.L("Enable warmth multiplier below.", "启用下方的暖值倍率。")); SetDescription((ConfigEntryBase)(object)Plugin.LanternWarmthMultiplier, LanguageHelper.L("Cold resistance when holding lit lantern. 1=full block, 0.5=half cold, 0=no protection. Step=0.05", "持灯抗寒能力。1=完全抵御,0.5=减半受冷,0=无保护。步进=0.05")); SetDescription((ConfigEntryBase)(object)Plugin.EnableWarmthRestore, LanguageHelper.L("Master switch for warmth restore (zombie hit).", "回暖总开关(打僵尸回暖)。")); SetDescription((ConfigEntryBase)(object)Plugin.HitRestoreWarmth, LanguageHelper.L("Warmth per zombie hit (seconds).", "每次击杀僵尸回暖(秒)。")); SetDescription((ConfigEntryBase)(object)Plugin.RestoreRadius, LanguageHelper.L("Range for hit restore (meters).", "击杀回暖范围(米)。")); SetDescription((ConfigEntryBase)(object)Plugin.HitRestoreCooldown, LanguageHelper.L("Min interval between hit restores (seconds). Step=0.1", "击杀回暖最小间隔(秒)。步进=0.1")); SetDescription((ConfigEntryBase)(object)Plugin.ReserveWarmthMax, LanguageHelper.L("Reserve = max × ratio. Overflow fills reserve; burning drains it first.", "备用池 = 上限×比例。溢出存备用,燃烧优先消耗。")); SetDescription((ConfigEntryBase)(object)Plugin.AutoRefillEnabled, LanguageHelper.L("When lantern is out of fuel, slowly refill up to a cap.", "灯熄灭后自动缓慢回燃料至上限。")); SetDescription((ConfigEntryBase)(object)Plugin.AutoRefillCapPercent, LanguageHelper.L("Refill upper cap (ratio of max fuel). Step=0.05", "自动回料上限(占满燃料比例)。步进=0.05")); SetDescription((ConfigEntryBase)(object)Plugin.AutoRefillRate, LanguageHelper.L("Seconds of fuel gained per real second. Step=0.1", "每现实秒回复的燃料秒数。步进=0.1")); SetDescription((ConfigEntryBase)(object)Plugin.AutoRefillDaytimeOnly, LanguageHelper.L("Only refill during daytime.", "仅在白天自动回料。")); SetDescription((ConfigEntryBase)(object)Plugin.AutoRefillRequireHold, LanguageHelper.L("Only refill while a player is holding the lantern.", "仅在玩家持有灯时自动回料。")); SetDescription((ConfigEntryBase)(object)Plugin.BugleUltimateEnabled, LanguageHelper.L("Enable bugle ultimate: nearby players' lanterns refill, long global cooldown.", "启用号角大招:附近玩家灯笼回满,全局长CD。")); SetDescription((ConfigEntryBase)(object)Plugin.BugleUltimateCooldown, LanguageHelper.L("Global cooldown between bugle ultimates (seconds).", "号角大招CD(秒),全局共享。")); SetDescription((ConfigEntryBase)(object)Plugin.BugleUltimateRadius, LanguageHelper.L("Ultimate effect radius (meters).", "大招生效范围(米)。")); SetDescription((ConfigEntryBase)(object)Plugin.BugleUltimateRestore, LanguageHelper.L("Warmth gained per ultimate. -1 means refill to max.", "单次大招回暖秒数。-1 表示回满。")); SetDescription((ConfigEntryBase)(object)Plugin.PurgeExtraLanterns, LanguageHelper.L("Auto destroy duplicate lanterns on a player (Faerie lantern exempt).", "自动销毁玩家身上的多余灯笼(仙子提灯豁免)。")); SetDescription((ConfigEntryBase)(object)Plugin.StrayBugleCleanupEnabled, LanguageHelper.L("Host cleans stray (unheld, far away) bugles after grace period.", "房主自动清理无人持有且偏远的号角。")); SetDescription((ConfigEntryBase)(object)Plugin.StrayBugleDistance, LanguageHelper.L("Distance from nearest player to count as stray (meters).", "距最近玩家大于此距离才算偏远(米)。")); SetDescription((ConfigEntryBase)(object)Plugin.StrayBugleGracePeriod, LanguageHelper.L("Must stay stray for N seconds before cleanup.", "持续N秒满足条件才清理。")); SetDescription((ConfigEntryBase)(object)Plugin.EnableCampfireRefuel, LanguageHelper.L("Light campfire = refill lantern; near campfire = no fuel drain.", "篝火补满&暂停消耗。")); SetDescription((ConfigEntryBase)(object)Plugin.FlashlightDrainMultiplier, LanguageHelper.L("Fuel drain multiplier when BPR flashlight mode is active. 1=normal, 2=double. Step=0.05", "BPR手电筒模式激活时的燃料消耗倍率。1=正常,2=双倍消耗。步进=0.05")); SetDescription((ConfigEntryBase)(object)Plugin.CompanionDrainMultiplier, LanguageHelper.L("Drain multiplier when companions are nearby. More players = stronger effect (scales to 3). Step=0.05", "附近有同伴时的消耗倍率。人越多效果越强(最多按3人计算)。步进=0.05")); SetDescription((ConfigEntryBase)(object)Plugin.SoloDrainMultiplier, LanguageHelper.L("Drain multiplier when alone. Step=0.05", "独自行动时的消耗倍率。步进=0.05")); SetDescription((ConfigEntryBase)(object)Plugin.ProximityGracePeriod, LanguageHelper.L("Seconds before companion/solo multiplier activates after state change.", "状态切换后,结伴/独行倍率生效前的等待秒数。")); SetDescription((ConfigEntryBase)(object)Plugin.EnableHud, LanguageHelper.L("Show visual HUD panel (fuel bar, multipliers, status).", "显示可视化信息面板(燃料条、倍率、状态)。")); SetDescription((ConfigEntryBase)(object)Plugin.HudPos, LanguageHelper.L("HUD panel screen position (8 positions).", "HUD面板屏幕位置(8方位)。")); SetDescription((ConfigEntryBase)(object)Plugin.HudSize, LanguageHelper.L("HUD panel size preset (Small/Medium/Large/ExtraLarge).", "HUD面板尺寸预设(小/中/大/超大)。")); SetDescription((ConfigEntryBase)(object)Plugin.ShowDayNightOnHud, LanguageHelper.L("Show day/night info on HUD (day count, time, BPR darkness).", "在HUD上显示日夜信息(天数、时间、BPR黑暗状态)。")); SetDescription((ConfigEntryBase)(object)Plugin.SpawnItemsKey, LanguageHelper.L("Hotkey to spawn Bugle & Backpack if missing. Set to None to disable.", "快捷键生成号角和背包(身上没有时)。设为None可禁用。")); SetDescription((ConfigEntryBase)(object)Plugin.BugleRecallKey, LanguageHelper.L("Host-only hotkey: destroy ALL bugles in the scene (held or dropped). Set to None to disable.", "房主专属快捷键:销毁场景里所有号角(含被持有/掉落的)。设为None可禁用。")); SetDescription((ConfigEntryBase)(object)Plugin.MuteZombieTornado, LanguageHelper.L("Mute all zombie and tornado sounds.", "静音所有僵尸和龙卷风的声音。")); SetDescription((ConfigEntryBase)(object)Plugin.ActivePreset, LanguageHelper.L("Config preset. Custom=manual, Casual=easy, Balanced=default, Hardcore=challenging. Switching applies all values at once.", "配置预设。自定义=手动调节,休闲=轻松,平衡=默认值,硬核=高挑战。切换时一键覆盖所有平衡相关配置。")); SetDescription((ConfigEntryBase)(object)Plugin.EnableFuelIndicator3D, LanguageHelper.L("Show 3D fuel percentage above lit lanterns.", "在点燃的灯笼上方显示 3D 燃料百分比。")); SetDescription((ConfigEntryBase)(object)Plugin.EnableUpgradeSystem, LanguageHelper.L("Enable lantern upgrade system. Points auto-spend to upgrade (capacity first, then efficiency).", "启用灯笼升级系统。点数足够时自动升级(先容量再效率)。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeLevelCostsCsv, LanguageHelper.L("Point cost per level (comma separated, 5 values).", "每级升级所需点数(逗号分隔,5 个值)。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeCapacityBonusCsv, LanguageHelper.L("Capacity bonus per level (comma separated, 5 values). Final multiplier = 1 + value.", "每级容量加成(5 个值)。最终倍率 = 1 + 值。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeEfficiencyBonusCsv, LanguageHelper.L("Efficiency bonus per level (comma separated, 5 values). Final drain multiplier = 1 - value.", "每级效率加成(5 个值)。最终消耗倍率 = 1 - 值。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradePassiveTickInterval, LanguageHelper.L("Passive points accrual interval in seconds.", "被动累积点数的间隔秒数。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradePassivePointsPerTick, LanguageHelper.L("Points gained per passive tick.", "每次被动累积获得的点数。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeHitPoints, LanguageHelper.L("Points gained per zombie hit.", "每次打僵尸获得的点数。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeCampfirePoints, LanguageHelper.L("Points gained per campfire light-up.", "每次点燃篝火获得的点数。")); SetDescription((ConfigEntryBase)(object)Plugin.UpgradeBuglePoints, LanguageHelper.L("Points gained per bugle ultimate trigger.", "每次号角大招触发获得的点数。")); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DEBUG] ApplyLocalizedDescriptions failed: " + ex.Message)); } } } private static void ModConfigDisplayNamePostfix(ref string __result) { if (!string.IsNullOrEmpty(__result)) { string localizedKeyName = GetLocalizedKeyName(__result.Replace(" ", "")); if (localizedKeyName != null) { __result = localizedKeyName; } } } private static void ModConfigUiChangedPostfix(MonoBehaviour __instance) { __instance.StartCoroutine(LocalizeModConfigUiDeferred(((Component)__instance).transform)); } [IteratorStateMachine(typeof(d__8))] private static IEnumerator LocalizeModConfigUiDeferred(Transform root) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(0) { root = root }; } private static void LocalizeTextInHierarchy(Transform root) { try { if (_tmpTextType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { _tmpTextType = assemblies[i].GetType("TMPro.TextMeshProUGUI"); if (_tmpTextType != null) { _tmpTextProp = _tmpTextType.GetProperty("text"); break; } } } if (_tmpTextType == null || _tmpTextProp == null) { return; } Component[] componentsInChildren = ((Component)root).GetComponentsInChildren(_tmpTextType, true); foreach (Component obj in componentsInChildren) { string text = _tmpTextProp.GetValue(obj) as string; if (!string.IsNullOrEmpty(text)) { string localizedKeyName = GetLocalizedKeyName(text.Replace(" ", "")); if (localizedKeyName != null && localizedKeyName != text) { _tmpTextProp.SetValue(obj, localizedKeyName); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DEBUG] LocalizeTextInHierarchy: " + ex.Message)); } } } private static string GetLocalizedKeyName(string key) { bool isChinese = LanguageHelper.IsChinese; switch (key) { case "Lantern": if (!isChinese) { return "Lantern"; } return "灯笼"; case "Restore": if (!isChinese) { return "Restore"; } return "回暖"; case "Bugle": if (!isChinese) { return "Bugle"; } return "号角"; case "Upgrade": if (!isChinese) { return "Upgrade"; } return "灯笼升级"; case "Display": if (!isChinese) { return "Display"; } return "显示与操作"; case "LanternShootZombiesNight": if (!isChinese) { return "LanternShootZombiesNight"; } return "煤灯与夜晚"; case "EnableWarmthReduction": if (!isChinese) { return "Enable Warmth Reduction"; } return "暖值倍率开关"; case "LanternWarmthMultiplier": if (!isChinese) { return "Warmth Multiplier"; } return "暖值倍率"; case "LanternMaxFuel": if (!isChinese) { return "Max Fuel"; } return "燃料上限"; case "GameDefault": if (!isChinese) { return "Game Default"; } return "默认"; case "Seconds30": if (!isChinese) { return "30 Seconds"; } return "30秒"; case "Seconds60": if (!isChinese) { return "60 Seconds"; } return "60秒"; case "Seconds90": if (!isChinese) { return "90 Seconds"; } return "90秒"; case "Seconds120": if (!isChinese) { return "120 Seconds"; } return "120秒"; case "Seconds240": if (!isChinese) { return "240 Seconds"; } return "240秒"; case "Infinite": if (!isChinese) { return "Infinite"; } return "无限"; case "EnableWarmthRestore": if (!isChinese) { return "Enable Restore"; } return "回暖总开关"; case "HitRestoreWarmth": if (!isChinese) { return "Hit Restore"; } return "击杀回暖"; case "RestoreRadius": if (!isChinese) { return "Restore Radius"; } return "回暖范围"; case "HitRestoreCooldown": if (!isChinese) { return "Restore Cooldown"; } return "回暖冷却"; case "RescueRestoreWarmth": case "HuddleRestoreWarmth": case "RestoreVarietyCount": case "BugleRestoreWarmth": case "ChestRestoreWarmth": case "HuddleTickInterval": case "UseItemRestoreWarmth": case "ConsumeRestoreWarmth": case "CookingRestoreWarmth": case "UnifiedRestoreWarmth": case "CookedFoodBonusMultiplier": case "UseUnifiedRestore": return null; case "AutoRefillEnabled": if (!isChinese) { return "Auto Refill"; } return "自动回料开关"; case "AutoRefillCapPercent": if (!isChinese) { return "Refill Cap"; } return "回料上限比例"; case "AutoRefillRate": if (!isChinese) { return "Refill Rate"; } return "回料速率"; case "AutoRefillDaytimeOnly": if (!isChinese) { return "Daytime Only"; } return "仅白天回料"; case "AutoRefillRequireHold": if (!isChinese) { return "Require Hold"; } return "仅持有时回料"; case "BugleUltimateEnabled": if (!isChinese) { return "Ultimate Enabled"; } return "大招开关"; case "BugleUltimateCooldown": if (!isChinese) { return "Ultimate Cooldown"; } return "大招CD"; case "BugleUltimateRadius": if (!isChinese) { return "Ultimate Radius"; } return "大招范围"; case "BugleUltimateRestore": if (!isChinese) { return "Ultimate Restore"; } return "大招回暖量"; case "PurgeExtraLanterns": if (!isChinese) { return "Purge Extra Lanterns"; } return "自动销毁多余灯"; case "StrayBugleCleanupEnabled": if (!isChinese) { return "Cleanup Stray Bugle"; } return "清理无主号角"; case "StrayBugleDistance": if (!isChinese) { return "Stray Distance"; } return "无主距离阈值"; case "StrayBugleGracePeriod": if (!isChinese) { return "Stray Grace"; } return "无主持续时间"; case "ReserveWarmthMax": if (!isChinese) { return "Reserve Ratio"; } return "备用池比例"; case "EnableCampfireRefuel": if (!isChinese) { return "Campfire Refuel"; } return "篝火补满&暂停消耗"; case "EnableHud": if (!isChinese) { return "Enable HUD"; } return "信息面板"; case "HudPos": if (!isChinese) { return "HUD Position"; } return "面板位置"; case "HudSize": case "HudFontSize": if (!isChinese) { return "HUD Size"; } return "面板尺寸"; case "ShowDayNightOnHud": if (!isChinese) { return "Day/Night HUD"; } return "显示日夜"; case "SpawnItemsKey": if (!isChinese) { return "Spawn Items Key"; } return "生成装备快捷键"; case "BugleRecallKey": if (!isChinese) { return "Bugle Recall Key"; } return "召回号角快捷键"; case "MuteZombieTornado": if (!isChinese) { return "Mute Zombie & Tornado"; } return "僵尸龙卷风静音"; case "EnableFuelIndicator3D": if (!isChinese) { return "3D Fuel Indicator"; } return "3D燃料指示"; case "EnableUpgradeSystem": if (!isChinese) { return "Upgrade System"; } return "升级系统"; case "LevelCostsCsv": if (!isChinese) { return "Level Costs"; } return "升级消耗点数"; case "CapacityBonusCsv": if (!isChinese) { return "Capacity Bonus"; } return "容量加成表"; case "EfficiencyBonusCsv": if (!isChinese) { return "Efficiency Bonus"; } return "效率加成表"; case "PassiveTickInterval": if (!isChinese) { return "Passive Interval"; } return "被动累积间隔"; case "PassivePointsPerTick": if (!isChinese) { return "Passive Points"; } return "被动每次点数"; case "HitPoints": if (!isChinese) { return "Hit Points"; } return "打僵尸点数"; case "CampfirePoints": if (!isChinese) { return "Campfire Points"; } return "篝火点数"; case "BuglePoints": if (!isChinese) { return "Bugle Points"; } return "号角大招点数"; case "Small": if (!isChinese) { return "Small"; } return "小"; case "Medium": if (!isChinese) { return "Medium"; } return "中"; case "Large": if (!isChinese) { return "Large"; } return "大"; case "ExtraLarge": if (!isChinese) { return "Extra Large"; } return "超大"; case "TopLeft": if (!isChinese) { return "Top Left"; } return "左上"; case "Top": if (!isChinese) { return "Top"; } return "上方"; case "TopRight": if (!isChinese) { return "Top Right"; } return "右上"; case "Left": if (!isChinese) { return "Left"; } return "左侧"; case "Right": if (!isChinese) { return "Right"; } return "右侧"; case "BottomLeft": if (!isChinese) { return "Bottom Left"; } return "左下"; case "Bottom": if (!isChinese) { return "Bottom"; } return "下方"; case "BottomRight": if (!isChinese) { return "Bottom Right"; } return "右下"; case "FlashlightDrainMultiplier": if (!isChinese) { return "Flashlight Drain"; } return "手电筒消耗倍率"; case "CompanionDrainMultiplier": if (!isChinese) { return "Companion Drain"; } return "结伴消耗倍率"; case "SoloDrainMultiplier": if (!isChinese) { return "Solo Drain"; } return "独行消耗倍率"; case "ProximityGracePeriod": if (!isChinese) { return "Grace Period"; } return "状态生效时间"; case "ActivePreset": if (!isChinese) { return "Active Preset"; } return "配置预设"; case "Custom": if (!isChinese) { return "Custom"; } return "自定义"; case "Casual": if (!isChinese) { return "Casual"; } return "休闲"; case "Balanced": if (!isChinese) { return "Balanced"; } return "平衡"; case "Hardcore": if (!isChinese) { return "Hardcore"; } return "硬核"; case "Off": if (!isChinese) { return "Off"; } return "关闭"; case "Quarter": if (!isChinese) { return "1/4"; } return "1/4"; case "Half": if (!isChinese) { return "1/2"; } return "1/2"; case "ThreeQuarters": if (!isChinese) { return "3/4"; } return "3/4"; default: return null; } } private static void SetDescription(ConfigEntryBase entry, string text) { if (((entry != null) ? entry.Description : null) != null && !(_descriptionBackingField == null)) { _descriptionBackingField.SetValue(entry.Description, text); } } } internal static class ModIntegration { private static Component _cachedSyncHelper; private static int _cachedSyncHelperItemId = -1; public static bool IsBprLoaded { get; private set; } public static bool IsSzLoaded { get; private set; } public static bool IsNightColdReflectionReady { get { if (ReflectionCache.SzNightColdProp != null) { return ReflectionCache.SzConfigValueGetter != null; } return false; } } public static void Initialize() { IsBprLoaded = Chainloader.PluginInfos.ContainsKey("HnskNoah.BlackPeakRemix"); IsSzLoaded = Chainloader.PluginInfos.ContainsKey("com.github.Thanks.ShootZombies"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ModIntegration] Initialized: BPR={IsBprLoaded}, SZ={IsSzLoaded}"); } } public static bool IsFlashlightActive(Item lanternItem) { if (!IsBprLoaded || (Object)(object)lanternItem == (Object)null) { return false; } if (ReflectionCache.BprSyncHelperType == null || ReflectionCache.BprSyncHelperModeField == null) { return false; } int instanceID = ((Object)lanternItem).GetInstanceID(); if (instanceID != _cachedSyncHelperItemId) { _cachedSyncHelper = ((Component)lanternItem).GetComponent(ReflectionCache.BprSyncHelperType); _cachedSyncHelperItemId = instanceID; } if ((Object)(object)_cachedSyncHelper == (Object)null) { return false; } try { object value = ReflectionCache.BprSyncHelperModeField.GetValue(_cachedSyncHelper); if (value == null) { return false; } return (byte)Convert.ChangeType(value, typeof(byte)) == 1; } catch { return false; } } public static Component GetSyncHelper(Item item) { if (!IsBprLoaded || (Object)(object)item == (Object)null || ReflectionCache.BprSyncHelperType == null) { return null; } int instanceID = ((Object)item).GetInstanceID(); if (instanceID != _cachedSyncHelperItemId) { _cachedSyncHelper = ((Component)item).GetComponent(ReflectionCache.BprSyncHelperType); _cachedSyncHelperItemId = instanceID; } return _cachedSyncHelper; } public static void ClearSyncHelperCache() { _cachedSyncHelper = null; _cachedSyncHelperItemId = -1; } public static float GetBprDarknessWeight() { if (!IsBprLoaded || ReflectionCache.BprCurrentWeightProp == null) { return 0f; } try { return (float)ReflectionCache.BprCurrentWeightProp.GetValue(null); } catch { return 0f; } } public static bool IsBprExtremeDark() { if (!IsBprLoaded || ReflectionCache.BprIsExtremeDarkProp == null) { return false; } try { return (bool)ReflectionCache.BprIsExtremeDarkProp.GetValue(null); } catch { return false; } } public static bool IsBprDark() { if (!IsBprLoaded) { return false; } if (GetBprDarknessWeight() > 0f) { return true; } return IsBprExtremeDark(); } public static bool IsSzNightColdEnabled() { if (ReflectionCache.SzNightColdProp == null || ReflectionCache.SzConfigValueGetter == null) { return true; } try { object value = ReflectionCache.SzNightColdProp.GetValue(null); if (value == null) { return true; } return (bool)ReflectionCache.SzConfigValueGetter.Invoke(value, null); } catch { return true; } } } internal static class NightColdWarning { private static bool _wasDaytime = true; private static float _showTimer; private static bool _shownThisNight; private static GameObject _noticeObj; private static TextMeshProUGUI _noticeText; private const float ShowDuration = 15f; public static void Tick() { bool isDaytime = DayNightTracker.IsDaytime; if (_showTimer > 0f) { _showTimer -= Time.deltaTime; if (_showTimer <= 0f) { HideNotice(); } } if (_wasDaytime && !isDaytime) { _shownThisNight = false; TryShowWarning(); } if (!_wasDaytime && isDaytime) { _shownThisNight = false; HideNotice(); } _wasDaytime = isDaytime; } private static void TryShowWarning() { if (_shownThisNight) { return; } bool flag = IsShootZombiesNightColdOff(); bool flag2 = Ascents.currentAscent < 5 && !IsCustomRunColdNightOn(); if (flag || flag2) { _shownThisNight = true; ShowNotice(flag, flag2); _showTimer = 15f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [NightColdWarning] Warning shown: SZ_NightColdOff={flag}, LowAscent={flag2}(ascent={Ascents.currentAscent})"); } } } private static bool IsShootZombiesNightColdOff() { if (!ModIntegration.IsNightColdReflectionReady) { return false; } return !ModIntegration.IsSzNightColdEnabled(); } private static bool IsCustomRunColdNightOn() { try { if (!RunSettings.IsCustomRun) { return false; } return RunSettings.GetValue((SETTINGTYPE)500, false) == 1; } catch { return false; } } private static void ShowNotice(bool szOff, bool lowAscent) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) HideNotice(); GUIManager instance = GUIManager.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.hudCanvas == (Object)null)) { _noticeObj = new GameObject("NightColdWarning"); _noticeObj.transform.SetParent(((Component)instance.hudCanvas).transform, false); RectTransform obj = _noticeObj.AddComponent(); obj.anchorMin = new Vector2(0.5f, 1f); obj.anchorMax = new Vector2(0.5f, 1f); obj.pivot = new Vector2(0.5f, 1f); obj.anchoredPosition = new Vector2(0f, -60f); obj.sizeDelta = new Vector2(800f, 100f); GameObject val = new GameObject("BG"); val.transform.SetParent(_noticeObj.transform, false); RectTransform obj2 = val.AddComponent(); obj2.anchorMin = Vector2.zero; obj2.anchorMax = Vector2.one; obj2.offsetMin = new Vector2(-10f, -5f); obj2.offsetMax = new Vector2(10f, 5f); Image obj3 = val.AddComponent(); ((Graphic)obj3).color = new Color(0f, 0f, 0f, 0.7f); ((Graphic)obj3).raycastTarget = false; _noticeText = _noticeObj.AddComponent(); TMP_FontAsset val2 = ResolveFont(); if ((Object)(object)val2 != (Object)null) { ((TMP_Text)_noticeText).font = val2; } ((TMP_Text)_noticeText).fontSize = 20f; ((Graphic)_noticeText).color = new Color(1f, 0.35f, 0.3f, 1f); ((TMP_Text)_noticeText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_noticeText).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)_noticeText).overflowMode = (TextOverflowModes)0; ((Graphic)_noticeText).raycastTarget = false; ((TMP_Text)_noticeText).text = BuildMessage(szOff, lowAscent); } } private static string BuildMessage(bool szOff, bool lowAscent) { bool isChinese = LanguageHelper.IsChinese; string text = ""; if (szOff) { text += (isChinese ? "⚠ 「夜晚寒冷 / Night Cold」已关闭,灯笼抗寒无效\n→ 请在 ModConfig → Features 中开启(Thanks-FogAndColdControl 或 ShootZombies)" : "⚠ 'Night Cold' is OFF — cold resistance disabled\n→ Enable in ModConfig → Features (Thanks-FogAndColdControl or ShootZombies)"); } if (lowAscent) { if (text.Length > 0) { text += "\n"; } int currentAscent = Ascents.currentAscent; text += (isChinese ? $"⚠ 当前天阶 {currentAscent}(需天阶5+)无夜间寒冷,抗寒功能不生效" : $"⚠ Ascent {currentAscent} has no night cold (requires 5+), cold resistance inactive"); } return text; } private static void HideNotice() { if ((Object)(object)_noticeObj != (Object)null) { Object.Destroy((Object)(object)_noticeObj); _noticeObj = null; _noticeText = null; } } private static TMP_FontAsset ResolveFont() { PlayerConnectionLog val = Object.FindAnyObjectByType(); if ((Object)(object)val != (Object)null && (Object)(object)val.text != (Object)null) { return ((TMP_Text)val.text).font; } GUIManager instance = GUIManager.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.interactNameText != (Object)null) { return ((TMP_Text)instance.interactNameText).font; } return null; } } internal static class PresetManager { private static bool _applying; public static bool IsApplying => _applying; public static void ApplyPreset(ConfigPreset preset) { if (preset == ConfigPreset.Custom) { return; } _applying = true; try { switch (preset) { case ConfigPreset.Casual: ApplyCasual(); break; case ConfigPreset.Balanced: ApplyBalanced(); break; case ConfigPreset.Hardcore: ApplyHardcore(); break; } try { Plugin instance = Plugin.Instance; if (instance != null) { ConfigFile config = ((BaseUnityPlugin)instance).Config; if (config != null) { config.Save(); } } } catch { } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[PresetManager] Applied preset: {preset}"); } } finally { _applying = false; } } private static void ApplyCasual() { Set(Plugin.LanternMaxFuel, LanternFuelOption.Seconds120); Set(Plugin.EnableWarmthReduction, value: true); Set(Plugin.LanternWarmthMultiplier, 0.75f); Set(Plugin.EnableCampfireRefuel, value: true); Set(Plugin.ReserveWarmthMax, ReserveWarmthRatio.Half); Set(Plugin.EnableWarmthRestore, value: true); Set(Plugin.HitRestoreWarmth, 8); Set(Plugin.RestoreRadius, 50); Set(Plugin.HitRestoreCooldown, 0.3f); Set(Plugin.AutoRefillEnabled, value: true); Set(Plugin.AutoRefillCapPercent, 0.5f); Set(Plugin.AutoRefillRate, 1f); Set(Plugin.AutoRefillDaytimeOnly, value: false); Set(Plugin.AutoRefillRequireHold, value: false); Set(Plugin.BugleUltimateEnabled, value: true); Set(Plugin.BugleUltimateCooldown, 180f); Set(Plugin.BugleUltimateRadius, 20f); Set(Plugin.BugleUltimateRestore, -1f); Set(Plugin.PurgeExtraLanterns, value: true); Set(Plugin.StrayBugleCleanupEnabled, value: true); Set(Plugin.StrayBugleDistance, 100f); Set(Plugin.StrayBugleGracePeriod, 60f); Set(Plugin.FlashlightDrainMultiplier, 1.2f); Set(Plugin.CompanionDrainMultiplier, 0.8f); Set(Plugin.SoloDrainMultiplier, 1.2f); Set(Plugin.ProximityGracePeriod, 15); Set(Plugin.EnableUpgradeSystem, value: true); Set(Plugin.UpgradeLevelCostsCsv, "30,60,90,120,150"); Set(Plugin.UpgradeCapacityBonusCsv, "0.15,0.3,0.45,0.6,0.75"); Set(Plugin.UpgradeEfficiencyBonusCsv, "0.1,0.2,0.3,0.4,0.5"); Set(Plugin.UpgradePassiveTickInterval, 30f); Set(Plugin.UpgradePassivePointsPerTick, 1); Set(Plugin.UpgradeHitPoints, 1); Set(Plugin.UpgradeCampfirePoints, 5); Set(Plugin.UpgradeBuglePoints, 3); Set(Plugin.EnableFuelIndicator3D, value: true); Set(Plugin.MuteZombieTornado, value: false); } private static void ApplyBalanced() { Set(Plugin.LanternMaxFuel, LanternFuelOption.Seconds120); Set(Plugin.EnableWarmthReduction, value: true); Set(Plugin.LanternWarmthMultiplier, 0.6f); Set(Plugin.EnableCampfireRefuel, value: true); Set(Plugin.ReserveWarmthMax, ReserveWarmthRatio.Half); Set(Plugin.EnableWarmthRestore, value: true); Set(Plugin.HitRestoreWarmth, 6); Set(Plugin.RestoreRadius, 45); Set(Plugin.HitRestoreCooldown, 0.4f); Set(Plugin.AutoRefillEnabled, value: true); Set(Plugin.AutoRefillCapPercent, 0.45f); Set(Plugin.AutoRefillRate, 0.7f); Set(Plugin.AutoRefillDaytimeOnly, value: false); Set(Plugin.AutoRefillRequireHold, value: true); Set(Plugin.BugleUltimateEnabled, value: true); Set(Plugin.BugleUltimateCooldown, 220f); Set(Plugin.BugleUltimateRadius, 18f); Set(Plugin.BugleUltimateRestore, -1f); Set(Plugin.PurgeExtraLanterns, value: true); Set(Plugin.StrayBugleCleanupEnabled, value: true); Set(Plugin.StrayBugleDistance, 90f); Set(Plugin.StrayBugleGracePeriod, 55f); Set(Plugin.FlashlightDrainMultiplier, 1.4f); Set(Plugin.CompanionDrainMultiplier, 0.85f); Set(Plugin.SoloDrainMultiplier, 1.3f); Set(Plugin.ProximityGracePeriod, 25); Set(Plugin.EnableUpgradeSystem, value: true); Set(Plugin.UpgradeLevelCostsCsv, "45,90,140,200,260"); Set(Plugin.UpgradeCapacityBonusCsv, "0.18,0.36,0.54,0.72,0.9"); Set(Plugin.UpgradeEfficiencyBonusCsv, "0.1,0.2,0.3,0.4,0.5"); Set(Plugin.UpgradePassiveTickInterval, 45f); Set(Plugin.UpgradePassivePointsPerTick, 1); Set(Plugin.UpgradeHitPoints, 1); Set(Plugin.UpgradeCampfirePoints, 5); Set(Plugin.UpgradeBuglePoints, 3); Set(Plugin.EnableFuelIndicator3D, value: true); Set(Plugin.MuteZombieTornado, value: false); } private static void ApplyHardcore() { Set(Plugin.LanternMaxFuel, LanternFuelOption.Seconds90); Set(Plugin.EnableWarmthReduction, value: true); Set(Plugin.LanternWarmthMultiplier, 0.4f); Set(Plugin.EnableCampfireRefuel, value: true); Set(Plugin.ReserveWarmthMax, ReserveWarmthRatio.Quarter); Set(Plugin.EnableWarmthRestore, value: true); Set(Plugin.HitRestoreWarmth, 4); Set(Plugin.RestoreRadius, 35); Set(Plugin.HitRestoreCooldown, 0.5f); Set(Plugin.AutoRefillEnabled, value: true); Set(Plugin.AutoRefillCapPercent, 0.35f); Set(Plugin.AutoRefillRate, 0.4f); Set(Plugin.AutoRefillDaytimeOnly, value: true); Set(Plugin.AutoRefillRequireHold, value: true); Set(Plugin.BugleUltimateEnabled, value: true); Set(Plugin.BugleUltimateCooldown, 280f); Set(Plugin.BugleUltimateRadius, 15f); Set(Plugin.BugleUltimateRestore, -1f); Set(Plugin.PurgeExtraLanterns, value: true); Set(Plugin.StrayBugleCleanupEnabled, value: true); Set(Plugin.StrayBugleDistance, 80f); Set(Plugin.StrayBugleGracePeriod, 45f); Set(Plugin.FlashlightDrainMultiplier, 1.7f); Set(Plugin.CompanionDrainMultiplier, 0.9f); Set(Plugin.SoloDrainMultiplier, 1.4f); Set(Plugin.ProximityGracePeriod, 40); Set(Plugin.EnableUpgradeSystem, value: true); Set(Plugin.UpgradeLevelCostsCsv, "60,120,190,270,360"); Set(Plugin.UpgradeCapacityBonusCsv, "0.2,0.4,0.6,0.8,1.0"); Set(Plugin.UpgradeEfficiencyBonusCsv, "0.08,0.16,0.24,0.32,0.4"); Set(Plugin.UpgradePassiveTickInterval, 60f); Set(Plugin.UpgradePassivePointsPerTick, 1); Set(Plugin.UpgradeHitPoints, 1); Set(Plugin.UpgradeCampfirePoints, 4); Set(Plugin.UpgradeBuglePoints, 2); Set(Plugin.EnableFuelIndicator3D, value: true); Set(Plugin.MuteZombieTornado, value: false); } private static void Set(ConfigEntry entry, T value) { if (entry != null) { entry.Value = value; } } } internal static class ProximityDrainMonitor { private const string CompanionSourceKey = "companion"; private const string SoloSourceKey = "solo"; private const float CheckInterval = 1f; private const int MaxCompanionScale = 3; private static bool _hasCompanion; private static float _stateTimer; private static bool _companionActive; private static bool _soloActive; private static int _companionCount; private static float _lastCheckTime = -999f; public static void Tick() { if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || PhotonNetwork.CurrentRoom.PlayerCount <= 1) { if (_companionActive || _soloActive) { LanternHelper.RemoveDrainSource("companion"); LanternHelper.RemoveDrainSource("solo"); _companionActive = false; _soloActive = false; _hasCompanion = false; _stateTimer = 0f; _companionCount = 0; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [ProximityDrain] Solo/offline room → disabled"); } } return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } _stateTimer += Time.deltaTime; if (Time.time - _lastCheckTime >= 1f) { _lastCheckTime = Time.time; float radius = Plugin.RestoreRadius.Value; int num = CountNearbyPlayers(localCharacter, radius); bool flag = num > 0; if (flag != _hasCompanion) { _hasCompanion = flag; _companionCount = num; _stateTimer = 0f; _companionActive = false; _soloActive = false; LanternHelper.RemoveDrainSource("companion"); LanternHelper.RemoveDrainSource("solo"); float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)string.Format("[DEBUG] [ProximityDrain] State → {0}, timer reset, finalDrain={1:F2}x", flag ? $"COMPANION(count={num})" : "SOLO", fuelDrainMultiplier)); } return; } _companionCount = num; if (_companionActive && _hasCompanion) { float value = Plugin.CompanionDrainMultiplier.Value; float num2 = Mathf.Clamp01((float)_companionCount / 3f); float multiplier = Mathf.Lerp(1f, value, num2); LanternHelper.SetDrainSource("companion", multiplier); } } TryActivate(); } private static void TryActivate() { float num = Plugin.ProximityGracePeriod.Value; if (_stateTimer < num) { return; } if (_hasCompanion && !_companionActive) { float value = Plugin.CompanionDrainMultiplier.Value; float num2 = Mathf.Clamp01((float)_companionCount / 3f); float num3 = Mathf.Lerp(1f, value, num2); LanternHelper.SetDrainSource("companion", num3); LanternHelper.RemoveDrainSource("solo"); _companionActive = true; _soloActive = false; float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [ProximityDrain] Companion ACTIVATED: count={_companionCount}, config={value:F2}x, applied={num3:F2}x (finalDrain={fuelDrainMultiplier:F2}x)"); } } else if (!_hasCompanion && !_soloActive) { float value2 = Plugin.SoloDrainMultiplier.Value; LanternHelper.SetDrainSource("solo", value2); LanternHelper.RemoveDrainSource("companion"); _soloActive = true; _companionActive = false; float fuelDrainMultiplier2 = LanternHelper.FuelDrainMultiplier; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [ProximityDrain] Solo ACTIVATED: config={value2:F2}x (finalDrain={fuelDrainMultiplier2:F2}x)"); } } } private static int CountNearbyPlayers(Character local, float radius) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) int num = 0; Vector3 center = local.Center; foreach (Character allCharacter in Character.AllCharacters) { if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)local) && (!((Object)(object)allCharacter.data != (Object)null) || !allCharacter.data.dead) && Vector3.Distance(center, allCharacter.Center) <= radius) { num++; } } return num; } } internal static class ReflectionCache { public static FieldRef LanternLit; public static FieldRef LanternFuel; public static FieldInfo LanternLitField; public static FieldInfo LanternFuelField; public static Type BprSyncHelperType; public static FieldInfo BprSyncHelperModeField; public static MethodInfo BprSyncHelperInitMethod; public static object BprStateOmni; public static object BprStateFlashlight; public static FieldInfo BprLogField; public static PropertyInfo BprCurrentWeightProp; public static PropertyInfo BprIsExtremeDarkProp; public static PropertyInfo SzNightColdProp; public static MethodInfo SzConfigValueGetter; public static FieldInfo LocalizedTextLanguageField; private static bool _gameReflectionDone; public static void InitGameReflectionEarly() { if (!_gameReflectionDone) { InitGameReflection(Plugin.Log); } } public static void Initialize(bool bprLoaded, bool szLoaded) { ManualLogSource log = Plugin.Log; InitLanternFields(log); if (bprLoaded) { InitBprReflection(log); } InitSzReflection(log); InitGameReflection(log); if (log != null) { log.LogInfo((object)"[ReflectionCache] Initialization complete"); } } private static void InitLanternFields(ManualLogSource log) { try { LanternLit = AccessTools.FieldRefAccess("lit"); if (log != null) { log.LogInfo((object)"[ReflectionCache] Lantern.lit FieldRef OK"); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[ReflectionCache] Lantern.lit FieldRef FAILED: " + ex.Message)); } } try { LanternFuel = AccessTools.FieldRefAccess("fuel"); if (log != null) { log.LogInfo((object)"[ReflectionCache] Lantern.fuel FieldRef OK"); } } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("[ReflectionCache] Lantern.fuel FieldRef FAILED: " + ex2.Message)); } } LanternLitField = typeof(Lantern).GetField("lit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); LanternFuelField = typeof(Lantern).GetField("fuel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static void InitBprReflection(ManualLogSource log) { try { Assembly assembly = typeof(BlackPeakRemix).Assembly; BprSyncHelperType = assembly.GetType("BlackPeakRemix.LanternSyncHelper"); if (BprSyncHelperType != null) { BprSyncHelperModeField = BprSyncHelperType.GetField("localInstanceMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); BprSyncHelperInitMethod = BprSyncHelperType.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } Type type = assembly.GetType("BlackPeakRemix.LanternState"); if (type != null) { BprStateOmni = Enum.Parse(type, "Omni"); BprStateFlashlight = Enum.Parse(type, "Flashlight"); } BprLogField = typeof(BlackPeakRemix).GetField("Log", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); Type type2 = assembly.GetType("BlackPeakRemix.BPR_EnvironmentController"); if (type2 != null) { BprCurrentWeightProp = type2.GetProperty("CurrentWeight", BindingFlags.Static | BindingFlags.Public); BprIsExtremeDarkProp = type2.GetProperty("IsExtremeDarkMode", BindingFlags.Static | BindingFlags.Public); } if (log != null) { log.LogInfo((object)($"[ReflectionCache] BPR: syncHelper={BprSyncHelperType != null}, " + $"mode={BprSyncHelperModeField != null}, init={BprSyncHelperInitMethod != null}, " + $"states={BprStateOmni != null}/{BprStateFlashlight != null}, " + $"env.weight={BprCurrentWeightProp != null}, env.dark={BprIsExtremeDarkProp != null}")); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[ReflectionCache] BPR reflection failed: " + ex.Message)); } } } private static void InitSzReflection(ManualLogSource log) { if (!TryLoadNightColdFromPlugin("com.github.Thanks.ShootZombies", "SZ", log)) { TryLoadNightColdFromPlugin("com.github.Thanks.FogClimb", "FogClimb", log); } } private static bool TryLoadNightColdFromPlugin(string guid, string tag, ManualLogSource log) { try { if (!Chainloader.PluginInfos.TryGetValue(guid, out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { if (log != null) { log.LogInfo((object)("[ReflectionCache] " + tag + ": plugin '" + guid + "' not loaded")); } return false; } Type type = ((object)value.Instance).GetType(); PropertyInfo property = type.GetProperty("NightColdEnabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { if (log != null) { log.LogInfo((object)("[ReflectionCache] " + tag + ": NightColdEnabled property not found on " + type.Name)); } return false; } MethodInfo methodInfo = property.PropertyType.GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)?.GetGetMethod(); if (methodInfo == null) { if (log != null) { log.LogWarning((object)("[ReflectionCache] " + tag + ": NightColdEnabled found but Value getter missing")); } return false; } SzNightColdProp = property; SzConfigValueGetter = methodInfo; if (log != null) { log.LogInfo((object)("[ReflectionCache] " + tag + ": NightColdEnabled reflection OK (source=" + tag + ")")); } return true; } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[ReflectionCache] " + tag + ": reflection failed: " + ex.Message)); } return false; } } private static void InitGameReflection(ManualLogSource log) { if (_gameReflectionDone) { return; } _gameReflectionDone = true; try { Type type = typeof(Item).Assembly.GetType("LocalizedText"); if (type != null) { LocalizedTextLanguageField = type.GetField("CURRENT_LANGUAGE", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (log != null) { log.LogInfo((object)($"[ReflectionCache] Game: LocalizedText={type != null}, " + $"langField={LocalizedTextLanguageField != null}")); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[ReflectionCache] Game reflection failed: " + ex.Message)); } } } public static bool GetLit(Lantern instance) { if ((Object)(object)instance == (Object)null) { return false; } if (LanternLit != null) { return LanternLit.Invoke(instance); } if (LanternLitField != null) { return (bool)(LanternLitField.GetValue(instance) ?? ((object)false)); } return false; } public static void SetLit(Lantern instance, bool value) { if (!((Object)(object)instance == (Object)null)) { if (LanternLit != null) { LanternLit.Invoke(instance) = value; } else { LanternLitField?.SetValue(instance, value); } } } public static float GetFuel(Lantern instance) { if ((Object)(object)instance == (Object)null) { return 0f; } if (LanternFuel != null) { return LanternFuel.Invoke(instance); } if (LanternFuelField != null) { return (float)(LanternFuelField.GetValue(instance) ?? ((object)0f)); } return 0f; } public static void SetFuel(Lantern instance, float value) { if (!((Object)(object)instance == (Object)null)) { if (LanternFuel != null) { LanternFuel.Invoke(instance) = value; } else { LanternFuelField?.SetValue(instance, value); } } } } internal static class RestoreTracker { private static string _lastSource; private static float _lastWarmth; private static float _lastTime; private static float _lastAutoRefillTime = -999f; public static string LastSource => _lastSource; public static float LastWarmth => _lastWarmth; public static float LastTime => _lastTime; public static float LastAutoRefillTime => _lastAutoRefillTime; public static void ReportLast(string sourceKey, float warmth) { _lastSource = sourceKey; _lastWarmth = warmth; _lastTime = Time.time; } public static void ReportAutoRefillActive() { _lastAutoRefillTime = Time.time; } public static string CleanItemName(string raw) { if (string.IsNullOrEmpty(raw)) { return raw; } int num = raw.IndexOf("(Clone)"); if (num < 0) { return raw.Trim(); } return raw.Substring(0, num).Trim(); } } internal static class RoomConfigSync { private sealed class CallbackProxy : IInRoomCallbacks { public void OnRoomPropertiesUpdate(Hashtable changed) { if (changed != null && ((Dictionary)(object)changed).ContainsKey((object)"LSN.HostConfig") && HasOnlineRoom() && !PhotonNetwork.IsMasterClient) { _lastPollTime = Time.unscaledTime; ApplyHostConfigIfNeeded(); } } public void OnMasterClientSwitched(Player newMaster) { string arg = ((newMaster != null) ? newMaster.NickName : null) ?? "unknown"; int num = ((newMaster != null) ? newMaster.ActorNumber : (-1)); bool isMasterClient = PhotonNetwork.IsMasterClient; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RoomConfigSync] OnMasterClientSwitched: new master='{arg}' (actor={num}), iAmMaster={isMasterClient}"); } _lastPollTime = -10f; if (isMasterClient) { RestoreBackup(); MarkDirty(immediate: true); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[RoomConfigSync] Became master: restored local backup, will republish config"); } } else { CaptureBackup(); ApplyHostConfigIfNeeded(); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[RoomConfigSync] Became client: captured local backup, applying new host config"); } } } public void OnPlayerEnteredRoom(Player newPlayer) { } public void OnPlayerLeftRoom(Player otherPlayer) { } public void OnPlayerPropertiesUpdate(Player target, Hashtable changed) { } } private const string PropertyKey = "LSN.HostConfig"; private const string VersionKey = "LSN.CfgVer"; private const float PublishInterval = 5f; private const float PollInterval = 5f; private static bool _applyingPayload; private static string _activeRoomName = string.Empty; private static string _lastPublishedPayload = string.Empty; private static string _lastPublishedHash = string.Empty; private static string _cachedPayload = string.Empty; private static string _cachedPayloadHash = string.Empty; private static string _lastAppliedPayload = string.Empty; private static string _localBackupPayload = string.Empty; private static bool _hasLocalBackup; private static bool _wasMasterClient; private static float _lastPublishTime = -10f; private static float _lastPollTime = -10f; private static bool _configDirty = true; private static bool _callbacksRegistered; private static CallbackProxy _callbackProxy; private static float _lastImmediatePublishTime = -10f; public static bool IsApplyingPayload => _applyingPayload; public static void Initialize(Plugin plugin) { try { if (!_callbacksRegistered) { if (_callbackProxy == null) { _callbackProxy = new CallbackProxy(); } PhotonNetwork.AddCallbackTarget((object)_callbackProxy); _callbacksRegistered = true; } SubscribeConfigChanges(plugin); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RoomConfigSync] Initialized (publishInterval={5f}s, pollInterval={5f}s)"); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[RoomConfigSync] Initialize failed: " + ex.Message)); } } } public static void UpdateSync() { try { if (!HasOnlineRoom()) { RestoreBackup(); ResetState(full: true); return; } string text = PhotonNetwork.CurrentRoom.Name ?? string.Empty; bool flag = false; if (!string.Equals(_activeRoomName, text, StringComparison.Ordinal)) { RestoreBackup(); ResetState(full: false); _activeRoomName = text; flag = true; } if (PhotonNetwork.IsMasterClient) { if (!_wasMasterClient) { RestoreBackup(); MarkDirty(immediate: true); } if ((_configDirty || string.IsNullOrEmpty(_lastPublishedPayload)) && Time.unscaledTime - _lastPublishTime >= 5f) { PublishHostConfig(string.IsNullOrEmpty(_lastPublishedPayload)); } } else { CaptureBackup(); if (flag || string.IsNullOrEmpty(_lastAppliedPayload)) { ApplyHostConfigIfNeeded(); } if (Time.unscaledTime - _lastPollTime >= 5f) { _lastPollTime = Time.unscaledTime; ApplyHostConfigIfNeeded(); } } _wasMasterClient = PhotonNetwork.IsMasterClient; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RoomConfigSync] UpdateSync error: " + ex.Message)); } } } public static void Cleanup() { try { RestoreBackup(); if (_callbacksRegistered && _callbackProxy != null) { PhotonNetwork.RemoveCallbackTarget((object)_callbackProxy); _callbacksRegistered = false; } UnsubscribeConfigChanges(); } catch { } } private static void PublishHostConfig(bool force) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_007b: Expected O, but got Unknown if (_applyingPayload || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null) { return; } string orBuildPayload = GetOrBuildPayload(); string cachedPayloadHash = _cachedPayloadHash; if (string.IsNullOrEmpty(orBuildPayload)) { _configDirty = false; _lastPublishTime = Time.unscaledTime; return; } if (!force && string.Equals(cachedPayloadHash, _lastPublishedHash, StringComparison.Ordinal)) { _configDirty = false; _lastPublishTime = Time.unscaledTime; return; } Hashtable val = new Hashtable(); ((Dictionary)val).Add((object)"LSN.HostConfig", (object)orBuildPayload); ((Dictionary)val).Add((object)"LSN.CfgVer", (object)cachedPayloadHash); Hashtable val2 = val; PhotonNetwork.CurrentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null); _lastPublishedPayload = orBuildPayload; _lastPublishedHash = cachedPayloadHash; _lastAppliedPayload = orBuildPayload; _configDirty = false; _lastPublishTime = Time.unscaledTime; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RoomConfigSync] Host published config ({orBuildPayload.Length} chars, hash={cachedPayloadHash})"); } } private static void ApplyHostConfigIfNeeded() { if (_applyingPayload || PhotonNetwork.IsMasterClient || (TryGetRoomProperty("LSN.CfgVer", out var value) && !string.IsNullOrEmpty(value) && string.Equals(value, _lastPublishedHash, StringComparison.Ordinal) && !string.IsNullOrEmpty(_lastAppliedPayload)) || !TryGetRoomProperty("LSN.HostConfig", out var value2) || string.IsNullOrEmpty(value2) || string.Equals(value2, _lastAppliedPayload, StringComparison.Ordinal)) { return; } ApplyPayload(value2); _lastAppliedPayload = value2; _lastPublishedHash = value ?? ""; int num = 0; string text = value2; for (int i = 0; i < text.Length; i++) { if (text[i] == '|') { num++; } } num++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[RoomConfigSync] Applied host config: {0} fields, {1} chars, hash={2}", num, value2.Length, value ?? "")); } } private static bool TryGetRoomProperty(string key, out string value) { value = null; Room currentRoom = PhotonNetwork.CurrentRoom; if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) == null) { return false; } value = ((RoomInfo)currentRoom).CustomProperties[(object)key] as string; return !string.IsNullOrEmpty(value); } private static void CaptureBackup() { if (_hasLocalBackup) { return; } _localBackupPayload = BuildPayload(); _hasLocalBackup = !string.IsNullOrEmpty(_localBackupPayload); if (_hasLocalBackup) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[RoomConfigSync] Local config backup captured"); } } } private static void RestoreBackup() { if (_hasLocalBackup && !string.IsNullOrEmpty(_localBackupPayload)) { ApplyPayload(_localBackupPayload); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[RoomConfigSync] Local config restored from backup"); } } _localBackupPayload = string.Empty; _hasLocalBackup = false; } private static string GetOrBuildPayload() { if (_configDirty || string.IsNullOrEmpty(_cachedPayload)) { _cachedPayload = BuildPayload(); _cachedPayloadHash = ComputeHash(_cachedPayload); } return _cachedPayload; } private static string BuildPayload() { StringBuilder stringBuilder = new StringBuilder(256); Append(stringBuilder, "LanternMaxFuel", Plugin.LanternMaxFuel?.Value.ToString()); AppendBool(stringBuilder, "EnableWarmthReduction", Plugin.EnableWarmthReduction); AppendFloat(stringBuilder, "LanternWarmthMultiplier", Plugin.LanternWarmthMultiplier); AppendBool(stringBuilder, "EnableWarmthRestore", Plugin.EnableWarmthRestore); AppendInt(stringBuilder, "HitRestoreWarmth", Plugin.HitRestoreWarmth); AppendInt(stringBuilder, "RestoreRadius", Plugin.RestoreRadius); AppendFloat(stringBuilder, "HitRestoreCooldown", Plugin.HitRestoreCooldown); Append(stringBuilder, "ReserveWarmthMax", Plugin.ReserveWarmthMax?.Value.ToString()); AppendBool(stringBuilder, "AutoRefillEnabled", Plugin.AutoRefillEnabled); AppendFloat(stringBuilder, "AutoRefillCapPercent", Plugin.AutoRefillCapPercent); AppendFloat(stringBuilder, "AutoRefillRate", Plugin.AutoRefillRate); AppendBool(stringBuilder, "AutoRefillDaytimeOnly", Plugin.AutoRefillDaytimeOnly); AppendBool(stringBuilder, "AutoRefillRequireHold", Plugin.AutoRefillRequireHold); AppendBool(stringBuilder, "BugleUltimateEnabled", Plugin.BugleUltimateEnabled); AppendFloat(stringBuilder, "BugleUltimateCooldown", Plugin.BugleUltimateCooldown); AppendFloat(stringBuilder, "BugleUltimateRadius", Plugin.BugleUltimateRadius); AppendFloat(stringBuilder, "BugleUltimateRestore", Plugin.BugleUltimateRestore); AppendBool(stringBuilder, "PurgeExtraLanterns", Plugin.PurgeExtraLanterns); AppendBool(stringBuilder, "StrayBugleCleanupEnabled", Plugin.StrayBugleCleanupEnabled); AppendFloat(stringBuilder, "StrayBugleDistance", Plugin.StrayBugleDistance); AppendFloat(stringBuilder, "StrayBugleGracePeriod", Plugin.StrayBugleGracePeriod); AppendBool(stringBuilder, "EnableCampfireRefuel", Plugin.EnableCampfireRefuel); AppendFloat(stringBuilder, "FlashlightDrainMultiplier", Plugin.FlashlightDrainMultiplier); AppendFloat(stringBuilder, "CompanionDrainMultiplier", Plugin.CompanionDrainMultiplier); AppendFloat(stringBuilder, "SoloDrainMultiplier", Plugin.SoloDrainMultiplier); AppendInt(stringBuilder, "ProximityGracePeriod", Plugin.ProximityGracePeriod); AppendBool(stringBuilder, "EnableUpgradeSystem", Plugin.EnableUpgradeSystem); AppendString(stringBuilder, "UpgradeLevelCostsCsv", Plugin.UpgradeLevelCostsCsv); AppendString(stringBuilder, "UpgradeCapacityBonusCsv", Plugin.UpgradeCapacityBonusCsv); AppendString(stringBuilder, "UpgradeEfficiencyBonusCsv", Plugin.UpgradeEfficiencyBonusCsv); AppendFloat(stringBuilder, "UpgradePassiveTickInterval", Plugin.UpgradePassiveTickInterval); AppendInt(stringBuilder, "UpgradePassivePointsPerTick", Plugin.UpgradePassivePointsPerTick); AppendInt(stringBuilder, "UpgradeHitPoints", Plugin.UpgradeHitPoints); AppendInt(stringBuilder, "UpgradeCampfirePoints", Plugin.UpgradeCampfirePoints); AppendInt(stringBuilder, "UpgradeBuglePoints", Plugin.UpgradeBuglePoints); return stringBuilder.ToString(); } private static string ComputeHash(string payload) { if (string.IsNullOrEmpty(payload)) { return string.Empty; } using MD5 mD = MD5.Create(); return BitConverter.ToString(mD.ComputeHash(Encoding.UTF8.GetBytes(payload)), 0, 4).Replace("-", ""); } private static void ApplyPayload(string payload) { if (string.IsNullOrEmpty(payload)) { return; } Dictionary dictionary = ParsePayload(payload); if (dictionary.Count == 0) { return; } _applyingPayload = true; UnsubscribeConfigChanges(); try { ApplyEnum(dictionary, "LanternMaxFuel", Plugin.LanternMaxFuel); ApplyBool(dictionary, "EnableWarmthReduction", Plugin.EnableWarmthReduction); ApplyFloat(dictionary, "LanternWarmthMultiplier", Plugin.LanternWarmthMultiplier); ApplyBool(dictionary, "EnableWarmthRestore", Plugin.EnableWarmthRestore); ApplyInt(dictionary, "HitRestoreWarmth", Plugin.HitRestoreWarmth); ApplyInt(dictionary, "RestoreRadius", Plugin.RestoreRadius); ApplyFloat(dictionary, "HitRestoreCooldown", Plugin.HitRestoreCooldown); ApplyEnum(dictionary, "ReserveWarmthMax", Plugin.ReserveWarmthMax); ApplyBool(dictionary, "AutoRefillEnabled", Plugin.AutoRefillEnabled); ApplyFloat(dictionary, "AutoRefillCapPercent", Plugin.AutoRefillCapPercent); ApplyFloat(dictionary, "AutoRefillRate", Plugin.AutoRefillRate); ApplyBool(dictionary, "AutoRefillDaytimeOnly", Plugin.AutoRefillDaytimeOnly); ApplyBool(dictionary, "AutoRefillRequireHold", Plugin.AutoRefillRequireHold); ApplyBool(dictionary, "BugleUltimateEnabled", Plugin.BugleUltimateEnabled); ApplyFloat(dictionary, "BugleUltimateCooldown", Plugin.BugleUltimateCooldown); ApplyFloat(dictionary, "BugleUltimateRadius", Plugin.BugleUltimateRadius); ApplyFloat(dictionary, "BugleUltimateRestore", Plugin.BugleUltimateRestore); ApplyBool(dictionary, "PurgeExtraLanterns", Plugin.PurgeExtraLanterns); ApplyBool(dictionary, "StrayBugleCleanupEnabled", Plugin.StrayBugleCleanupEnabled); ApplyFloat(dictionary, "StrayBugleDistance", Plugin.StrayBugleDistance); ApplyFloat(dictionary, "StrayBugleGracePeriod", Plugin.StrayBugleGracePeriod); ApplyBool(dictionary, "EnableCampfireRefuel", Plugin.EnableCampfireRefuel); ApplyFloat(dictionary, "FlashlightDrainMultiplier", Plugin.FlashlightDrainMultiplier); ApplyFloat(dictionary, "CompanionDrainMultiplier", Plugin.CompanionDrainMultiplier); ApplyFloat(dictionary, "SoloDrainMultiplier", Plugin.SoloDrainMultiplier); ApplyInt(dictionary, "ProximityGracePeriod", Plugin.ProximityGracePeriod); ApplyBool(dictionary, "EnableUpgradeSystem", Plugin.EnableUpgradeSystem); ApplyString(dictionary, "UpgradeLevelCostsCsv", Plugin.UpgradeLevelCostsCsv); ApplyString(dictionary, "UpgradeCapacityBonusCsv", Plugin.UpgradeCapacityBonusCsv); ApplyString(dictionary, "UpgradeEfficiencyBonusCsv", Plugin.UpgradeEfficiencyBonusCsv); ApplyFloat(dictionary, "UpgradePassiveTickInterval", Plugin.UpgradePassiveTickInterval); ApplyInt(dictionary, "UpgradePassivePointsPerTick", Plugin.UpgradePassivePointsPerTick); ApplyInt(dictionary, "UpgradeHitPoints", Plugin.UpgradeHitPoints); ApplyInt(dictionary, "UpgradeCampfirePoints", Plugin.UpgradeCampfirePoints); ApplyInt(dictionary, "UpgradeBuglePoints", Plugin.UpgradeBuglePoints); } finally { _applyingPayload = false; SubscribeConfigChanges(Plugin.Instance); } } private static Dictionary ParsePayload(string payload) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); if (string.IsNullOrEmpty(payload)) { return dictionary; } string[] array = payload.Split(new char[1] { '|' }); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0 && num < text.Length - 1) { dictionary[text.Substring(0, num)] = text.Substring(num + 1); } } return dictionary; } private static void Append(StringBuilder sb, string key, string value) { if (sb.Length > 0) { sb.Append('|'); } sb.Append(key).Append('=').Append(value ?? string.Empty); } private static void AppendBool(StringBuilder sb, string key, ConfigEntry entry) { Append(sb, key, (entry != null && entry.Value) ? "1" : "0"); } private static void AppendFloat(StringBuilder sb, string key, ConfigEntry entry) { Append(sb, key, (entry?.Value ?? 0f).ToString("R", CultureInfo.InvariantCulture)); } private static void AppendInt(StringBuilder sb, string key, ConfigEntry entry) { Append(sb, key, (entry?.Value ?? 0).ToString(CultureInfo.InvariantCulture)); } private static void AppendString(StringBuilder sb, string key, ConfigEntry entry) { string text = entry?.Value ?? string.Empty; text = text.Replace("|", "\u0001").Replace("=", "\u0002"); Append(sb, key, text); } private static void ApplyBool(Dictionary dict, string key, ConfigEntry entry) { if (entry != null && dict.TryGetValue(key, out var value)) { if (value == "1") { entry.Value = true; } else if (value == "0") { entry.Value = false; } } } private static void ApplyFloat(Dictionary dict, string key, ConfigEntry entry) { if (entry != null && dict.TryGetValue(key, out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { entry.Value = result; } } private static void ApplyInt(Dictionary dict, string key, ConfigEntry entry) { if (entry != null && dict.TryGetValue(key, out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { entry.Value = result; } } private static void ApplyString(Dictionary dict, string key, ConfigEntry entry) { if (entry != null && dict.TryGetValue(key, out var value)) { entry.Value = value.Replace("\u0001", "|").Replace("\u0002", "="); } } private static void ApplyEnum(Dictionary dict, string key, ConfigEntry entry) where T : struct { if (entry != null && dict.TryGetValue(key, out var value) && Enum.TryParse(value, out var result)) { entry.Value = result; } } private static void SubscribeConfigChanges(Plugin plugin) { if (((plugin != null) ? ((BaseUnityPlugin)plugin).Config : null) != null) { ((BaseUnityPlugin)plugin).Config.SettingChanged += OnSettingChanged; } } private static void UnsubscribeConfigChanges() { Plugin instance = Plugin.Instance; if (((instance != null) ? ((BaseUnityPlugin)instance).Config : null) != null) { ((BaseUnityPlugin)Plugin.Instance).Config.SettingChanged -= OnSettingChanged; } } private static void OnSettingChanged(object sender, SettingChangedEventArgs e) { if (_applyingPayload) { return; } if (!PresetManager.IsApplying && Plugin.ActivePreset != null && e.ChangedSetting == Plugin.ActivePreset) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[RoomConfigSync] Preset change detected: {Plugin.ActivePreset.Value}, applying..."); } PresetManager.ApplyPreset(Plugin.ActivePreset.Value); } if (!HasOnlineRoom() || !PhotonNetwork.IsMasterClient) { return; } MarkDirty(immediate: false); if (PresetManager.IsApplying || !(Time.unscaledTime - _lastImmediatePublishTime >= 1f)) { return; } _lastImmediatePublishTime = Time.unscaledTime; ManualLogSource log2 = Plugin.Log; if (log2 != null) { ConfigEntryBase changedSetting = e.ChangedSetting; object obj; if (changedSetting == null) { obj = null; } else { ConfigDefinition definition = changedSetting.Definition; obj = ((definition != null) ? definition.Key : null); } if (obj == null) { obj = "?"; } log2.LogInfo((object)("[RoomConfigSync] Host setting changed: '" + (string?)obj + "' → republish")); } PublishHostConfig(force: true); } private static bool HasOnlineRoom() { if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null) { return !PhotonNetwork.OfflineMode; } return false; } private static void MarkDirty(bool immediate) { _configDirty = true; if (immediate) { _lastPublishTime = -10f; } } private static void ResetState(bool full) { _lastPublishedPayload = string.Empty; _lastAppliedPayload = string.Empty; _lastPublishTime = -10f; _lastPollTime = -10f; _configDirty = true; _wasMasterClient = false; if (full) { _activeRoomName = string.Empty; } } } internal static class StrayBugleCleaner { private const float ScanInterval = 5f; private const float OwnershipRetryInterval = 15f; private const int OwnershipMaxRetries = 5; private static float _lastScanTime = -999f; private static readonly Dictionary _strayStart = new Dictionary(); private static readonly Dictionary _transferLastTime = new Dictionary(); private static readonly Dictionary _transferAttemptCount = new Dictionary(); private static readonly Dictionary _transferLastOwnerActor = new Dictionary(); private static readonly HashSet _transferBlacklist = new HashSet(); private static bool _lastWasMaster; public static void Tick() { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) if (Plugin.StrayBugleCleanupEnabled == null || !Plugin.StrayBugleCleanupEnabled.Value || !PhotonNetwork.IsConnected) { return; } bool isMasterClient = PhotonNetwork.IsMasterClient; if (isMasterClient != _lastWasMaster) { _lastWasMaster = isMasterClient; if (_strayStart.Count > 0 || _transferAttemptCount.Count > 0 || _transferBlacklist.Count > 0) { _strayStart.Clear(); _transferLastTime.Clear(); _transferAttemptCount.Clear(); _transferLastOwnerActor.Clear(); _transferBlacklist.Clear(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[StrayBugleCleaner] master-state changed (now master={isMasterClient}) → reset timers & ownership retry state"); } } } if (!isMasterClient || Time.time - _lastScanTime < 5f) { return; } _lastScanTime = Time.time; float value = Plugin.StrayBugleDistance.Value; float value2 = Plugin.StrayBugleGracePeriod.Value; BugleSFX[] array = Object.FindObjectsByType((FindObjectsSortMode)0); HashSet hashSet = new HashSet(); BugleSFX[] array2 = array; foreach (BugleSFX val in array2) { if ((Object)(object)val == (Object)null || (Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) { continue; } int viewID = ((MonoBehaviourPun)val).photonView.ViewID; hashSet.Add(viewID); Item component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.holderCharacter != (Object)null) { _strayStart.Remove(viewID); continue; } float num = float.MaxValue; int num2 = 0; Vector3 position = ((Component)val).transform.position; if (Character.AllCharacters != null) { foreach (Character allCharacter in Character.AllCharacters) { if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter.data == (Object)null) && !allCharacter.data.dead) { num2++; float num3 = Vector3.Distance(allCharacter.Center, position); if (num3 < num) { num = num3; } } } } if (num2 == 0) { continue; } if (num <= value) { _strayStart.Remove(viewID); continue; } if (!_strayStart.TryGetValue(viewID, out var value3)) { _strayStart[viewID] = Time.time; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[StrayBugleCleaner] bugle ViewID={viewID} stray started (minDist={num:F0}m, threshold={value:F0}m)"); } continue; } float num4 = Time.time - value3; if (num4 < value2) { continue; } PhotonView photonView = ((MonoBehaviourPun)val).photonView; if (!photonView.IsMine) { Player owner = photonView.Owner; string text = ((owner == null) ? "null(scene?)" : string.Format("{0}#{1}{2}", owner.NickName, owner.ActorNumber, owner.IsInactive ? "[INACTIVE]" : "")); int num5 = ((owner != null) ? owner.ActorNumber : (-1)); if (_transferBlacklist.Contains(viewID)) { continue; } if (_transferLastOwnerActor.TryGetValue(viewID, out var value4) && value4 != num5) { _transferAttemptCount[viewID] = 0; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[StrayBugleCleaner] ViewID={viewID} owner changed ({value4}→{num5}), reset retry count"); } } if (_transferLastTime.TryGetValue(viewID, out var value5) && Time.time - value5 < 15f) { continue; } _transferAttemptCount.TryGetValue(viewID, out var value6); if (value6 >= 5) { _transferBlacklist.Add(viewID); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)$"[StrayBugleCleaner] ViewID={viewID} reached max retries ({5}), blacklisted (currentOwner={text})"); } continue; } try { photonView.TransferOwnership(PhotonNetwork.LocalPlayer); _transferLastTime[viewID] = Time.time; _transferAttemptCount[viewID] = value6 + 1; _transferLastOwnerActor[viewID] = num5; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[StrayBugleCleaner] requested ownership for ViewID={viewID}, currentOwner={text}, attempt={value6 + 1}/{5} (will destroy next scan)"); } } catch (Exception ex) { _transferLastTime[viewID] = Time.time; _transferAttemptCount[viewID] = value6 + 1; _transferLastOwnerActor[viewID] = num5; ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)$"[StrayBugleCleaner] TransferOwnership failed ViewID={viewID}, currentOwner={text}, attempt={value6 + 1}: {ex.Message}"); } } continue; } _transferLastTime.Remove(viewID); _transferAttemptCount.Remove(viewID); _transferLastOwnerActor.Remove(viewID); try { PhotonNetwork.Destroy(((Component)val).gameObject); _strayStart.Remove(viewID); ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)$"[StrayBugleCleaner] destroyed stray bugle ViewID={viewID} (minDist={num:F0}m, stray={num4:F0}s)"); } } catch (Exception ex2) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogWarning((object)$"[StrayBugleCleaner] Destroy failed for ViewID={viewID}: {ex2.Message}"); } } } if (_strayStart.Count <= 0 && _transferAttemptCount.Count <= 0 && _transferBlacklist.Count <= 0) { return; } List list = null; foreach (KeyValuePair item in _strayStart) { if (!hashSet.Contains(item.Key)) { if (list == null) { list = new List(); } list.Add(item.Key); } } if (list == null) { return; } foreach (int item2 in list) { _strayStart.Remove(item2); _transferLastTime.Remove(item2); _transferAttemptCount.Remove(item2); _transferLastOwnerActor.Remove(item2); _transferBlacklist.Remove(item2); } } } public enum HudPosition { TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight } public enum HudSizePreset { Small, Medium, Large, ExtraLarge } public enum LanternFuelOption { GameDefault = 0, Seconds30 = 30, Seconds60 = 60, Seconds90 = 90, Seconds120 = 120, Seconds240 = 240, Infinite = -1 } [BepInPlugin("com.wuyachiyu.LanternShootZombiesNight", "LanternShootZombiesNight", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin, IOnEventCallback { [CompilerGenerated] private sealed class d__48 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Plugin <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__48(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown int num = <>1__state; Plugin plugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(8f); <>1__state = 1; return true; case 1: { <>1__state = -1; bool flag = LanguageHelper.DetectChineseLanguage(); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[DEBUG] DeferredLangRefresh: was=" + (LanguageHelper.IsChinese ? "zh" : "en") + ", now=" + (flag ? "zh" : "en"))); } if (flag != LanguageHelper.IsChinese) { LanguageHelper.IsChinese = flag; ModConfigLocalization.ApplyLocalizedDescriptions(); try { ((BaseUnityPlugin)plugin).Config.Save(); } catch { } ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)("[DEBUG] Config descriptions updated to " + (LanguageHelper.IsChinese ? "Chinese" : "English"))); } ModConfigLocalization.RefreshCache(); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string ModVersion = "0.2.0"; public static Plugin Instance; public static ConfigEntry ActivePreset; public static ConfigEntry LanternMaxFuel; public static ConfigEntry EnableWarmthReduction; public static ConfigEntry LanternWarmthMultiplier; public static ConfigEntry EnableCampfireRefuel; public static ConfigEntry EnableWarmthRestore; public static ConfigEntry HitRestoreWarmth; public static ConfigEntry RestoreRadius; public static ConfigEntry HitRestoreCooldown; public static ConfigEntry ReserveWarmthMax; public static ConfigEntry AutoRefillEnabled; public static ConfigEntry AutoRefillCapPercent; public static ConfigEntry AutoRefillRate; public static ConfigEntry AutoRefillDaytimeOnly; public static ConfigEntry AutoRefillRequireHold; public static ConfigEntry BugleUltimateEnabled; public static ConfigEntry BugleUltimateCooldown; public static ConfigEntry BugleUltimateRadius; public static ConfigEntry BugleUltimateRestore; public static ConfigEntry PurgeExtraLanterns; public static ConfigEntry StrayBugleCleanupEnabled; public static ConfigEntry StrayBugleDistance; public static ConfigEntry StrayBugleGracePeriod; public static ConfigEntry EnableHud; public static ConfigEntry HudPos; public static ConfigEntry HudSize; public static ConfigEntry ShowDayNightOnHud; public static ConfigEntry SpawnItemsKey; public static ConfigEntry BugleRecallKey; public static ConfigEntry FlashlightDrainMultiplier; public static ConfigEntry CompanionDrainMultiplier; public static ConfigEntry SoloDrainMultiplier; public static ConfigEntry ProximityGracePeriod; public static ConfigEntry MuteZombieTornado; public static ConfigEntry EnableFuelIndicator3D; public static ConfigEntry EnableUpgradeSystem; public static ConfigEntry UpgradeLevelCostsCsv; public static ConfigEntry UpgradeCapacityBonusCsv; public static ConfigEntry UpgradeEfficiencyBonusCsv; public static ConfigEntry UpgradePassiveTickInterval; public static ConfigEntry UpgradePassivePointsPerTick; public static ConfigEntry UpgradeHitPoints; public static ConfigEntry UpgradeCampfirePoints; public static ConfigEntry UpgradeBuglePoints; internal static ManualLogSource Log; private void Awake() { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Expected O, but got Unknown //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Expected O, but got Unknown //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Expected O, but got Unknown //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Expected O, but got Unknown //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Expected O, but got Unknown //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Expected O, but got Unknown //IL_04ed: Unknown result type (might be due to invalid IL or missing references) //IL_04f7: Expected O, but got Unknown //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_053e: Expected O, but got Unknown //IL_06a9: Unknown result type (might be due to invalid IL or missing references) //IL_06b3: Expected O, but got Unknown //IL_06fa: Unknown result type (might be due to invalid IL or missing references) //IL_0704: Expected O, but got Unknown //IL_074b: Unknown result type (might be due to invalid IL or missing references) //IL_0755: Expected O, but got Unknown //IL_0793: Unknown result type (might be due to invalid IL or missing references) //IL_079d: Expected O, but got Unknown //IL_09c9: Unknown result type (might be due to invalid IL or missing references) //IL_09cf: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"[DEBUG] Plugin Awake start"); ReflectionCache.InitGameReflectionEarly(); LanguageHelper.IsChinese = LanguageHelper.DetectChineseLanguage(); Log.LogInfo((object)("[DEBUG] Language detected: " + (LanguageHelper.IsChinese ? "Chinese" : "English"))); LanternMaxFuel = ((BaseUnityPlugin)this).Config.Bind("Lantern", "LanternMaxFuel", LanternFuelOption.Seconds120, LanguageHelper.L("Fuel capacity. GameDefault=unchanged, others=seconds, Infinite=never burns out.", "燃料上限。默认=不改,其他=秒数,无限=永不熄灭。")); EnableWarmthReduction = ((BaseUnityPlugin)this).Config.Bind("Lantern", "EnableWarmthReduction", true, LanguageHelper.L("Enable lantern cold-resistance multiplier below.", "启用下方的灯笼抗寒倍率。")); LanternWarmthMultiplier = ((BaseUnityPlugin)this).Config.Bind("Lantern", "LanternWarmthMultiplier", 0.75f, new ConfigDescription(LanguageHelper.L("Cold resistance when holding lit lantern. 1=full block, 0.5=half cold, 0=no protection. Step=0.05", "持灯抗寒能力。1=完全抵御,0.5=减半受冷,0=无保护。步进=0.05"), (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())).WithStep(0.05f); EnableCampfireRefuel = ((BaseUnityPlugin)this).Config.Bind("Lantern", "EnableCampfireRefuel", true, LanguageHelper.L("Light campfire = refill lantern; near campfire = no fuel drain.", "篅火补满&暂停消耗。")); ReserveWarmthMax = ((BaseUnityPlugin)this).Config.Bind("Lantern", "ReserveWarmthMax", ReserveWarmthRatio.Half, LanguageHelper.L("Reserve = max × ratio. Overflow fills reserve; burning drains it first.", "备用池 = 上限×比例。溢出存备用,燃烧优先消耗。")); EnableWarmthRestore = ((BaseUnityPlugin)this).Config.Bind("Restore", "EnableWarmthRestore", true, LanguageHelper.L("Master switch for warmth restore (zombie hit).", "回暖总开关(打僵尸回暖)。")); HitRestoreWarmth = ((BaseUnityPlugin)this).Config.Bind("Restore", "HitRestoreWarmth", 8, new ConfigDescription(LanguageHelper.L("Warmth per zombie hit (seconds).", "每次击杀僵尸回暖(秒)。"), (AcceptableValueBase)(object)new AcceptableValueRange(0, 30), Array.Empty())); RestoreRadius = ((BaseUnityPlugin)this).Config.Bind("Restore", "RestoreRadius", 50, new ConfigDescription(LanguageHelper.L("Range for hit restore (meters).", "击杀回暖范围(米)。"), (AcceptableValueBase)(object)new AcceptableValueRange(1, 200), Array.Empty())); HitRestoreCooldown = ((BaseUnityPlugin)this).Config.Bind("Restore", "HitRestoreCooldown", 0.3f, new ConfigDescription(LanguageHelper.L("Min interval between hit restores (seconds). Step=0.1", "击杀回暖最小间隔(秒)。步进=0.1"), (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 5f), Array.Empty())).WithStep(0.1f); AutoRefillEnabled = ((BaseUnityPlugin)this).Config.Bind("Lantern", "AutoRefillEnabled", true, LanguageHelper.L("When lantern is out of fuel, slowly refill up to a cap.", "灯熄灭后自动缓慢回燃料至上限。")); AutoRefillCapPercent = ((BaseUnityPlugin)this).Config.Bind("Lantern", "AutoRefillCapPercent", 0.5f, new ConfigDescription(LanguageHelper.L("Refill upper cap (ratio of max fuel). Step=0.05", "自动回料上限(占满燃料比例)。步进=0.05"), (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 1f), Array.Empty())).WithStep(0.05f); AutoRefillRate = ((BaseUnityPlugin)this).Config.Bind("Lantern", "AutoRefillRate", 1f, new ConfigDescription(LanguageHelper.L("Seconds of fuel gained per real second. Step=0.1", "每现实秒回复的燃料秒数。步进=0.1"), (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 5f), Array.Empty())).WithStep(0.1f); AutoRefillDaytimeOnly = ((BaseUnityPlugin)this).Config.Bind("Lantern", "AutoRefillDaytimeOnly", false, LanguageHelper.L("Only refill during daytime.", "仅在白天自动回料。")); AutoRefillRequireHold = ((BaseUnityPlugin)this).Config.Bind("Lantern", "AutoRefillRequireHold", false, LanguageHelper.L("Only refill while a player is holding the lantern.", "仅在玩家持有灯时自动回料。")); BugleUltimateEnabled = ((BaseUnityPlugin)this).Config.Bind("Bugle", "BugleUltimateEnabled", true, LanguageHelper.L("Enable bugle ultimate: nearby players' lanterns refill, long global cooldown.", "启用号角大招:附近玩家灯笼回满,全局长CD。")); BugleUltimateCooldown = ((BaseUnityPlugin)this).Config.Bind("Bugle", "BugleUltimateCooldown", 180f, new ConfigDescription(LanguageHelper.L("Global cooldown between bugle ultimates (seconds).", "号角大招CD(秒),全局共享。"), (AcceptableValueBase)(object)new AcceptableValueRange(30f, 600f), Array.Empty())); BugleUltimateRadius = ((BaseUnityPlugin)this).Config.Bind("Bugle", "BugleUltimateRadius", 20f, new ConfigDescription(LanguageHelper.L("Ultimate effect radius (meters).", "大招生效范围(米)。"), (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), Array.Empty())); BugleUltimateRestore = ((BaseUnityPlugin)this).Config.Bind("Bugle", "BugleUltimateRestore", -1f, new ConfigDescription(LanguageHelper.L("Warmth gained per ultimate. -1 means refill to max.", "单次大招回暖秒数。-1 表示回满。"), (AcceptableValueBase)(object)new AcceptableValueRange(-1f, 300f), Array.Empty())); PurgeExtraLanterns = ((BaseUnityPlugin)this).Config.Bind("Lantern", "PurgeExtraLanterns", true, LanguageHelper.L("Auto destroy duplicate lanterns on a player (Faerie lantern exempt).", "自动销毁玩家身上的多余灯笼(仙子提灯豁免)。")); StrayBugleCleanupEnabled = ((BaseUnityPlugin)this).Config.Bind("Bugle", "StrayBugleCleanupEnabled", true, LanguageHelper.L("Host cleans stray (unheld, far away) bugles after grace period.", "房主自动清理无人持有且偏远的号角。")); StrayBugleDistance = ((BaseUnityPlugin)this).Config.Bind("Bugle", "StrayBugleDistance", 100f, new ConfigDescription(LanguageHelper.L("Distance from nearest player to count as stray (meters).", "距最近玩家大于此距离才算偏远(米)。"), (AcceptableValueBase)(object)new AcceptableValueRange(30f, 500f), Array.Empty())); StrayBugleGracePeriod = ((BaseUnityPlugin)this).Config.Bind("Bugle", "StrayBugleGracePeriod", 60f, new ConfigDescription(LanguageHelper.L("Must stay stray for N seconds before cleanup.", "持续N秒满足条件才清理。"), (AcceptableValueBase)(object)new AcceptableValueRange(10f, 600f), Array.Empty())); ActivePreset = ((BaseUnityPlugin)this).Config.Bind("Display", "ActivePreset", ConfigPreset.Custom, LanguageHelper.L("Config preset. Custom=manual, Casual=easy, Balanced=default, Hardcore=challenging. Switching applies all values at once.", "配置预设。自定义=手动调节,休闲=轻松,平衡=默认值,硬核=高挑战。切换时一键覆盖所有平衡相关配置。")); EnableHud = ((BaseUnityPlugin)this).Config.Bind("Display", "EnableHud", true, LanguageHelper.L("Show visual HUD panel (fuel bar, multipliers, status).", "显示可视化信息面板(燃料条、倍率、状态)。")); HudPos = ((BaseUnityPlugin)this).Config.Bind("Display", "HudPos", HudPosition.Bottom, LanguageHelper.L("HUD panel screen position (8 positions).", "HUD面板屏幕位置(8方位)。")); HudSize = ((BaseUnityPlugin)this).Config.Bind("Display", "HudSize", HudSizePreset.ExtraLarge, LanguageHelper.L("HUD panel size preset (Small/Medium/Large/ExtraLarge).", "HUD面板尺寸预设(小/中/大/超大)。")); ShowDayNightOnHud = ((BaseUnityPlugin)this).Config.Bind("Display", "ShowDayNightOnHud", true, LanguageHelper.L("Show day/night info on HUD (day count, time, BPR darkness).", "在HUD上显示日夜信息(天数、时间、BPR黑暗状态)。")); SpawnItemsKey = ((BaseUnityPlugin)this).Config.Bind("Display", "SpawnItemsKey", (KeyCode)289, LanguageHelper.L("Hotkey to spawn Bugle & Backpack if missing. Set to None to disable.", "快捷键生成号角和背包(身上没有时)。设为None可禁用。")); BugleRecallKey = ((BaseUnityPlugin)this).Config.Bind("Display", "BugleRecallKey", (KeyCode)288, LanguageHelper.L("Host-only hotkey: destroy ALL bugles in the scene (held or dropped). Set to None to disable.", "房主专属快捷键:销毁场景里所有号角(含被持有/掉落的)。设为None可禁用。")); FlashlightDrainMultiplier = ((BaseUnityPlugin)this).Config.Bind("Lantern", "FlashlightDrainMultiplier", 1.2f, new ConfigDescription(LanguageHelper.L("Fuel drain multiplier when BPR flashlight mode is active. 1=normal, 2=double. Step=0.05", "BPR手电筒模式激活时的燃料消耗倍率。1=正常,2=双倍消耗。步进=0.05"), (AcceptableValueBase)(object)new AcceptableValueRange(1f, 2f), Array.Empty())).WithStep(0.05f); CompanionDrainMultiplier = ((BaseUnityPlugin)this).Config.Bind("Lantern", "CompanionDrainMultiplier", 0.8f, new ConfigDescription(LanguageHelper.L("Drain multiplier when companions are nearby. More players = stronger effect (scales to 3). Step=0.05", "附近有同伴时的消耗倍率。人越多效果越强(最多按3人计算)。步进=0.05"), (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 1f), Array.Empty())).WithStep(0.05f); SoloDrainMultiplier = ((BaseUnityPlugin)this).Config.Bind("Lantern", "SoloDrainMultiplier", 1.2f, new ConfigDescription(LanguageHelper.L("Drain multiplier when alone. Step=0.05", "独自行动时的消耗倍率。步进=0.05"), (AcceptableValueBase)(object)new AcceptableValueRange(1f, 1.5f), Array.Empty())).WithStep(0.05f); ProximityGracePeriod = ((BaseUnityPlugin)this).Config.Bind("Lantern", "ProximityGracePeriod", 15, new ConfigDescription(LanguageHelper.L("Seconds before companion/solo multiplier activates after state change.", "状态切换后,结伴/独行倍率生效前的等待秒数。"), (AcceptableValueBase)(object)new AcceptableValueRange(10, 120), Array.Empty())); MuteZombieTornado = ((BaseUnityPlugin)this).Config.Bind("Display", "MuteZombieTornado", false, LanguageHelper.L("Mute all zombie and tornado sounds.", "静音所有僵尸和龙卷风的声音。")); EnableFuelIndicator3D = ((BaseUnityPlugin)this).Config.Bind("Display", "EnableFuelIndicator3D", true, LanguageHelper.L("Show 3D fuel percentage above lit lanterns.", "在点燃的灯笼上方显示 3D 燃料百分比。")); EnableFuelIndicator3D.SettingChanged += delegate { LanternFuelIndicator3D.GlobalEnabled = EnableFuelIndicator3D.Value; }; EnableUpgradeSystem = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "EnableUpgradeSystem", true, LanguageHelper.L("Enable lantern upgrade system. Points auto-spend to upgrade (capacity first, then efficiency).", "启用灯笼升级系统。点数足够时自动升级(先容量再效率)。")); UpgradeLevelCostsCsv = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "LevelCostsCsv", "30,60,90,120,150", LanguageHelper.L("Point cost per level (comma separated, 5 values).", "每级升级所需点数(逗号分隔,5 个值)。")); UpgradeCapacityBonusCsv = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "CapacityBonusCsv", "0.15,0.3,0.45,0.6,0.75", LanguageHelper.L("Capacity bonus per level (comma separated, 5 values). Final multiplier = 1 + value.", "每级容量加成(5 个值)。最终倍率 = 1 + 值。")); UpgradeEfficiencyBonusCsv = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "EfficiencyBonusCsv", "0.1,0.2,0.3,0.4,0.5", LanguageHelper.L("Efficiency bonus per level (comma separated, 5 values). Final drain multiplier = 1 - value.", "每级效率加成(5 个值)。最终消耗倍率 = 1 - 值。")); UpgradePassiveTickInterval = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "PassiveTickInterval", 30f, LanguageHelper.L("Passive points accrual interval in seconds.", "被动累积点数的间隔秒数。")); UpgradePassivePointsPerTick = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "PassivePointsPerTick", 1, LanguageHelper.L("Points gained per passive tick.", "每次被动累积获得的点数。")); UpgradeHitPoints = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "HitPoints", 1, LanguageHelper.L("Points gained per zombie hit.", "每次打僵尸获得的点数。")); UpgradeCampfirePoints = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "CampfirePoints", 5, LanguageHelper.L("Points gained per campfire light-up.", "每次点燃篝火获得的点数。")); UpgradeBuglePoints = ((BaseUnityPlugin)this).Config.Bind("Upgrade", "BuglePoints", 3, LanguageHelper.L("Points gained per bugle ultimate trigger.", "每次号角大招触发获得的点数。")); Log.LogInfo((object)"[DEBUG] Config bound OK"); Instance = this; RoomConfigSync.Initialize(this); Harmony val = new Harmony("com.wuyachiyu.LanternShootZombiesNight"); try { val.PatchAll(typeof(ReduceLanternWarmthPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: ReduceLanternWarmthPatch"); } catch (Exception ex) { Log.LogError((object)("[DEBUG] Patch FAILED: ReduceLanternWarmthPatch - " + ex.Message)); } try { val.PatchAll(typeof(HeatEmissionMultiplierPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: HeatEmissionMultiplierPatch"); } catch (Exception ex2) { Log.LogError((object)("[DEBUG] Patch FAILED: HeatEmissionMultiplierPatch - " + ex2.Message)); } try { val.PatchAll(typeof(SubtractColdMonitorPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: SubtractColdMonitorPatch"); } catch (Exception ex3) { Log.LogError((object)("[DEBUG] Patch FAILED: SubtractColdMonitorPatch - " + ex3.Message)); } try { val.PatchAll(typeof(StatusFieldColdPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: StatusFieldColdPatch"); } catch (Exception ex4) { Log.LogError((object)("[DEBUG] Patch FAILED: StatusFieldColdPatch - " + ex4.Message)); } try { val.PatchAll(typeof(LocalDartFuelPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: LocalDartFuelPatch (DartImpact)"); } catch (Exception ex5) { Log.LogError((object)("[DEBUG] Patch FAILED: LocalDartFuelPatch - " + ex5.Message)); } try { val.PatchAll(typeof(RpcDartFuelPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: RpcDartFuelPatch (RPC_DartImpact)"); } catch (Exception ex6) { Log.LogError((object)("[DEBUG] Patch FAILED: RpcDartFuelPatch - " + ex6.Message)); } try { val.PatchAll(typeof(BugleUltimatePatch)); Log.LogInfo((object)"[DEBUG] Patch OK: BugleUltimatePatch"); } catch (Exception ex7) { Log.LogError((object)("[DEBUG] Patch FAILED: BugleUltimatePatch - " + ex7.Message)); } try { val.PatchAll(typeof(CampfireLightRefuelPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: CampfireLightRefuelPatch"); } catch (Exception ex8) { Log.LogError((object)("[DEBUG] Patch FAILED: CampfireLightRefuelPatch - " + ex8.Message)); } try { val.PatchAll(typeof(CampfireAwakePatch)); Log.LogInfo((object)"[DEBUG] Patch OK: CampfireAwakePatch"); } catch (Exception ex9) { Log.LogError((object)("[DEBUG] Patch FAILED: CampfireAwakePatch - " + ex9.Message)); } try { CampfireDestroyPatch.TryApply(val); } catch (Exception ex10) { Log.LogError((object)("[DEBUG] Patch FAILED: CampfireDestroyPatch - " + ex10.Message)); } try { val.PatchAll(typeof(LanternFuelOverridePatch)); Log.LogInfo((object)"[DEBUG] Patch OK: LanternFuelOverridePatch"); } catch (Exception ex11) { Log.LogError((object)("[DEBUG] Patch FAILED: LanternFuelOverridePatch - " + ex11.Message)); } try { val.PatchAll(typeof(MuteZombieSfxPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: MuteZombieSfxPatch"); } catch (Exception ex12) { Log.LogError((object)("[DEBUG] Patch FAILED: MuteZombieSfxPatch - " + ex12.Message)); } try { val.PatchAll(typeof(MuteTornadoSfxPatch)); Log.LogInfo((object)"[DEBUG] Patch OK: MuteTornadoSfxPatch"); } catch (Exception ex13) { Log.LogError((object)("[DEBUG] Patch FAILED: MuteTornadoSfxPatch - " + ex13.Message)); } try { ModConfigLocalization.PatchDisplayNames(val); Log.LogInfo((object)"[DEBUG] Patch OK: ModConfig display names"); } catch (Exception ex14) { Log.LogWarning((object)("[DEBUG] ModConfig display name patch skipped: " + ex14.Message)); } ModIntegration.Initialize(); ReflectionCache.Initialize(ModIntegration.IsBprLoaded, ModIntegration.IsSzLoaded); ((Component)this).gameObject.AddComponent(); LanternFuelIndicator3D.GlobalEnabled = EnableFuelIndicator3D.Value; if (ModIntegration.IsBprLoaded) { try { BprPerformanceFix.Apply(val); } catch (Exception ex15) { Log.LogError((object)("[DEBUG] BprPerformanceFix FAILED: " + ex15.Message)); } } if (ModIntegration.IsSzLoaded) { try { ShootZombiesPerformanceFix.Apply(val); } catch (Exception ex16) { Log.LogError((object)("[DEBUG] ShootZombiesPerformanceFix FAILED: " + ex16.Message)); } } PhotonNetwork.AddCallbackTarget((object)this); Log.LogInfo((object)"[DEBUG] Plugin Awake done"); Log.LogInfo((object)"[DEBUG] ═══ Config Summary ═══"); Log.LogInfo((object)$"[DEBUG] [Lantern/Cold] MaxFuel={LanternMaxFuel.Value}, Reduction={EnableWarmthReduction.Value}, Multiplier={LanternWarmthMultiplier.Value:F2}"); Log.LogInfo((object)$"[DEBUG] [Lantern/AutoRefill] Enabled={AutoRefillEnabled.Value}, Cap={AutoRefillCapPercent.Value:F2}, Rate={AutoRefillRate.Value:F2}/s, DaytimeOnly={AutoRefillDaytimeOnly.Value}, RequireHold={AutoRefillRequireHold.Value}"); Log.LogInfo((object)$"[DEBUG] [Lantern/Drain] Flashlight={FlashlightDrainMultiplier.Value:F2}, Companion={CompanionDrainMultiplier.Value:F2}, Solo={SoloDrainMultiplier.Value:F2}, GracePeriod={ProximityGracePeriod.Value}s"); Log.LogInfo((object)$"[DEBUG] [Lantern/Purge] PurgeExtra={PurgeExtraLanterns.Value}"); Log.LogInfo((object)$"[DEBUG] [Restore] Enabled={EnableWarmthRestore.Value}, Hit={HitRestoreWarmth.Value}s, Radius={RestoreRadius.Value}m, Cooldown={HitRestoreCooldown.Value:F1}s, Reserve={ReserveWarmthMax.Value}, Campfire={EnableCampfireRefuel.Value}"); Log.LogInfo((object)$"[DEBUG] [Bugle/Ult] Enabled={BugleUltimateEnabled.Value}, CD={BugleUltimateCooldown.Value:F0}s, Radius={BugleUltimateRadius.Value:F0}m, Restore={BugleUltimateRestore.Value:F0}s (-1=refillMax)"); Log.LogInfo((object)$"[DEBUG] [Bugle/Stray] Cleanup={StrayBugleCleanupEnabled.Value}, Dist={StrayBugleDistance.Value:F0}m, Grace={StrayBugleGracePeriod.Value:F0}s"); Log.LogInfo((object)$"[DEBUG] [Upgrade] Enabled={EnableUpgradeSystem.Value} (auto-upgrade, capacity first)"); Log.LogInfo((object)$"[DEBUG] [Display] HUD={EnableHud.Value} Pos={HudPos.Value} Size={HudSize.Value} DayNight={ShowDayNightOnHud.Value} Mute={MuteZombieTornado.Value} 3DIndicator={EnableFuelIndicator3D.Value} Preset={ActivePreset.Value}"); Log.LogInfo((object)"[DEBUG] ═══════════════════════"); ((MonoBehaviour)this).StartCoroutine(DeferredLanguageRefresh()); } [IteratorStateMachine(typeof(d__48))] private IEnumerator DeferredLanguageRefresh() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__48(0) { <>4__this = this }; } private void Update() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) LanternHud.Tick(); FlashlightDrainMonitor.Tick(); ProximityDrainMonitor.Tick(); DayNightTracker.Tick(); NightColdWarning.Tick(); ExtraLanternPurger.Tick(); StrayBugleCleaner.Tick(); LanternUpgradeSystem.Tick(); RoomConfigSync.UpdateSync(); LanternUpgradeSystem.SyncFromNetworkIfNeeded(); if ((int)SpawnItemsKey.Value != 0 && UnityInput.Current.GetKeyDown(SpawnItemsKey.Value)) { ItemSpawnHelper.TrySpawnMissingItems(); } if ((int)BugleRecallKey.Value != 0 && UnityInput.Current.GetKeyDown(BugleRecallKey.Value)) { BugleRecaller.TryRecall(); } } private void OnDestroy() { PhotonNetwork.RemoveCallbackTarget((object)this); RoomConfigSync.Cleanup(); } public void OnEvent(EventData photonEvent) { if (photonEvent.Code == 42) { LanternUpgradeSystem.HandleUpgradeEvent(photonEvent); } } } public enum ReserveWarmthRatio { Off = 0, Quarter = 25, Half = 50, ThreeQuarters = 75 } } namespace Lantern_ShootZombies_Night.Patches { public static class ZombieHitFuelHelper { private static float _lastFuelAddTime = -1f; public static void TryAddFuelOnHit(Character hitCharacter, string source) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hitCharacter == (Object)null || (!hitCharacter.isZombie && !hitCharacter.isBot) || !Plugin.EnableWarmthRestore.Value) { return; } float value = Plugin.HitRestoreCooldown.Value; if (Time.time - _lastFuelAddTime < value) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } float num = Vector3.Distance(localCharacter.Center, hitCharacter.Center); float num2 = Plugin.RestoreRadius.Value; if (num > num2) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [HitRestore] {source}: zombie hit but out of range (dist={num:F1}, radius={num2:F1})"); } return; } float num3 = Plugin.HitRestoreWarmth.Value; if (num3 <= 0f) { return; } if (LanternHelper.AddPlayerLanternFuelAnyState(localCharacter, num3, out var overflow, out var maxFuelOut)) { _lastFuelAddTime = Time.time; RestoreTracker.ReportLast("Hit", num3); LanternUpgradeSystem.AddEvent("Hit"); float num4 = num3 - overflow; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[WARMTH_LOG] source=HitRestore | item=ZombieHit({source}) | warmth=+{num3:F1}s | lantern+{num4:F1}s | overflow={overflow:F1}s | dist={num:F1}m | result=SUCCESS"); } if (overflow > 0f) { LanternHelper.AddOverflowToReserve(overflow, maxFuelOut); } } else { int value2 = (int)Plugin.LanternMaxFuel.Value; float lanternMax = ((value2 > 0) ? ((float)value2) : 300f); LanternHelper.AddOverflowToReserve(num3, lanternMax); _lastFuelAddTime = Time.time; RestoreTracker.ReportLast("Hit", num3); LanternUpgradeSystem.AddEvent("Hit"); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[WARMTH_LOG] source=HitRestore | item=ZombieHit({source}) | warmth=+{num3:F1}s | dist={num:F1}m | result=RESERVED(no lantern, stored in reserve)"); } } } } [HarmonyPatch] public static class LocalDartFuelPatch { public static MethodBase TargetMethod() { try { Type type = typeof(Item).Assembly.GetType("Action_RaycastDart"); if (type == null) { return null; } return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => string.Equals(m.Name, "DartImpact", StringComparison.Ordinal) && m.GetParameters().Length == 3 && m.GetParameters()[0].ParameterType == typeof(Character) && m.GetParameters()[1].ParameterType == typeof(Vector3) && m.GetParameters()[2].ParameterType == typeof(Vector3)); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[LocalDartFuelPatch] TargetMethod error: {arg}"); } return null; } } [HarmonyPostfix] public static void Postfix(MonoBehaviour __instance, Character hitCharacter, Vector3 origin, Vector3 endpoint) { ZombieHitFuelHelper.TryAddFuelOnHit(hitCharacter, "DartImpact"); } } [HarmonyPatch] public static class RpcDartFuelPatch { public static MethodBase TargetMethod() { try { Type type = typeof(Item).Assembly.GetType("Action_RaycastDart"); if (type == null) { return null; } return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => string.Equals(m.Name, "RPC_DartImpact", StringComparison.Ordinal) && (m.GetParameters().Length == 3 || m.GetParameters().Length == 4) && m.GetParameters()[0].ParameterType == typeof(int) && m.GetParameters()[1].ParameterType == typeof(Vector3) && m.GetParameters()[2].ParameterType == typeof(Vector3)); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RpcDartFuelPatch] TargetMethod error: {arg}"); } return null; } } [HarmonyPostfix] public static void Postfix(MonoBehaviour __instance, int characterID, Vector3 origin, Vector3 endpoint) { if (characterID > 0) { PhotonView val = PhotonView.Find(characterID); if (!((Object)(object)val == (Object)null)) { ZombieHitFuelHelper.TryAddFuelOnHit(((Component)val).GetComponentInParent(), "RPC_DartImpact"); } } } } internal static class BprPerformanceFix { private static int _cachedLanternItemId = -1; private static Lantern _cachedLanternComp; private static bool _applied; internal static void Apply(Harmony harmony) { //IL_0041: 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) //IL_005a: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown if (_applied) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(BlackPeakRemix), "Update", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(BprPerformanceFix), "UpdatePrefix", (Type[])null, (Type[])null)) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PerfFix] Patched BPR.Update() — cached Lantern.lit FieldInfo"); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[PerfFix] BPR.Update() method not found"); } } MethodInfo methodInfo2 = AccessTools.Method(typeof(Lantern), "UpdateFuel", (Type[])null, (Type[])null); Type type = typeof(BlackPeakRemix).Assembly.GetType("BlackPeakRemix.Patch_Lantern_BurnDuration"); MethodInfo methodInfo3 = ((type != null) ? AccessTools.Method(type, "PrefixUpdateFuel", (Type[])null, (Type[])null) : null); if (methodInfo2 != null && methodInfo3 != null) { harmony.Unpatch((MethodBase)methodInfo2, methodInfo3); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(AccessTools.Method(typeof(BprPerformanceFix), "OptimizedPrefixUpdateFuel", (Type[])null, (Type[])null)) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[PerfFix] Replaced BPR.PrefixUpdateFuel — cached Lantern.lit FieldInfo"); } } else { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("[PerfFix] Cannot replace PrefixUpdateFuel " + $"(updateFuel={methodInfo2 != null}, burnType={type != null}, prefix={methodInfo3 != null})")); } } _applied = true; } catch (Exception arg) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)$"[PerfFix] BprPerformanceFix.Apply failed: {arg}"); } } } private static bool UpdatePrefix() { //IL_012c: Unknown result type (might be due to invalid IL or missing references) try { if (!BlackPeakRemix.ActiveMasterSwitch) { return false; } if ((Object)(object)Character.localCharacter == (Object)null) { return false; } CharacterData data = Character.localCharacter.data; Item val = (((Object)(object)data != (Object)null) ? data.currentItem : null); if ((Object)(object)val == (Object)null || val.itemID != 42) { return false; } if (ReflectionCache.BprSyncHelperType == null) { return false; } Component val2 = ((Component)val).GetComponent(ReflectionCache.BprSyncHelperType); if ((Object)(object)val2 == (Object)null) { Lantern component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } val2 = ((Component)val).gameObject.AddComponent(ReflectionCache.BprSyncHelperType); ReflectionCache.BprSyncHelperInitMethod?.Invoke(val2, new object[1] { component }); } int instanceID = ((Object)val).GetInstanceID(); Lantern val3; if (instanceID != _cachedLanternItemId || (Object)(object)_cachedLanternComp == (Object)null) { val3 = ((Component)val).GetComponent(); _cachedLanternItemId = instanceID; _cachedLanternComp = val3; } else { val3 = _cachedLanternComp; } if ((Object)(object)val3 == (Object)null) { return false; } if (ReflectionCache.GetLit(val3) && Input.GetKeyDown(BlackPeakRemix.ToggleKey.Value) && ReflectionCache.BprSyncHelperModeField != null && ReflectionCache.BprStateOmni != null && ReflectionCache.BprStateFlashlight != null) { object obj = (ReflectionCache.BprSyncHelperModeField.GetValue(val2).Equals(ReflectionCache.BprStateOmni) ? ReflectionCache.BprStateFlashlight : ReflectionCache.BprStateOmni); ReflectionCache.BprSyncHelperModeField.SetValue(val2, obj); object? obj2 = ReflectionCache.BprLogField?.GetValue(null); object? obj3 = ((obj2 is ManualLogSource) ? obj2 : null); if (obj3 != null) { ((ManualLogSource)obj3).LogInfo((object)$"[Input] Lantern mode toggled: {obj}"); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PerfFix] BPR.Update prefix error: " + ex.Message)); } } return false; } private static bool OptimizedPrefixUpdateFuel(Lantern __instance) { try { if (!BlackPeakRemix.ActiveMasterSwitch || !IsNormalLantern(__instance)) { return true; } if (!((ItemComponent)__instance).HasData((DataEntryKey)10)) { return true; } if (!ReflectionCache.GetLit(__instance) || !NetworkingUtilities.HasAuthority((MonoBehaviourPunCallbacks)(object)__instance)) { return false; } float activeLanternBurnDuration = BlackPeakRemix.ActiveLanternBurnDuration; if (activeLanternBurnDuration > 20000f) { return false; } float num = 60f / activeLanternBurnDuration; float num2 = Mathf.Clamp(((ItemComponent)__instance).GetData((DataEntryKey)10).Value - Time.deltaTime * num, 0f, __instance.startingFuel); ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = num2; Item item = ((ItemComponent)__instance).item; if ((Object)(object)item != (Object)null) { item.SetUseRemainingPercentage(num2 / __instance.startingFuel); } if (num2 <= 0f && ((MonoBehaviourPun)__instance).photonView.IsMine) { __instance.SnuffLantern(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PerfFix] PrefixUpdateFuel error: " + ex.Message)); } } return false; } private static bool IsNormalLantern(Lantern lantern) { Item val = (((Object)(object)lantern != (Object)null) ? ((Component)lantern).GetComponent() : null); if ((Object)(object)val != (Object)null) { return val.itemID == 42; } return false; } } [HarmonyPatch(typeof(BugleSFX), "RPC_StartToot")] internal static class BugleUltimatePatch { private static float _lastUltTime = -999f; [HarmonyPostfix] public static void Postfix(BugleSFX __instance) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || Plugin.BugleUltimateEnabled == null || !Plugin.BugleUltimateEnabled.Value) { return; } float value = Plugin.BugleUltimateCooldown.Value; if (Time.time - _lastUltTime < value) { float num = value - (Time.time - _lastUltTime); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [BugleUlt] CD not ready: remain={num:F1}s / {value:F0}s"); } return; } _lastUltTime = Time.time; Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } Vector3 position = ((Component)__instance).transform.position; float num2 = Vector3.Distance(localCharacter.Center, position); float value2 = Plugin.BugleUltimateRadius.Value; if (num2 > value2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [BugleUlt] out of range: dist={num2:F1} > radius={value2:F1}"); } return; } float totalOverflow; float lanternMaxAny; int num3 = RefillAllLocalLanterns(localCharacter, out totalOverflow, out lanternMaxAny); if (num3 == 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[DEBUG] [BugleUlt] no lanterns to refill"); } return; } if (totalOverflow > 0f && lanternMaxAny > 0f) { LanternHelper.AddOverflowToReserve(totalOverflow, lanternMaxAny); } RestoreTracker.ReportLast("Bugle", totalOverflow); LanternUpgradeSystem.AddEvent("Bugle"); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[WARMTH_LOG] source=BugleUlt | count={num3} | overflow={totalOverflow:F1} | dist={num2:F1}m | cd={value:F0}s"); } } private static int RefillAllLocalLanterns(Character character, out float totalOverflow, out float lanternMaxAny) { totalOverflow = 0f; lanternMaxAny = 0f; if ((Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)character.player == (Object)null) { return 0; } float value = Plugin.BugleUltimateRestore.Value; bool refillToMax = value < 0f; int num = 0; if (character.player.itemSlots != null) { ItemSlot[] itemSlots = character.player.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (TryProcessSlot(itemSlots[i], refillToMax, value, isInBackpack: false, ref totalOverflow, ref lanternMaxAny)) { num++; } } } if (TryProcessSlot(character.player.tempFullSlot, refillToMax, value, isInBackpack: false, ref totalOverflow, ref lanternMaxAny)) { num++; } BackpackSlot backpackSlot = character.player.backpackSlot; BackpackData val = default(BackpackData); if (backpackSlot != null && backpackSlot.hasBackpack && ((ItemSlot)backpackSlot).data != null && ((ItemSlot)backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val) && val != null && val.itemSlots != null) { ItemSlot[] itemSlots = val.itemSlots; for (int i = 0; i < itemSlots.Length; i++) { if (TryProcessSlot(itemSlots[i], refillToMax, value, isInBackpack: true, ref totalOverflow, ref lanternMaxAny)) { num++; } } } return num; } private static bool TryProcessSlot(ItemSlot slot, bool refillToMax, float configRestore, bool isInBackpack, ref float totalOverflow, ref float lanternMaxAny) { if (slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null || slot.data == null) { return false; } if (((Object)slot.prefab).name != "Lantern") { return false; } Item val = LanternHelper.FindWorldItemByGuid(slot.data.guid); if ((Object)(object)val == (Object)null) { return false; } Lantern component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } if (LanternHelper.IsSpecialLantern(component)) { return false; } FloatItemData val2 = default(FloatItemData); if (!val.data.TryGetDataEntry((DataEntryKey)10, ref val2)) { return false; } float startingFuel = component.startingFuel; if (startingFuel <= 0f) { return false; } float value = val2.Value; float num = (refillToMax ? (startingFuel - value) : configRestore); if (num <= 0f) { return false; } float num2 = value + num; float num3 = (val2.Value = Mathf.Min(num2, startingFuel)); float num4 = ((num2 > startingFuel) ? (num2 - startingFuel) : 0f); totalOverflow += num4; lanternMaxAny = startingFuel; if ((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null) { ((MonoBehaviourPun)val).photonView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { val.data }); } if (!ReflectionCache.GetLit(component) && num3 > 0.1f && (Object)(object)((MonoBehaviourPun)component).photonView != (Object)null && ((MonoBehaviourPun)component).photonView.IsMine) { if (isInBackpack) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [BugleUlt] Skipped re-lit (in backpack, viewId={((MonoBehaviourPun)component).photonView.ViewID})"); } } else { ((MonoBehaviourPun)component).photonView.RPC("LightLanternRPC", (RpcTarget)0, new object[1] { true }); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [BugleUlt] Re-lit lantern (viewId={((MonoBehaviourPun)component).photonView.ViewID})"); } } } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] [BugleUlt] Refilled: {value:F1} → {num3:F1} / max={startingFuel:F1}, overflow={num4:F1}, backpack={isInBackpack}"); } return true; } } [HarmonyPatch(typeof(Campfire), "Light_Rpc")] internal static class CampfireLightRefuelPatch { [HarmonyPostfix] public static void Postfix(Campfire __instance) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.EnableCampfireRefuel.Value || !__instance.Lit) { return; } float moraleBoostRadius = __instance.moraleBoostRadius; Vector3 position = ((Component)__instance).transform.position; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [CampfireRefuel] Campfire lit! radius={moraleBoostRadius:F1}"); } foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter == (Object)null || !allCharacter.IsLocal) { continue; } float num = Vector3.Distance(allCharacter.Center, position); if (num > moraleBoostRadius) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [CampfireRefuel] Out of range (dist={num:F1})"); } } else if (LanternHelper.RefillPlayerLanternFuelAnyState(allCharacter)) { RestoreTracker.ReportLast("Campfire", 0f); LanternUpgradeSystem.AddEvent("Campfire"); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[WARMTH_LOG] source=CampfireRefuel | item=Campfire | warmth=REFILL_MAX | dist={num:F1}m | result=SUCCESS"); } } else { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[WARMTH_LOG] source=CampfireRefuel | item=Campfire | warmth=0 | dist={num:F1}m | result=FAILED(no lantern)"); } } } } } internal static class CampfireHelper { private static readonly List _activeCampfires = new List(); private static float _lastFallbackTime = -999f; private const float FallbackInterval = 10f; public static void Register(Campfire cf) { if ((Object)(object)cf != (Object)null && !_activeCampfires.Contains(cf)) { _activeCampfires.Add(cf); } } public static void Unregister(Campfire cf) { _activeCampfires.Remove(cf); } public static bool IsNearLitCampfire(Character character) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return false; } _activeCampfires.RemoveAll((Campfire c) => (Object)(object)c == (Object)null); if (Time.time - _lastFallbackTime >= 10f) { _lastFallbackTime = Time.time; Campfire[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Campfire val in array) { if ((Object)(object)val != (Object)null && !_activeCampfires.Contains(val)) { _activeCampfires.Add(val); } } } Vector3 center = character.Center; foreach (Campfire activeCampfire in _activeCampfires) { if (!((Object)(object)activeCampfire == (Object)null) && activeCampfire.Lit && Vector3.Distance(center, ((Component)activeCampfire).transform.position) <= activeCampfire.moraleBoostRadius) { return true; } } return false; } } [HarmonyPatch(typeof(Campfire), "Awake")] internal static class CampfireAwakePatch { [HarmonyPostfix] public static void Postfix(Campfire __instance) { CampfireHelper.Register(__instance); } } internal static class CampfireDestroyPatch { public static void Postfix(Campfire __instance) { CampfireHelper.Unregister(__instance); } public static bool TryApply(Harmony harmony) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Campfire), "OnDestroy", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(Campfire), "OnDisable", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[DEBUG] CampfireDestroyPatch: no OnDestroy/OnDisable in Campfire, relying on null cleanup"); } return false; } HarmonyMethod val = new HarmonyMethod(typeof(CampfireDestroyPatch).GetMethod("Postfix")); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[DEBUG] Patch OK: CampfireDestroyPatch → " + methodInfo.Name)); } return true; } } [HarmonyPatch(typeof(Lantern))] internal static class LanternFuelOverridePatch { private static readonly Dictionary _trackedFuel = new Dictionary(); private static float _prefixSavedFuel = -1f; private static readonly HashSet _needsRelight = new HashSet(); private static readonly Dictionary _lastKnownLit = new Dictionary(); private static float _reserveLogTimer; private static float _lastLoggedReserve = -1f; private static bool _wasCampfireProtecting; private static readonly Dictionary _autoRefillLogTimer = new Dictionary(); private static float _drainLogTimer = 30f; [HarmonyPostfix] [HarmonyPatch("Awake")] public static void AwakePostfix(Lantern __instance) { if (LanternHelper.IsSpecialLantern(__instance)) { return; } if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } int value = (int)Plugin.LanternMaxFuel.Value; if (value == 0) { return; } float num = ((value > 0) ? ((float)value) : 99999f); if (Plugin.EnableUpgradeSystem != null && Plugin.EnableUpgradeSystem.Value && LanternUpgradeSystem.CapacityLevel > 0) { float num2 = num; num *= LanternUpgradeSystem.CapacityMultiplier; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LanternFuel] Upgrade capacity bonus: base={num2:F0} ×{LanternUpgradeSystem.CapacityMultiplier:F1} = {num:F0}"); } } __instance.startingFuel = num; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [LanternFuel] startingFuel → {num}"); } } [HarmonyPostfix] [HarmonyPatch("OnInstanceDataSet")] public static void OnInstanceDataSetPostfix(Lantern __instance) { if (LanternHelper.IsSpecialLantern(__instance)) { return; } bool flag = ((ItemComponent)__instance).HasData((DataEntryKey)3) && ((ItemComponent)__instance).GetData((DataEntryKey)3).Value; int num = (((Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null) ? ((MonoBehaviourPun)__instance).photonView.ViewID : (-1)); bool flag2 = (Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null && ((MonoBehaviourPun)__instance).photonView.IsMine; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LitSync] OnInstanceDataSet: viewId={num}, FlareActive={flag}, lit={ReflectionCache.GetLit(__instance)}, isMine={flag2}"); } if (Plugin.LanternMaxFuel.Value != 0) { int instanceID = ((Object)__instance).GetInstanceID(); float startingFuel = __instance.startingFuel; float num2 = startingFuel; if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { num2 = ((ItemComponent)__instance).GetData((DataEntryKey)10).Value; } _trackedFuel[instanceID] = Mathf.Clamp(num2, 0f, startingFuel); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [LanternFuel] OnInstanceDataSet: id={instanceID}, dataFuel={num2:F1}, tracked={_trackedFuel[instanceID]:F1}, max={startingFuel:F1}"); } Item component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.SetUseRemainingPercentage(_trackedFuel[instanceID] / startingFuel); } } } [HarmonyPostfix] [HarmonyPatch("LightLanternRPC")] public static void LightLanternRPCPostfix(Lantern __instance, bool litValue) { if (!LanternHelper.IsSpecialLantern(__instance)) { int num = (((Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null) ? ((MonoBehaviourPun)__instance).photonView.ViewID : (-1)); bool flag = (Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null && ((MonoBehaviourPun)__instance).photonView.IsMine; bool flag2 = (Object)(object)__instance.lanternLight != (Object)null && ((Component)__instance.lanternLight).gameObject.activeSelf; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LitSync] LightLanternRPC RECEIVED: viewId={num}, lit={litValue}, isMine={flag}, lightGO.active={flag2}"); } } } [HarmonyPrefix] [HarmonyPatch("UpdateFuel")] [HarmonyPriority(800)] public static void UpdateFuelPrefix(Lantern __instance) { _prefixSavedFuel = -1f; if (LanternHelper.IsSpecialLantern(__instance)) { return; } int value = (int)Plugin.LanternMaxFuel.Value; if (value == 0 && ((MonoBehaviourPun)__instance).photonView.IsMine) { _prefixSavedFuel = ReflectionCache.GetFuel(__instance); } if (value == 0 || !((MonoBehaviourPun)__instance).photonView.IsMine) { return; } int instanceID = ((Object)__instance).GetInstanceID(); float startingFuel = __instance.startingFuel; if (_trackedFuel.ContainsKey(instanceID) && ((ItemComponent)__instance).HasData((DataEntryKey)10)) { float value2 = ((ItemComponent)__instance).GetData((DataEntryKey)10).Value; if (value2 > _trackedFuel[instanceID]) { float num = _trackedFuel[instanceID]; if (num < 0.1f && value2 > 0f) { _needsRelight.Add(instanceID); } _trackedFuel[instanceID] = Mathf.Min(value2, startingFuel); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LanternFuel] Prefix: external fuel detected! {num:F1} → {_trackedFuel[instanceID]:F1} (data={value2:F1})"); } } } float value3 = ((value == -1) ? startingFuel : (_trackedFuel.ContainsKey(instanceID) ? _trackedFuel[instanceID] : startingFuel)); ReflectionCache.SetFuel(__instance, value3); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = value3; } } [HarmonyPostfix] [HarmonyPatch("UpdateFuel")] public static void UpdateFuelPostfix(Lantern __instance) { if (LanternHelper.IsSpecialLantern(__instance)) { return; } bool flag = ReflectionCache.GetLit(__instance); int instanceID = ((Object)__instance).GetInstanceID(); if (!_lastKnownLit.TryGetValue(instanceID, out var value) || value != flag) { _lastKnownLit[instanceID] = flag; int num = (((Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null) ? ((MonoBehaviourPun)__instance).photonView.ViewID : (-1)); bool flag2 = (Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null && ((MonoBehaviourPun)__instance).photonView.IsMine; bool flag3 = (Object)(object)__instance.lanternLight != (Object)null && ((Component)__instance.lanternLight).gameObject.activeSelf; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LitSync] lit CHANGED: viewId={num}, {value}→{flag}, isMine={flag2}, lightGO.active={flag3}"); } } ConsumeReserveForLantern(__instance, flag); CompensateCampfireProtection(__instance, flag); CompensateDrainMultiplier(__instance); LogReservePeriodic(__instance, flag); int value2 = (int)Plugin.LanternMaxFuel.Value; if (value2 == 0) { return; } int instanceID2 = ((Object)__instance).GetInstanceID(); float startingFuel = __instance.startingFuel; if (value2 == -1) { if (((MonoBehaviourPun)__instance).photonView.IsMine) { ReflectionCache.SetFuel(__instance, startingFuel); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = startingFuel; } } _trackedFuel[instanceID2] = startingFuel; Item component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { component.SetUseRemainingPercentage(1f); } return; } if (!((MonoBehaviourPun)__instance).photonView.IsMine) { if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { float value3 = ((ItemComponent)__instance).GetData((DataEntryKey)10).Value; Item component2 = ((Component)__instance).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.SetUseRemainingPercentage(value3 / startingFuel); } } return; } if (!_trackedFuel.ContainsKey(instanceID2)) { _trackedFuel[instanceID2] = startingFuel; } if (!flag && _trackedFuel[instanceID2] > 0f && _needsRelight.Contains(instanceID2)) { _needsRelight.Remove(instanceID2); if (!ReflectionCache.GetLit(__instance)) { int num2 = (((Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null) ? ((MonoBehaviourPun)__instance).photonView.ViewID : (-1)); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[DEBUG] [LitSync] SEND LightLanternRPC(true) via AUTO-RE-LIT: viewId={num2}"); } ((MonoBehaviourPun)__instance).photonView.RPC("LightLanternRPC", (RpcTarget)0, new object[1] { true }); flag = true; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[DEBUG] [LanternFuel] Postfix: AUTO RE-LIT lantern! (fuel={_trackedFuel[instanceID2]:F1})"); } } else { flag = true; } } else if (flag) { _needsRelight.Remove(instanceID2); } if (flag) { float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; float num3 = Time.deltaTime * fuelDrainMultiplier; if (IsLanternHolderNearCampfire(__instance)) { num3 = 0f; } else { float num4 = LanternHelper.ConsumeReserve(num3); num3 -= num4; } _trackedFuel[instanceID2] -= num3; if (_trackedFuel[instanceID2] <= 0f) { _trackedFuel[instanceID2] = 0f; __instance.SnuffLantern(); } } else { TryAutoRefill(__instance, instanceID2, startingFuel); } float num5 = _trackedFuel[instanceID2]; ReflectionCache.SetFuel(__instance, num5); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = num5; } Item component3 = ((Component)__instance).GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.SetUseRemainingPercentage(num5 / startingFuel); } } private static void TryAutoRefill(Lantern __instance, int id, float max) { if (Plugin.AutoRefillEnabled == null || !Plugin.AutoRefillEnabled.Value || !_trackedFuel.ContainsKey(id)) { return; } float num = max * Mathf.Clamp01(Plugin.AutoRefillCapPercent.Value); float num2 = _trackedFuel[id]; if (num2 >= num || (Plugin.AutoRefillDaytimeOnly.Value && !DayNightTracker.IsDaytime)) { return; } bool flag = true; if (Plugin.AutoRefillRequireHold.Value) { Item component = ((Component)__instance).GetComponent(); flag = (Object)(object)component != (Object)null && (Object)(object)component.holderCharacter != (Object)null; } if (!flag) { return; } float num3 = Mathf.Max(0f, Plugin.AutoRefillRate.Value) * Time.deltaTime; float num4 = Mathf.Min(num2 + num3, num); _trackedFuel[id] = num4; if (num4 > num2) { RestoreTracker.ReportAutoRefillActive(); } if (!_autoRefillLogTimer.TryGetValue(id, out var value)) { value = 0f; } value += Time.deltaTime; if (value >= 5f) { _autoRefillLogTimer[id] = 0f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [AutoRefill] id={id}, {num2:F1} → {num4:F1} / cap={num:F1} (rate={Plugin.AutoRefillRate.Value:F2}/s)"); } } else { _autoRefillLogTimer[id] = value; } } private static void ConsumeReserveForLantern(Lantern __instance, bool lit) { if (Plugin.LanternMaxFuel.Value != 0 || !((MonoBehaviourPun)__instance).photonView.IsMine) { return; } if (!lit && ((ItemComponent)__instance).HasData((DataEntryKey)10)) { float value = ((ItemComponent)__instance).GetData((DataEntryKey)10).Value; if (value > 0.1f) { ((MonoBehaviourPun)__instance).photonView.RPC("LightLanternRPC", (RpcTarget)0, new object[1] { true }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [LanternFuel] GameDefault: AUTO RE-LIT (fuel={value:F1})"); } lit = true; } } if (LanternHelper.ReserveWarmth <= 0f || !lit) { return; } float num = LanternHelper.ConsumeReserve(Time.deltaTime); if (num > 0f) { float value2 = ReflectionCache.GetFuel(__instance) + num; ReflectionCache.SetFuel(__instance, value2); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = value2; } } } private static void CompensateCampfireProtection(Lantern __instance, bool lit) { if (Plugin.LanternMaxFuel.Value != 0 || !((MonoBehaviourPun)__instance).photonView.IsMine || !lit || !IsLanternHolderNearCampfire(__instance)) { return; } float deltaTime = Time.deltaTime; float fuel = ReflectionCache.GetFuel(__instance); float startingFuel = __instance.startingFuel; float num = Mathf.Min(fuel + deltaTime, startingFuel); ReflectionCache.SetFuel(__instance, num); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = num; } if (!_wasCampfireProtecting) { _wasCampfireProtecting = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [CampfireProtect] GameDefault: campfire protection STARTED (fuel={num:F1}/{startingFuel:F1})"); } } } private static void CompensateDrainMultiplier(Lantern __instance) { if (Plugin.LanternMaxFuel.Value != 0 || !((MonoBehaviourPun)__instance).photonView.IsMine || _prefixSavedFuel < 0f) { return; } float fuelDrainMultiplier = LanternHelper.FuelDrainMultiplier; if (Mathf.Approximately(fuelDrainMultiplier, 1f)) { return; } float fuel = ReflectionCache.GetFuel(__instance); float num = _prefixSavedFuel - fuel; if (num <= 0f) { return; } float num2 = num * fuelDrainMultiplier; float num3 = num - num2; float num4 = Mathf.Max(fuel + num3, 0f); float startingFuel = __instance.startingFuel; num4 = Mathf.Min(num4, startingFuel); ReflectionCache.SetFuel(__instance, num4); if (((ItemComponent)__instance).HasData((DataEntryKey)10)) { ((ItemComponent)__instance).GetData((DataEntryKey)10).Value = num4; } _drainLogTimer += Time.deltaTime; if (_drainLogTimer >= 30f) { _drainLogTimer = 0f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [FuelDrain] GameDefault: drain={fuelDrainMultiplier:F2}x, consumed {num:F4} → {num2:F4}, fuel={num4:F1}/{startingFuel:F1}"); } } } private static bool IsLanternHolderNearCampfire(Lantern lantern) { if (!Plugin.EnableCampfireRefuel.Value) { return false; } Item component = ((Component)lantern).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Character val = component.holderCharacter; if ((Object)(object)val == (Object)null) { val = Character.localCharacter; } if ((Object)(object)val == (Object)null || !val.IsLocal) { return false; } bool num = CampfireHelper.IsNearLitCampfire(val); if (!num && _wasCampfireProtecting) { _wasCampfireProtecting = false; ManualLogSource log = Plugin.Log; if (log == null) { return num; } log.LogInfo((object)"[DEBUG] [CampfireProtect] Campfire protection ENDED (left range)"); } return num; } private static void LogReservePeriodic(Lantern __instance, bool lit) { if (!((MonoBehaviourPun)__instance).photonView.IsMine) { return; } _reserveLogTimer += Time.deltaTime; if (_reserveLogTimer < 10f) { return; } _reserveLogTimer = 0f; float reserveWarmth = LanternHelper.ReserveWarmth; int value = (int)Plugin.LanternMaxFuel.Value; float fuel = ReflectionCache.GetFuel(__instance); float reserveMax = LanternHelper.GetReserveMax(__instance.startingFuel); if (reserveWarmth > 0f || _lastLoggedReserve > 0f) { _lastLoggedReserve = reserveWarmth; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[DEBUG] [Reserve] status: reserve={0:F1}/{1:F1}, fuel={2:F1}, lit={3}, mode={4}", reserveWarmth, reserveMax, fuel, lit, (value == 0) ? "GameDefault" : value.ToString())); } } } } [HarmonyPatch(typeof(MushroomZombie), "RPC_PlaySFX")] internal static class MuteZombieSfxPatch { private static bool _loggedMute; [HarmonyPrefix] private static bool Prefix() { if (Plugin.MuteZombieTornado.Value) { if (!_loggedMute) { _loggedMute = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [Mute] Zombie SFX muted (first occurrence)"); } } return false; } _loggedMute = false; return true; } } [HarmonyPatch(typeof(Tornado), "Start")] internal static class MuteTornadoSfxPatch { [HarmonyPostfix] private static void Postfix(Tornado __instance) { if (Plugin.MuteZombieTornado.Value) { AudioSource[] componentsInChildren = ((Component)__instance).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].mute = true; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DEBUG] [Mute] Tornado audio sources muted"); } } } } [HarmonyPatch(typeof(CharacterAfflictions), "AddStatus", new Type[] { typeof(STATUSTYPE), typeof(float), typeof(bool), typeof(bool), typeof(bool) })] internal class ReduceLanternWarmthPatch { private static float _logTimer = 10f; public static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float amount) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)statusType != 2 || !Plugin.EnableWarmthReduction.Value || DayNightTracker.IsDaytime) { return; } Character character = __instance.character; if ((Object)(object)character == (Object)null || !character.IsLocal || LanternHelper.FindLitLanternSlot(character) == null) { return; } float value = Plugin.LanternWarmthMultiplier.Value; float num = Mathf.Clamp01(1f - value); float num2 = amount; amount *= num; _logTimer += Time.deltaTime; if (_logTimer >= 10f) { _logTimer = 0f; float currentStatus = __instance.GetCurrentStatus((STATUSTYPE)2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [WarmthReduction] multiplier={value:F2}, factor={num:F2}, cold {num2:F4} → {amount:F4}, coldVal={currentStatus:F3}"); } } } } [HarmonyPatch(typeof(CharacterHeatEmission), "Update")] internal class HeatEmissionMultiplierPatch { private static readonly FieldInfo _characterField = AccessTools.Field(typeof(CharacterHeatEmission), "character"); internal static bool InsideHeatEmission = false; internal static bool IsLocalSource = false; [HarmonyPrefix] public static void Prefix(CharacterHeatEmission __instance) { InsideHeatEmission = true; IsLocalSource = false; if (Plugin.EnableWarmthReduction.Value && !DayNightTracker.IsDaytime) { object? obj = _characterField?.GetValue(__instance); Character val = (Character)((obj is Character) ? obj : null); if (!((Object)(object)val == (Object)null) && val.IsLocal && LanternHelper.FindLitLanternSlot(val) != null) { IsLocalSource = true; } } } [HarmonyPostfix] public static void Postfix() { InsideHeatEmission = false; IsLocalSource = false; } } [HarmonyPatch(typeof(CharacterAfflictions), "SubtractStatus", new Type[] { typeof(STATUSTYPE), typeof(float), typeof(bool), typeof(bool) })] internal class SubtractColdMonitorPatch { private static float _logTimer = 10f; public static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float amount, bool fromRPC, bool decreasedNaturally) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)statusType != 2) { return; } Character character = __instance.character; if ((Object)(object)character == (Object)null || !character.IsLocal || !HeatEmissionMultiplierPatch.InsideHeatEmission || !HeatEmissionMultiplierPatch.IsLocalSource || !Plugin.EnableWarmthReduction.Value || DayNightTracker.IsDaytime) { return; } float num = amount; amount = 0f; _logTimer += Time.deltaTime; if (_logTimer >= 10f) { _logTimer = 0f; float currentStatus = __instance.GetCurrentStatus((STATUSTYPE)2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [HeatEmission] BLOCKED: subtract {num:F4} → 0, coldVal={currentStatus:F3}"); } } } } [HarmonyPatch(typeof(StatusField), "Update")] internal class StatusFieldColdPatch { private static float _logTimer = 10f; [HarmonyPrefix] public static void Prefix(StatusField __instance, out float __state) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 __state = __instance.statusAmountPerSecond; if ((int)__instance.statusType != 2 || __instance.statusAmountPerSecond >= 0f || !Plugin.EnableWarmthReduction.Value || DayNightTracker.IsDaytime) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || LanternHelper.FindLitLanternSlot(localCharacter) == null) { return; } float num = 0f; __instance.statusAmountPerSecond *= num; _logTimer += Time.deltaTime; if (_logTimer >= 10f) { _logTimer = 0f; string arg = (((Object)(object)((Component)__instance).gameObject != (Object)null) ? ((Object)((Component)__instance).gameObject).name : "null"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[DEBUG] [StatusField] BLOCKED: obj='{arg}', rate {__state:F4} → 0, radius={__instance.radius:F1}"); } } } [HarmonyPostfix] public static void Postfix(StatusField __instance, float __state) { __instance.statusAmountPerSecond = __state; } } internal static class ShootZombiesPerformanceFix { private static readonly HashSet _processedZombies = new HashSet(); private static readonly Dictionary _zombieCharCache = new Dictionary(); private static MethodInfo _removeZombieMethod; private static bool _zombieReflectionInit; private static bool _applied; internal static void Apply(Harmony harmony) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown if (_applied) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(MushroomZombie), "Update", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(ZombieDeathPatch), "Postfix", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo2 != null) { harmony.Unpatch((MethodBase)methodInfo, methodInfo2); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(ShootZombiesPerformanceFix), "OptimizedZombiePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PerfFix] Replaced SZ.ZombieDeathPatch — removed redundant reflection"); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[PerfFix] Cannot replace SZ.ZombieDeathPatch " + $"(update={methodInfo != null}, postfix={methodInfo2 != null})")); } } _applied = true; } catch (Exception arg) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)$"[PerfFix] ShootZombiesPerformanceFix.Apply failed: {arg}"); } } } private static void OptimizedZombiePostfix(MushroomZombie __instance) { if ((Object)(object)__instance == (Object)null) { return; } try { GameObject gameObject = ((Component)__instance).gameObject; if ((Object)(object)gameObject == (Object)null || _processedZombies.Contains(gameObject)) { return; } int instanceID = ((Object)__instance).GetInstanceID(); if (!_zombieCharCache.TryGetValue(instanceID, out var value) || (Object)(object)value == (Object)null) { value = ((Component)__instance).GetComponent(); if ((Object)(object)value == (Object)null) { return; } _zombieCharCache[instanceID] = value; } if (!((Object)(object)value.data != (Object)null) || !value.data.dead) { return; } _processedZombies.Add(gameObject); _zombieCharCache.Remove(instanceID); if (!((Object)(object)gameObject != (Object)null)) { return; } _processedZombies.Remove(gameObject); RemoveZombieSafe(gameObject); if (PhotonNetwork.IsMasterClient) { PhotonView component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { PhotonNetwork.Destroy(component); } else { PhotonNetwork.Destroy(gameObject); } } } catch { } } private static void RemoveZombieSafe(GameObject go) { if (!_zombieReflectionInit) { _zombieReflectionInit = true; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType("ZombieSpawner") ?? assembly.GetType("ShootZombies.ZombieSpawner"); if (type != null) { _removeZombieMethod = type.GetMethod("RemoveZombie", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[PerfFix] ZombieSpawner.RemoveZombie resolved: {_removeZombieMethod != null}"); } break; } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[PerfFix] RemoveZombieSafe reflection failed: " + ex.Message)); } } } _removeZombieMethod?.Invoke(null, new object[1] { go }); } } }