Decompiled source of Lantern ShootZombies Night v0.2.0
Lantern_ShootZombies_Night.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
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 <RecallRoutine>d__1 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private BugleSFX[] <all>5__2; private int <destroyedPhase1>5__3; private List<int> <pending>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RecallRoutine>d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <all>5__2 = null; <pending>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; <all>5__2 = Object.FindObjectsByType<BugleSFX>((FindObjectsSortMode)0); <destroyedPhase1>5__3 = 0; <pending>5__4 = new List<int>(); BugleSFX[] array = <all>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); <destroyedPhase1>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); <pending>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 (<pending>5__4.Count == 0) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[BugleRecall] done, destroyed={<destroyedPhase1>5__3}/{<all>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 <pending>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 = <destroyedPhase1>5__3 + num; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[BugleRecall] done, destroyed={num3}/{<all>5__2.Length} (phase1={<destroyedPhase1>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(<RecallRoutine>d__1))] private static IEnumerator RecallRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RecallRoutine>d__1(0); } } internal static class ConfigStepHelper { public static ConfigEntry<float> WithStep(this ConfigEntry<float> 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<float> entry) { float num = Mathf.Round(value / step) * step; ConfigDescription description = ((ConfigEntryBase)entry).Description; if (((description != null) ? description.AcceptableValues : null) is AcceptableValueRange<float> 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 ? " <color=#FF6666>(暗)</color>" : " <color=#FF6666>(Dark)</color>"); } return $"<color={text2}>D{CurrentDay} {periodName} {text}</color>{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<Guid> _warnedMissingGuids = new HashSet<Guid>(); private static readonly HashSet<int> _warnedNoPermissionViewIds = new HashSet<int>(); 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<Candidate> 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<Lantern>((FindObjectsSortMode)0); foreach (Lantern val in array) { if (!((Object)(object)val == (Object)null) && !LanternHelper.IsSpecialLantern(val)) { num++; } } return num; } private static List<Candidate> CollectPlayerLanterns(Character character) { List<Candidate> list = new List<Candidate>(); 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<BackpackData>((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<Candidate> 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<Lantern>(); if (!((Object)(object)component == (Object)null) && !LanternHelper.IsSpecialLantern(component)) { float fuel = 0f; FloatItemData val2 = default(FloatItemData); if (val.data.TryGetDataEntry<FloatItemData>((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<byte>.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<BackpackData>((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<BackpackData>((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<BugleSFX>((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<Item>(); 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<FloatItemData>((DataEntryKey)10, ref val4)) { val4.Value = 0f; } else { FloatItemData val5 = component.data.RegisterNewEntry<FloatItemData>((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<Lantern>(); 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<PhotonView>(); 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<Item>(); 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<PhotonView>(); 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<Lantern>(); 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<Canvas>(); obj.renderMode = (RenderMode)2; obj.sortingOrder = 100; RectTransform component = _canvasObj.GetComponent<RectTransform>(); 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<TextMeshProUGUI>(); ((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<RectTransform>(); 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<TextMeshProUGUI>((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<int, SlotCacheEntry> _slotCacheById = new Dictionary<int, SlotCacheEntry>(); private const float CacheGracePeriod = 0.8f; private static bool _cacheHitLogged; private static float _reserveWarmth; private static readonly Dictionary<int, float> _fuelSyncTime = new Dictionary<int, float>(); private static readonly Dictionary<int, float> _fuelSyncValue = new Dictionary<int, float>(); private const float FuelSyncInterval = 2f; private const float FuelSyncThreshold = 0.05f; private static readonly Dictionary<string, float> _drainSources = new Dictionary<string, float>(); 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<Lantern>(); 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<FloatItemData>((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<Lantern>(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry<FloatItemData>((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<Lantern>(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry<FloatItemData>((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<Lantern>(); if ((Object)(object)component == (Object)null) { return false; } FloatItemData val4 = default(FloatItemData); if (!val3.data.TryGetDataEntry<FloatItemData>((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<BackpackData>((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<BackpackData>((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<Lantern>(); 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<PhotonView>(); 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<int, SlotCacheEntry> 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<Lantern>(); 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<Lantern>((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { Item component = ((Component)array[i]).GetComponent<Item>(); 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<BoolItemData>((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<PlayerConnectionLog>(); 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<RectTransform>(); _rootRect.sizeDelta = new Vector2(panelW, panelH); ((Graphic)_root.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.6f); _canvasGroup = _root.AddComponent<CanvasGroup>(); _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<RectTransform>(); obj2.anchoredPosition = new Vector2(0f, num); obj2.sizeDelta = new Vector2(panelW - 12f, barH); ((Graphic)obj.AddComponent<Image>()).color = new Color(0.2f, 0.2f, 0.2f, 0.8f); GameObject val2 = CreateChild(obj.transform, "FuelBarFill"); RectTransform val3 = val2.AddComponent<RectTransform>(); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.sizeDelta = Vector2.zero; val3.anchoredPosition = Vector2.zero; _fuelBarFillRect = val3; _fuelBarFill = val2.AddComponent<Image>(); ((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<FloatItemData>((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("<color=#FFFFFF>{0} {1:F2}x</color>", isChinese ? "回暖" : "Warm", value2); string text4 = (Mathf.Approximately(fuelDrainMultiplier, 1f) ? "" : string.Format(" <color=#FF6666>| {0} {1:F1}x</color>", 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 = " <color=#00FFFF>←" + restoreSourceName + text6 + "</color>"; } 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("<color=#FFA500>{0} {1:F1}s</color>", isChinese ? "备用" : "Rsv", reserveWarmth) : ("<color=#888888>" + (isChinese ? "备用" : "Rsv") + " 0</color>")); } 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 ? "<color=#88FFAA>回血中…</color>" : "<color=#88FFAA>Refill…</color>"); } 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 ? $"<color=#00FF88>容Lv{capacityLevel}({text13}) 效Lv{efficiencyLevel}({text14}) ★{points}</color>" : $"<color=#00FF88>Cap:{capacityLevel}({text13}) Eff:{efficiencyLevel}({text14}) ★{points}</color>"); } 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<RectTransform>(); obj2.anchoredPosition = anchoredPos; obj2.sizeDelta = size; TextMeshProUGUI val = obj.AddComponent<TextMeshProUGUI>(); 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<Lantern>(); 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, object>)(object)val).ContainsKey((object)"LSN.UPts") && val[(object)"LSN.UPts"] is int) ? ((int)val[(object)"LSN.UPts"]) : 0); int num2 = ((((Dictionary<object, object>)(object)val).ContainsKey((object)"LSN.UCap") && val[(object)"LSN.UCap"] is int) ? ((int)val[(object)"LSN.UCap"]) : 0); int num3 = ((((Dictionary<object, object>)(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<object, object>)val2).Add((object)"LSN.UPts", (object)num); ((Dictionary<object, object>)val2).Add((object)"LSN.UCap", (object)num2); ((Dictionary<object, object>)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, object>)(object)customProperties).TryGetValue((object)"LSN.UPts", out object value) && value is int) { _points = (int)value; } if (((Dictionary<object, object>)(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, object>)(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)((P