Decompiled source of OrbLights v1.0.0

BepInEx/plugins/OrbLights/OrbLights.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("OrbLights")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("OrbLights")]
[assembly: AssemblyTitle("OrbLights")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace OrbLights
{
	public sealed class OrbInstance : MonoBehaviour
	{
		internal Light Point;

		internal bool Lit = true;

		internal float Distance2;

		private Fireplace fire;

		private ZNetView view;

		private Transform orb;

		private int revision = -1;

		private bool registered;

		private bool requiredFuel;

		private static readonly MethodInfo UpdateFuel = AccessTools.Method(typeof(Fireplace), "UpdateFireplace", (Type[])null, (Type[])null);

		private void Start()
		{
			fire = ((Component)this).GetComponent<Fireplace>();
			view = ((Component)this).GetComponent<ZNetView>();
			orb = ((Component)this).transform.Find("OrbLights_Orb");
			Point = (Object.op_Implicit((Object)(object)orb) ? ((Component)orb).GetComponent<Light>() : null);
			if (Object.op_Implicit((Object)(object)view) && view.IsValid())
			{
				if (Object.op_Implicit((Object)(object)fire))
				{
					((MonoBehaviour)fire).CancelInvoke();
				}
				if (Object.op_Implicit((Object)(object)Plugin.Instance))
				{
					Plugin.Instance.Orbs.Add(this);
					registered = true;
					Refresh(fuelTick: true);
				}
			}
		}

		internal void Refresh(bool fuelTick)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)view) || !view.IsValid())
			{
				if (Object.op_Implicit((Object)(object)Point))
				{
					((Behaviour)Point).enabled = false;
				}
				Lit = false;
				return;
			}
			Settings options = instance.Options;
			if (revision != instance.Revision)
			{
				revision = instance.Revision;
				instance.ApplyRecipe(((Component)this).GetComponent<Piece>());
				if (Object.op_Implicit((Object)(object)orb))
				{
					orb.localScale = Vector3.one * options.F(options.Size, 0.05f, 0.6f);
				}
				if (Object.op_Implicit((Object)(object)Point))
				{
					Point.color = options.LightColor;
					Point.range = options.EffectiveRadius;
					Point.intensity = options.F(options.Intensity, 0f, 4f);
					Point.shadows = (LightShadows)((options.Preset.Value == PerformancePreset.Custom && options.Shadows.Value) ? 2 : 0);
				}
				if (Object.op_Implicit((Object)(object)fire))
				{
					if (options.Fuel.Value != requiredFuel && view.IsOwner())
					{
						view.GetZDO().Set(ZDOVars.s_lastTime, ZNet.instance.GetTime().Ticks);
					}
					requiredFuel = options.Fuel.Value;
					fire.m_infiniteFuel = !options.Fuel.Value;
					fire.m_canRefill = options.Fuel.Value;
					fire.m_secPerFuel = 3600f / options.F(options.BurnRate, 0.05f, 10f);
				}
			}
			if (fuelTick)
			{
				bool flag = false;
				if (options.Rain.Value && Object.op_Implicit((Object)(object)EnvMan.instance) && EnvMan.IsWet())
				{
					float num = default(float);
					bool flag2 = default(bool);
					Cover.GetCoverForPoint(Object.op_Implicit((Object)(object)orb) ? orb.position : ((Component)this).transform.position, ref num, ref flag2, 0.5f);
					flag = !flag2;
				}
				Lit = !flag && (!options.Fuel.Value || view.GetZDO().GetFloat(ZDOVars.s_fuel, 1f) > 0f);
				if (options.Fuel.Value && Object.op_Implicit((Object)(object)fire) && view.IsOwner())
				{
					UpdateFuel.Invoke(fire, null);
				}
				if (Object.op_Implicit((Object)(object)orb) && ((Component)orb).gameObject.activeSelf != Lit)
				{
					((Component)orb).gameObject.SetActive(Lit);
				}
			}
		}

		private void OnDestroy()
		{
			if (registered && Object.op_Implicit((Object)(object)Plugin.Instance))
			{
				Plugin.Instance.Orbs.Remove(this);
			}
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Awake")]
	internal static class FireplaceAwakePatch
	{
		private static void Postfix(Fireplace __instance)
		{
			if (Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<OrbInstance>()))
			{
				((MonoBehaviour)__instance).CancelInvoke();
			}
		}
	}
	[HarmonyPatch(typeof(Fireplace), "IsBurning")]
	internal static class FireplaceBurnPatch
	{
		private static bool Prefix(Fireplace __instance, ref bool __result)
		{
			OrbInstance component = ((Component)__instance).GetComponent<OrbInstance>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return true;
			}
			__result = component.Lit;
			return false;
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Interact")]
	internal static class FireplaceInteractPatch
	{
		private static bool Prefix(Fireplace __instance, ref bool __result)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<OrbInstance>()))
			{
				return true;
			}
			__result = false;
			if (Object.op_Implicit((Object)(object)Plugin.Instance) && Plugin.Instance.Options.Fuel.Value)
			{
				return PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Fireplace), "UseItem")]
	internal static class FireplaceUseItemPatch
	{
		private static bool Prefix(Fireplace __instance, ref bool __result)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<OrbInstance>()))
			{
				return true;
			}
			__result = false;
			if (Object.op_Implicit((Object)(object)Plugin.Instance) && Plugin.Instance.Options.Fuel.Value)
			{
				return PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false);
			}
			return false;
		}
	}
	[BepInPlugin("org.orblights.valheim", "OrbLights", "1.0.0")]
	[BepInDependency("com.jotunn.jotunn", "2.30.2")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "org.orblights.valheim";

		public const string PrefabName = "OrbLights_OrbTorch";

		internal static Plugin Instance;

		internal Settings Options;

		internal Material OrbMaterial;

		internal int Revision;

		internal readonly HashSet<OrbInstance> Orbs = new HashSet<OrbInstance>();

		private readonly List<OrbInstance> candidates = new List<OrbInstance>();

		private readonly List<OrbInstance> active = new List<OrbInstance>();

		private Harmony harmony;

		private Texture2D iconTexture;

		private Sprite icon;

		private DateTime configWrite;

		private bool dirty = true;

		private float nextFuel;

		private float nextLog;

		private float nextConfig;

		private CustomPiece custom;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			Instance = this;
			Options = new Settings(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += Changed;
			harmony = new Harmony("org.orblights.valheim");
			harmony.PatchAll(typeof(Plugin).Assembly);
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			PieceManager.OnPiecesRegistered += PiecesReady;
			SynchronizationManager.OnConfigurationSynchronized += Synchronized;
			((MonoBehaviour)this).StartCoroutine(Schedule());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"OrbLights 1.0.0 initialized; global color controls, centralized light budget, Jotunn required on all peers.");
		}

		private void Changed(object sender, SettingChangedEventArgs e)
		{
			dirty = true;
		}

		private void Synchronized(object sender, ConfigurationSynchronizationEventArgs e)
		{
			Options.ApplyLock();
			dirty = true;
		}

		private void PiecesReady()
		{
			dirty = true;
			ApplyRecipe();
		}

		internal void ApplyRecipe(Piece piece = null)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_017b: 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)
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance) || custom == null)
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)piece))
			{
				piece = custom.PiecePrefab.GetComponent<Piece>();
			}
			List<Requirement> list = new List<Requirement>();
			string[] array = new string[3] { "Wood", "Resin", "SurtlingCore" };
			int[] array2 = new int[3]
			{
				Options.Wood.Value,
				Options.Resin.Value,
				Options.Core.Value
			};
			for (int i = 0; i < 3; i++)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(array[i]);
				if (!Object.op_Implicit((Object)(object)itemPrefab))
				{
					return;
				}
				if (array2[i] > 0)
				{
					list.Add(new Requirement
					{
						m_resItem = itemPrefab.GetComponent<ItemDrop>(),
						m_amount = Mathf.Clamp(array2[i], 0, 100),
						m_recover = true
					});
				}
			}
			piece.m_resources = list.ToArray();
			GameObject val = ((Options.Station.Value == "None") ? null : PrefabManager.Instance.GetPrefab(Options.Station.Value));
			piece.m_craftingStation = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<CraftingStation>() : null);
			PieceCategory? pieceCategory = PieceManager.Instance.GetPieceCategory(Options.Category.Value);
			if (pieceCategory.HasValue)
			{
				piece.m_category = pieceCategory.Value;
			}
		}

		private void Register()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Invalid comparison between Unknown and I4
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Invalid comparison between Unknown and I4
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Expected O, but got Unknown
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Expected O, but got Unknown
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Expected O, but got Unknown
			if (custom != null)
			{
				return;
			}
			try
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab("piece_groundtorch_wood");
				if (!Object.op_Implicit((Object)(object)prefab))
				{
					throw new InvalidOperationException("Vanilla piece_groundtorch_wood is unavailable.");
				}
				Shader val = Shader.Find("Standard") ?? Shader.Find("Custom/Standard") ?? Shader.Find("Custom/StaticRock") ?? Shader.Find("Unlit/Color");
				if (!Object.op_Implicit((Object)(object)val))
				{
					Material val2 = ((IEnumerable<Material>)Resources.FindObjectsOfTypeAll<Material>()).FirstOrDefault((Func<Material, bool>)((Material m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)m.shader) && m.HasProperty("_EmissionColor") && m.HasProperty("_Color")));
					if (Object.op_Implicit((Object)(object)val2))
					{
						val = val2.shader;
					}
				}
				if (!Object.op_Implicit((Object)(object)val) && (int)SystemInfo.graphicsDeviceType == 4)
				{
					Material? obj = (from r in prefab.GetComponentsInChildren<Renderer>(true)
						select r.sharedMaterial).FirstOrDefault((Func<Material, bool>)((Material m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)m.shader)));
					val = ((obj != null) ? obj.shader : null);
				}
				if (!Object.op_Implicit((Object)(object)val) || (!val.isSupported && (int)SystemInfo.graphicsDeviceType != 4))
				{
					throw new InvalidOperationException("No supported orb shader found; refusing to register a broken piece.");
				}
				OrbMaterial = new Material(val)
				{
					name = "OrbLights_SharedOrb"
				};
				if (OrbMaterial.HasProperty("_MainTex"))
				{
					OrbMaterial.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
				}
				if (OrbMaterial.HasProperty("_Glossiness"))
				{
					OrbMaterial.SetFloat("_Glossiness", 0.35f);
				}
				if (OrbMaterial.HasProperty("_Metallic"))
				{
					OrbMaterial.SetFloat("_Metallic", 0f);
				}
				using (Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("OrbLights.icon.png"))
				{
					using MemoryStream memoryStream = new MemoryStream();
					if (stream == null)
					{
						throw new InvalidOperationException("Embedded Orb Torch icon missing.");
					}
					stream.CopyTo(memoryStream);
					iconTexture = new Texture2D(2, 2, (TextureFormat)4, false);
					if (!ImageConversion.LoadImage(iconTexture, memoryStream.ToArray()))
					{
						throw new InvalidOperationException("Could not decode icon.");
					}
					icon = Sprite.Create(iconTexture, new Rect(0f, 0f, (float)((Texture)iconTexture).width, (float)((Texture)iconTexture).height), new Vector2(0.5f, 0.5f), 100f);
				}
				List<RequirementConfig> list = new List<RequirementConfig>();
				AddCost(list, "Wood", Options.Wood.Value);
				AddCost(list, "Resin", Options.Resin.Value);
				AddCost(list, "SurtlingCore", Options.Core.Value);
				PieceConfig val3 = new PieceConfig();
				val3.Name = "Orb Torch";
				val3.Description = "A quiet glowing orb on a wooden stand.";
				val3.PieceTable = "Hammer";
				val3.Category = Options.Category.Value;
				val3.Usage = new string[2] { "Furniture", "Lighting" };
				val3.CraftingStation = ((Options.Station.Value == "None") ? "" : Options.Station.Value);
				val3.Icon = icon;
				val3.Requirements = list.ToArray();
				custom = new CustomPiece("OrbLights_OrbTorch", "piece_groundtorch_wood", val3);
				GameObject piecePrefab = custom.PiecePrefab;
				if (list.Count == 0)
				{
					piecePrefab.GetComponent<Piece>().m_resources = Array.Empty<Requirement>();
				}
				if (Options.Station.Value == "None")
				{
					piecePrefab.GetComponent<Piece>().m_craftingStation = null;
				}
				Sanitize(piecePrefab);
				if (!PieceManager.Instance.AddPiece(custom))
				{
					throw new InvalidOperationException("Jotunn rejected piece registration.");
				}
				dirty = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Registered OrbLights_OrbTorch with shader " + ((Object)val).name + "; vanilla torch unchanged."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("OrbLights registration failed: " + ex));
			}
		}

		private static void AddCost(List<RequirementConfig> costs, string item, int amount)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (amount > 0)
			{
				costs.Add(new RequirementConfig(item, Mathf.Clamp(amount, 0, 100), 0, true));
			}
		}

		private unsafe void Sanitize(GameObject root)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Expected O, but got Unknown
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			Fireplace component = root.GetComponent<Fireplace>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				throw new InvalidOperationException("Standing torch has no Fireplace; prefab layout changed.");
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(0f, 1.35f, 0f);
			Light componentInChildren = root.GetComponentInChildren<Light>(true);
			if (Object.op_Implicit((Object)(object)componentInChildren))
			{
				val = root.transform.InverseTransformPoint(((Component)componentInChildren).transform.position);
			}
			else if (Object.op_Implicit((Object)(object)component.m_enabledObject))
			{
				val = root.transform.InverseTransformPoint(component.m_enabledObject.transform.position);
			}
			GameObject[] array = (GameObject[])(object)new GameObject[8] { component.m_enabledObject, component.m_enabledObjectLow, component.m_enabledObjectHigh, component.m_fullObject, component.m_halfObject, component.m_emptyObject, component.m_playerBaseObject, component.m_snowMelter };
			foreach (GameObject val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2) && (Object)(object)val2 != (Object)(object)root)
				{
					val2.SetActive(false);
				}
			}
			ParticleSystem[] componentsInChildren = root.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem obj in componentsInChildren)
			{
				obj.Stop(true, (ParticleSystemStopBehavior)0);
				((Component)obj).gameObject.SetActive(false);
			}
			Component[] componentsInChildren2 = root.GetComponentsInChildren<Component>(true);
			foreach (Component val3 in componentsInChildren2)
			{
				if (val3 is Light || val3 is AudioSource || val3 is ParticleSystem || val3 is ParticleSystemRenderer || val3 is Animator || val3 is Animation || val3 is EffectArea || val3 is SmokeSpawner || val3 is LightLod || ((object)val3).GetType().Name == "LightFlicker" || ((object)val3).GetType().Name == "ZSFX")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			component.m_enabledObject = new GameObject("OrbLights_FuelState");
			component.m_enabledObject.transform.SetParent(root.transform, false);
			component.m_enabledObjectLow = (component.m_enabledObjectHigh = null);
			component.m_fullObject = (component.m_halfObject = (component.m_emptyObject = null));
			component.m_playerBaseObject = (component.m_snowMelter = null);
			component.m_smokeSpawner = null;
			component.m_fuelAddedEffects = new EffectList();
			component.m_toggleOnEffects = new EffectList();
			component.m_fireworkItemList = Array.Empty<FireworkItem>();
			component.m_igniteInterval = 0f;
			component.m_igniteChance = 0f;
			component.m_firePrefab = null;
			component.m_disableCoverCheck = true;
			component.m_canTurnOff = false;
			component.m_startFuel = 1f;
			component.m_maxFuel = 10f;
			component.m_name = "Orb Torch";
			GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)0);
			((Object)obj2).name = "OrbLights_Orb";
			obj2.transform.SetParent(root.transform, false);
			obj2.transform.localPosition = val;
			obj2.transform.localScale = Vector3.one * Options.F(Options.Size, 0.05f, 0.6f);
			Object.DestroyImmediate((Object)(object)obj2.GetComponent<Collider>());
			MeshRenderer component2 = obj2.GetComponent<MeshRenderer>();
			((Renderer)component2).sharedMaterial = OrbMaterial;
			((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component2).receiveShadows = false;
			Light obj3 = obj2.AddComponent<Light>();
			obj3.type = (LightType)2;
			obj3.shadows = (LightShadows)0;
			((Behaviour)obj3).enabled = false;
			root.AddComponent<OrbInstance>();
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			Vector3 val4 = val;
			logger.LogInfo((object)("Orb offset from cloned torch light: " + ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString()));
		}

		private void ApplyMaterial()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)OrbMaterial))
			{
				Color val = (Options.Matches.Value ? Options.LightColor : Color.white);
				if (OrbMaterial.HasProperty("_Color"))
				{
					OrbMaterial.SetColor("_Color", val);
				}
				if (OrbMaterial.HasProperty("_BaseColor"))
				{
					OrbMaterial.SetColor("_BaseColor", val);
				}
				if (OrbMaterial.HasProperty("_EmissionColor"))
				{
					OrbMaterial.EnableKeyword("_EMISSION");
					OrbMaterial.SetColor("_EmissionColor", val * Options.F(Options.Emission, 0f, 5f));
				}
			}
		}

		private IEnumerator Schedule()
		{
			WaitForSecondsRealtime wait = new WaitForSecondsRealtime(0.1f);
			int tick = 0;
			while (true)
			{
				int num = tick + 1;
				tick = num;
				if (num % 5 == 0)
				{
					Refresh();
				}
				if (Options.EffectiveFlicker)
				{
					foreach (OrbInstance item in active)
					{
						if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)item.Point))
						{
							item.Point.intensity = Options.F(Options.Intensity, 0f, 4f) * (1f + Mathf.Sin(Time.unscaledTime * Options.F(Options.FlickerSpeed, 0.1f, 5f) * 2f * MathF.PI + (float)((Object)item).GetInstanceID()) * Options.F(Options.FlickerAmount, 0f, 0.5f));
						}
					}
				}
				yield return wait;
			}
		}

		private void Refresh()
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: 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)
			if (Time.unscaledTime >= nextConfig)
			{
				nextConfig = Time.unscaledTime + 2f;
				try
				{
					DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
					if (configWrite != default(DateTime) && lastWriteTimeUtc != configWrite)
					{
						((BaseUnityPlugin)this).Config.Reload();
					}
					configWrite = lastWriteTimeUtc;
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Config reload failed: " + ex.Message));
				}
			}
			if (dirty)
			{
				dirty = false;
				Revision++;
				ApplyMaterial();
				ApplyRecipe();
			}
			bool flag = Time.unscaledTime >= nextFuel;
			if (flag)
			{
				nextFuel = Time.unscaledTime + 2f;
			}
			candidates.Clear();
			Player localPlayer = Player.m_localPlayer;
			float num = Options.EffectiveDistance * Options.EffectiveDistance;
			foreach (OrbInstance orb in Orbs)
			{
				if (!Object.op_Implicit((Object)(object)orb))
				{
					continue;
				}
				orb.Refresh(flag);
				if (Object.op_Implicit((Object)(object)localPlayer) && ((Behaviour)orb).isActiveAndEnabled && orb.Lit && Object.op_Implicit((Object)(object)orb.Point))
				{
					Vector3 val = ((Component)orb).transform.position - ((Component)localPlayer).transform.position;
					orb.Distance2 = ((Vector3)(ref val)).sqrMagnitude;
					if (orb.Distance2 <= num)
					{
						candidates.Add(orb);
					}
				}
			}
			candidates.Sort(delegate(OrbInstance a, OrbInstance b)
			{
				int num4 = a.Distance2.CompareTo(b.Distance2);
				return (num4 == 0) ? ((Object)a).GetInstanceID().CompareTo(((Object)b).GetInstanceID()) : num4;
			});
			foreach (OrbInstance item in active)
			{
				if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)item.Point))
				{
					((Behaviour)item.Point).enabled = false;
				}
			}
			active.Clear();
			int num2 = Math.Min(Options.EffectiveMaximum, candidates.Count);
			for (int num3 = 0; num3 < num2; num3++)
			{
				OrbInstance orbInstance = candidates[num3];
				((Behaviour)orbInstance.Point).enabled = true;
				active.Add(orbInstance);
			}
			if (Options.Debug.Value && Time.unscaledTime >= nextLog)
			{
				nextLog = Time.unscaledTime + 30f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"OrbLights registered={Orbs.Count}, nearby={candidates.Count}, active={active.Count}, budget={Options.EffectiveMaximum}");
			}
		}

		private void OnDestroy()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			PieceManager.OnPiecesRegistered -= PiecesReady;
			SynchronizationManager.OnConfigurationSynchronized -= Synchronized;
			((BaseUnityPlugin)this).Config.SettingChanged -= Changed;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			foreach (OrbInstance orb in Orbs)
			{
				if (Object.op_Implicit((Object)(object)orb) && Object.op_Implicit((Object)(object)orb.Point))
				{
					((Behaviour)orb.Point).enabled = false;
				}
			}
			Orbs.Clear();
			active.Clear();
			candidates.Clear();
			if (Object.op_Implicit((Object)(object)OrbMaterial))
			{
				Object.Destroy((Object)(object)OrbMaterial);
			}
			if (Object.op_Implicit((Object)(object)icon))
			{
				Object.Destroy((Object)(object)icon);
			}
			if (Object.op_Implicit((Object)(object)iconTexture))
			{
				Object.Destroy((Object)(object)iconTexture);
			}
			Instance = null;
		}
	}
	public enum PerformancePreset
	{
		Potato,
		Performance,
		Balanced,
		Quality,
		Custom
	}
	public enum ColorPreset
	{
		Custom,
		WarmWhite,
		NeutralWhite,
		CoolWhite,
		Red,
		Orange,
		Yellow,
		Green,
		Cyan,
		Blue,
		Purple,
		Pink
	}
	public sealed class Settings
	{
		public readonly ConfigEntry<bool> EnableLight;

		public readonly ConfigEntry<bool> Matches;

		public readonly ConfigEntry<bool> Flicker;

		public readonly ConfigEntry<bool> Shadows;

		public readonly ConfigEntry<bool> EmissionOnly;

		public readonly ConfigEntry<bool> Fuel;

		public readonly ConfigEntry<bool> Rain;

		public readonly ConfigEntry<bool> Debug;

		public readonly ConfigEntry<int> Red;

		public readonly ConfigEntry<int> Green;

		public readonly ConfigEntry<int> Blue;

		public readonly ConfigEntry<int> Maximum;

		public readonly ConfigEntry<int> Wood;

		public readonly ConfigEntry<int> Resin;

		public readonly ConfigEntry<int> Core;

		public readonly ConfigEntry<float> Intensity;

		public readonly ConfigEntry<float> Radius;

		public readonly ConfigEntry<float> Size;

		public readonly ConfigEntry<float> Emission;

		public readonly ConfigEntry<float> FlickerAmount;

		public readonly ConfigEntry<float> FlickerSpeed;

		public readonly ConfigEntry<float> Distance;

		public readonly ConfigEntry<float> BurnRate;

		public readonly ConfigEntry<string> Category;

		public readonly ConfigEntry<string> Station;

		public readonly ConfigEntry<PerformancePreset> Preset;

		public readonly ConfigEntry<ColorPreset> ColorChoice;

		public readonly ConfigEntry<bool> Lock;

		private readonly ConfigFile file;

		public Color LightColor => (Color)(ColorChoice.Value switch
		{
			ColorPreset.WarmWhite => RGB(255, 225, 185), 
			ColorPreset.NeutralWhite => Color.white, 
			ColorPreset.CoolWhite => RGB(195, 220, 255), 
			ColorPreset.Red => RGB(255, 50, 40), 
			ColorPreset.Orange => RGB(255, 135, 40), 
			ColorPreset.Yellow => RGB(255, 235, 65), 
			ColorPreset.Green => RGB(75, 255, 90), 
			ColorPreset.Cyan => RGB(65, 245, 255), 
			ColorPreset.Blue => RGB(75, 115, 255), 
			ColorPreset.Purple => RGB(165, 80, 255), 
			ColorPreset.Pink => RGB(255, 110, 195), 
			_ => RGB(Red.Value, Green.Value, Blue.Value), 
		});

		public float EffectiveRadius
		{
			get
			{
				if (Preset.Value != PerformancePreset.Custom)
				{
					return (Preset.Value == PerformancePreset.Performance) ? 8 : ((Preset.Value == PerformancePreset.Quality) ? 16 : 12);
				}
				return F(Radius, 1f, 25f);
			}
		}

		public float EffectiveDistance
		{
			get
			{
				if (Preset.Value != PerformancePreset.Custom)
				{
					return (Preset.Value == PerformancePreset.Performance) ? 25 : ((Preset.Value == PerformancePreset.Quality) ? 60 : 40);
				}
				return F(Distance, 5f, 100f);
			}
		}

		public int EffectiveMaximum
		{
			get
			{
				if (!EmissionOnly.Value && EnableLight.Value && Preset.Value != PerformancePreset.Potato)
				{
					if (Preset.Value != PerformancePreset.Custom)
					{
						if (Preset.Value != PerformancePreset.Performance)
						{
							if (Preset.Value != PerformancePreset.Quality)
							{
								return 32;
							}
							return 64;
						}
						return 16;
					}
					return Mathf.Clamp(Maximum.Value, 0, 128);
				}
				return 0;
			}
		}

		public bool EffectiveFlicker
		{
			get
			{
				if (Preset.Value == PerformancePreset.Custom)
				{
					return Flicker.Value;
				}
				return false;
			}
		}

		public Settings(ConfigFile config)
		{
			file = config;
			Lock = Bind("7. Multiplayer", "LockConfiguration", value: true, "Lock visual and performance settings to the server. When false these settings are client-local. Recipe and fuel settings always remain server-authoritative. Restart all peers after changing.", null, true);
			EnableLight = Bind("1. General", "EnableLight", value: true, "Enable real-time point lights; the orb remains visible when disabled.");
			Category = Bind("1. General", "PieceCategory", "Furniture", "Hammer category. Restart required.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Furniture", "Misc", "Building", "Crafting" }));
			Wood = Number("2. Recipe", "Wood", 2, 0, 100, "Wood cost; zero removes this ingredient. Restart required.");
			Resin = Number("2. Recipe", "Resin", 1, 0, 100, "Resin cost; zero removes this ingredient. Restart required.");
			Core = Number("2. Recipe", "SurtlingCore", 1, 0, 100, "Surtling Core cost; zero removes this ingredient. Restart required.");
			Station = Bind("2. Recipe", "CraftingStationRequirement", "None", "Required station. Restart required.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "None", "piece_workbench", "forge", "piece_stonecutter", "piece_artisanstation", "piece_magetable" }));
			ColorChoice = Bind("3. Light Color", "ColorPreset", ColorPreset.Custom, "Global light color preset. Custom uses the RGB values below.");
			Red = Number("3. Light Color", "Red", 255, 0, 255, "Custom red channel.");
			Green = Number("3. Light Color", "Green", 225, 0, 255, "Custom green channel.");
			Blue = Number("3. Light Color", "Blue", 185, 0, 255, "Custom blue channel.");
			Intensity = Number("4. Light Appearance", "Intensity", 1.2f, 0f, 4f, "Point-light intensity.");
			Radius = Number("4. Light Appearance", "LightRadius", 12f, 1f, 25f, "Point-light radius in metres. Presets override this unless Custom.");
			Size = Number("4. Light Appearance", "OrbSize", 0.22f, 0.05f, 0.6f, "Orb diameter in metres.");
			Emission = Number("4. Light Appearance", "OrbEmissionStrength", 2f, 0f, 5f, "Material emission strength; bloom depends on game settings.");
			Matches = Bind("4. Light Appearance", "OrbMatchesLightColor", value: false, "Tint the visible orb to the selected light color; otherwise white.");
			Flicker = Bind("4. Light Appearance", "EnableFlicker", value: false, "Enable centrally sampled flicker on active lights only (10 Hz).");
			FlickerAmount = Number("4. Light Appearance", "FlickerAmount", 0.1f, 0f, 0.5f, "Maximum fractional intensity variation.");
			FlickerSpeed = Number("4. Light Appearance", "FlickerSpeed", 1f, 0.1f, 5f, "Flicker frequency in Hz.");
			Preset = Bind("5. Performance", "PerformancePreset", PerformancePreset.Balanced, "Potato: emission only. Performance: 8m/25m/16 lights. Balanced: 12m/40m/32. Quality: 16m/60m/64. Custom uses configured radius/distance/count/shadows/flicker.");
			EmissionOnly = Bind("5. Performance", "EmissionOnlyMode", value: false, "Always disables point lights, including with a preset selected.");
			Shadows = Bind("5. Performance", "EnableShadows", value: false, "Expensive point-light shadows. Only honored in Custom.");
			Distance = Number("5. Performance", "MaximumRenderDistance", 40f, 5f, 100f, "Maximum point-light distance from local player in metres. Custom only; emissive geometry is unaffected.");
			Maximum = Number("5. Performance", "MaximumActiveLights", 32, 0, 128, "Maximum nearby point lights, nearest first. Custom only; zero disables all.");
			Fuel = Bind("6. Fuel", "RequireFuel", value: false, "Use vanilla Resin refueling and persistent fuel storage. Starts with one Resin unit. When false, no fuel is burned.");
			BurnRate = Number("6. Fuel", "FuelBurnRate", 1f, 0.05f, 10f, "Resin units per real hour while loaded and lit. Fuel amount persists; consumption resumes on load without charging for offline time.");
			Rain = Bind("6. Fuel", "ExtinguishInRain", value: false, "Rain extinguishes uncovered orbs. Roofed orbs stay lit.");
			Debug = Bind("8. Debugging", "EnableDebugLogging", value: false, "Log active and registered light counts every 30 seconds.", null, false);
		}

		private ConfigEntry<T> Bind<T>(string section, string key, T value, string description, AcceptableValueBase range = null, bool? sync = null)
		{
			//IL_0015: 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_0029: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return file.Bind<T>(section, key, value, new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = (sync ?? true)
			} }));
		}

		public void ApplyLock()
		{
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in file)
			{
				switch (item.Key.Section)
				{
				default:
					if (!(item.Key.Key == "EnableLight"))
					{
						continue;
					}
					break;
				case "3. Light Color":
				case "4. Light Appearance":
				case "5. Performance":
					break;
				}
				object[] tags = item.Value.Description.Tags;
				foreach (object obj in tags)
				{
					ConfigurationManagerAttributes val = (ConfigurationManagerAttributes)((obj is ConfigurationManagerAttributes) ? obj : null);
					if (val != null)
					{
						val.IsAdminOnly = Lock.Value;
					}
				}
			}
		}

		private ConfigEntry<int> Number(string s, string k, int v, int min, int max, string d)
		{
			return Bind(s, k, v, d, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max));
		}

		private ConfigEntry<float> Number(string s, string k, float v, float min, float max, string d)
		{
			return Bind(s, k, v, d, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max));
		}

		public static float Safe(float value, float fallback, float min, float max)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Mathf.Clamp(value, min, max);
			}
			return fallback;
		}

		public float F(ConfigEntry<float> c, float min, float max)
		{
			return Safe(c.Value, (float)((ConfigEntryBase)c).DefaultValue, min, max);
		}

		private static Color RGB(int r, int g, int b)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)Mathf.Clamp(r, 0, 255) / 255f, (float)Mathf.Clamp(g, 0, 255) / 255f, (float)Mathf.Clamp(b, 0, 255) / 255f);
		}
	}
}