Decompiled source of Lantern ShootZombies Night v0.2.0

Lantern_ShootZombies_Night.dll

Decompiled 2 months ago
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