Decompiled source of SeasonalSLSBridge v1.4.0

plugins/SeasonalSLSBridge.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using StarLevelSystem;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SeasonalSLSBridge")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.3.0")]
[assembly: AssemblyInformationalVersion("1.4.3")]
[assembly: AssemblyProduct("SeasonalSLSBridge")]
[assembly: AssemblyTitle("SeasonalSLSBridge")]
[assembly: AssemblyVersion("1.4.3.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 SeasonalSLSBridge
{
	internal sealed class EcologyConfig
	{
		private sealed class PrefabProfile
		{
			internal ConfigEntry<string> Affinity;

			internal readonly Dictionary<string, ConfigEntry<string>> Seasons = new Dictionary<string, ConfigEntry<string>>();
		}

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<bool> LogDecisions;

		private readonly Dictionary<string, ConfigEntry<string>> _biomes = new Dictionary<string, ConfigEntry<string>>();

		private readonly Dictionary<string, ConfigEntry<string>> _affinities = new Dictionary<string, ConfigEntry<string>>();

		private readonly Dictionary<string, ConfigEntry<string>> _limits = new Dictionary<string, ConfigEntry<string>>();

		private readonly Dictionary<string, PrefabProfile> _prefabs = new Dictionary<string, PrefabProfile>(StringComparer.OrdinalIgnoreCase);

		private readonly string _registryError;

		internal EcologyConfig(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Ecology", "Enabled", true, "Enable contextual ecology for NEW creatures. False uses the original 1.2 seasonal probability profiles.");
			LogDecisions = config.Bind<bool>("Diagnostics", "LogEcologyDecisions", false, "Log birth context, resolved affinity and actual selection percentages (including secondary weighting and global gate).");
			string[] seasons = EcologyPolicy.Seasons;
			foreach (string text in seasons)
			{
				string[] biomes = EcologyPolicy.Biomes;
				foreach (string text2 in biomes)
				{
					_biomes.Add(text + "/" + text2, config.Bind<string>("Ecology.Biome." + text, text2, EcologyDefaults.BiomePatch(text, text2), "Overrides to the seasonal baseline. Modifier=number; Modifier=number. Primary entries are absolute percentages; others are conditional weights. Empty inherits. Zero disables in this layer; later explicit overrides may enable it."));
				}
				foreach (CreatureAffinity value in Enum.GetValues(typeof(CreatureAffinity)))
				{
					_affinities.Add(text + "/" + value, config.Bind<string>("Ecology.Affinity." + text, value.ToString(), EcologyDefaults.AffinityPatch(text, value), "Explicit overrides after the biome profile. One affinity per creature; no stacking. Same percentage/weight semantics as biome profiles."));
				}
			}
			seasons = EcologyPolicy.Biomes;
			foreach (string text3 in seasons)
			{
				_limits.Add(text3, config.Bind<string>("Ecology.HabitatLimits", text3, EcologyDefaults.HabitatLimits(text3), "Upper limits after biome and affinity rules, before explicit prefab overrides. Only Poison, PoisonNova, ElementalChaos. Natural Poison affinity bypasses Poison/PoisonNova limits, but never the Chaos limit. Same percentage/weight units as the seasonal pool."));
			}
			foreach (string item in from n in config.Bind<string>("Ecology", "PrefabProfiles", "", "Optional comma-separated exact prefab names. Restart after editing this list; configure the generated [Prefab.Name] sections. Unlisted creatures use built-in or Neutral affinity.").Value.Split(',')
				select n.Trim() into n
				where n.Length > 0
				select n)
			{
				if (item.Any((char c) => !char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.') || _prefabs.ContainsKey(item))
				{
					_registryError = "Invalid or duplicate Ecology.PrefabProfiles entry: " + item;
					continue;
				}
				PrefabProfile prefabProfile = new PrefabProfile
				{
					Affinity = config.Bind<string>("Prefab." + item, "Affinity", "Default", "Default keeps the built-in affinity. Or choose exactly one: Neutral, Cold, Fire, Poison, Spirit, Arcane.")
				};
				seasons = EcologyPolicy.Seasons;
				foreach (string text4 in seasons)
				{
					prefabProfile.Seasons.Add(text4, config.Bind<string>("Prefab." + item, text4, "", "Final explicit overrides for this prefab and season, after habitat limits. Modifier=number; Modifier=number. Empty inherits. Does not override creature filters."));
				}
				_prefabs.Add(item, prefabProfile);
			}
		}

		internal bool TryResolve(string season, string biome, string prefab, IDictionary<string, float> baseline, out CreatureAffinity affinity, out Dictionary<string, float> chances, out string error)
		{
			affinity = EcologyDefaults.Affinity(prefab);
			chances = null;
			error = _registryError;
			if (error != null)
			{
				return false;
			}
			if (!EcologyPolicy.Seasons.Contains(season) || !EcologyPolicy.Biomes.Contains(biome))
			{
				error = "Unknown season or biome; no ecological roll performed.";
				return false;
			}
			string prefabPatch = "";
			if (_prefabs.TryGetValue(prefab, out var value))
			{
				if (!string.Equals(value.Affinity.Value.Trim(), "Default", StringComparison.OrdinalIgnoreCase) && !EcologyPolicy.TryAffinity(value.Affinity.Value, out affinity))
				{
					error = "Unknown affinity for " + prefab + ": " + value.Affinity.Value;
					return false;
				}
				prefabPatch = value.Seasons[season].Value;
			}
			return EcologyPolicy.TryResolve(season, biome, affinity, baseline, _biomes[season + "/" + biome].Value, _affinities[season + "/" + affinity].Value, _limits[biome].Value, prefabPatch, out chances, out error);
		}
	}
	internal static class EcologyDefaults
	{
		private static readonly Dictionary<string, CreatureAffinity> Creatures = new Dictionary<string, CreatureAffinity>(StringComparer.OrdinalIgnoreCase)
		{
			["Fenring"] = CreatureAffinity.Cold,
			["Hatchling"] = CreatureAffinity.Cold,
			["Surtling"] = CreatureAffinity.Fire,
			["Fenring_Cultist"] = CreatureAffinity.Fire,
			["Blob"] = CreatureAffinity.Poison,
			["BlobElite"] = CreatureAffinity.Poison,
			["Leech"] = CreatureAffinity.Poison,
			["Wraith"] = CreatureAffinity.Spirit,
			["Skeleton"] = CreatureAffinity.Spirit,
			["Skeleton_Poison"] = CreatureAffinity.Poison,
			["Draugr"] = CreatureAffinity.Spirit,
			["Draugr_Ranged"] = CreatureAffinity.Spirit,
			["Draugr_Elite"] = CreatureAffinity.Spirit,
			["GoblinShaman"] = CreatureAffinity.Arcane,
			["DvergerMage"] = CreatureAffinity.Arcane
		};

		private static readonly Dictionary<string, string> BiomeRules = new Dictionary<string, string>
		{
			["winter/Meadows"] = "Frost=55; ResistFrost=25",
			["winter/BlackForest"] = "Frost=65; ResistFrost=35",
			["winter/Swamp"] = "Frost=25; ResistFrost=15",
			["winter/Mountain"] = "Frost=90; ResistFrost=80",
			["winter/DeepNorth"] = "Frost=90; ResistFrost=100",
			["winter/Plains"] = "Frost=45; ResistFrost=25",
			["winter/Mistlands"] = "Frost=45; ElementalChaos=20; ResistFrost=35",
			["winter/AshLands"] = "Frost=5; Fire=50; ResistFire=50; ResistFrost=5",
			["winter/Ocean"] = "Frost=35; ResistFrost=40",
			["spring/Meadows"] = "Poison=1; ElementalChaos=25; PoisonNova=0.5",
			["spring/BlackForest"] = "Poison=5; ElementalChaos=25; PoisonNova=2",
			["spring/Swamp"] = "Poison=75; ElementalChaos=5; PoisonNova=30; ResistPoison=35",
			["spring/Mountain"] = "Poison=0.2; ElementalChaos=5; PoisonNova=0.1",
			["spring/DeepNorth"] = "Poison=0.1; ElementalChaos=8; PoisonNova=0.1",
			["spring/Plains"] = "Poison=15; ElementalChaos=10; PoisonNova=5",
			["spring/Mistlands"] = "Poison=15; ElementalChaos=60; EitrDrain=20",
			["spring/AshLands"] = "Poison=1; ElementalChaos=15; Fire=30; ResistFire=20",
			["spring/Ocean"] = "Poison=3; ElementalChaos=10",
			["summer/Meadows"] = "Fire=55; Lightning=10",
			["summer/BlackForest"] = "Fire=60; Lightning=15",
			["summer/Swamp"] = "Fire=35; Lightning=15; Poison=10; PoisonNova=10",
			["summer/Mountain"] = "Fire=5; Lightning=10; Frost=40; ResistFrost=50",
			["summer/DeepNorth"] = "Fire=2; Lightning=10; Frost=50; ResistFrost=60",
			["summer/Plains"] = "Fire=75; Lightning=15; Fast=30",
			["summer/Mistlands"] = "Fire=40; Lightning=20; ElementalChaos=25; EitrDrain=15",
			["summer/AshLands"] = "Fire=90; Lightning=5; ResistFire=60",
			["summer/Ocean"] = "Fire=25; Lightning=25",
			["autumn/Meadows"] = "Lightning=25; Poison=1; ElementalChaos=20; PoisonNova=0.5",
			["autumn/BlackForest"] = "Lightning=35; Poison=5; ElementalChaos=20",
			["autumn/Swamp"] = "Lightning=25; Poison=55; ElementalChaos=10; PoisonNova=25",
			["autumn/Mountain"] = "Lightning=45; Poison=0.2; ElementalChaos=5",
			["autumn/DeepNorth"] = "Lightning=50; Poison=0.1; ElementalChaos=8",
			["autumn/Plains"] = "Lightning=50; Poison=15; ElementalChaos=15",
			["autumn/Mistlands"] = "Lightning=35; Poison=10; ElementalChaos=45",
			["autumn/AshLands"] = "Lightning=45; Poison=1; ElementalChaos=15; Fire=40; ResistFire=30",
			["autumn/Ocean"] = "Lightning=40; Poison=3; ElementalChaos=10"
		};

		private static readonly Dictionary<string, string> AffinityRules = new Dictionary<string, string>
		{
			["winter/Cold"] = "Frost=95; ResistFrost=80; Fire=0.5; FireNova=0.5; Poison=0.1; PoisonNova=0.1",
			["spring/Cold"] = "Poison=0.1; ElementalChaos=10; PoisonNova=0.1; Frost=80; ResistFrost=80; Fire=0.5; FireNova=0.5",
			["summer/Cold"] = "Fire=2; Lightning=10; FireNova=0.5; Frost=80; ResistFrost=80; Poison=0.1; PoisonNova=0.1",
			["autumn/Cold"] = "Lightning=20; Poison=0.1; ElementalChaos=10; Frost=80; ResistFrost=80; PoisonNova=0.1; Fire=0.5; FireNova=0.5",
			["winter/Fire"] = "Frost=1; Fire=80; ResistFire=80; FireNova=20; Poison=0.2; PoisonNova=0.2",
			["spring/Fire"] = "Poison=0.5; ElementalChaos=10; PoisonNova=0.2; Fire=80; ResistFire=80; Frost=1",
			["summer/Fire"] = "Fire=90; Lightning=5; ResistFire=80; Frost=1; Poison=0.2; PoisonNova=0.2",
			["autumn/Fire"] = "Lightning=35; Poison=0.5; ElementalChaos=10; Fire=80; ResistFire=80; Frost=1; PoisonNova=0.2",
			["winter/Poison"] = "Frost=15; Poison=80; PoisonNova=40; ResistPoison=50",
			["spring/Poison"] = "Poison=85; ElementalChaos=5; PoisonNova=40; ResistPoison=50",
			["summer/Poison"] = "Fire=15; Lightning=5; Poison=80; PoisonNova=40; ResistPoison=50",
			["autumn/Poison"] = "Lightning=15; Poison=70; ElementalChaos=5; PoisonNova=40; ResistPoison=50",
			["winter/Spirit"] = "Frost=45; Poison=0.5; PoisonNova=0.2; ElementalChaos=10; SoulEater=30; ResistSpirit=60",
			["spring/Spirit"] = "Poison=1; ElementalChaos=15; PoisonNova=0.2; SoulEater=30; ResistSpirit=60",
			["summer/Spirit"] = "Fire=25; Lightning=15; Poison=0.5; PoisonNova=0.2; ElementalChaos=15; SoulEater=30; ResistSpirit=60",
			["autumn/Spirit"] = "Lightning=35; Poison=1; ElementalChaos=20; PoisonNova=0.2; SoulEater=40; ResistSpirit=60",
			["winter/Arcane"] = "Frost=60; ElementalChaos=50; EitrDrain=25",
			["spring/Arcane"] = "Poison=5; ElementalChaos=70; EitrDrain=25",
			["summer/Arcane"] = "Fire=35; Lightning=25; ElementalChaos=70; EitrDrain=25",
			["autumn/Arcane"] = "Lightning=20; Poison=5; ElementalChaos=65; EitrDrain=25"
		};

		internal static CreatureAffinity Affinity(string prefab)
		{
			if (prefab == null || !Creatures.TryGetValue(prefab, out var value))
			{
				return CreatureAffinity.Neutral;
			}
			return value;
		}

		internal static string BiomePatch(string season, string biome)
		{
			if (!BiomeRules.TryGetValue(season + "/" + biome, out var value))
			{
				return "";
			}
			return value;
		}

		internal static string AffinityPatch(string season, CreatureAffinity affinity)
		{
			if (!AffinityRules.TryGetValue(season + "/" + affinity, out var value))
			{
				return "";
			}
			return value;
		}

		internal static string HabitatLimits(string biome)
		{
			return biome switch
			{
				"Meadows" => "Poison=1; PoisonNova=0.5", 
				"BlackForest" => "Poison=5; PoisonNova=2", 
				"Swamp" => "Poison=85; PoisonNova=40; ElementalChaos=30", 
				"Mountain" => "Poison=0.2; PoisonNova=0.1; ElementalChaos=5", 
				"DeepNorth" => "Poison=0.1; PoisonNova=0.1; ElementalChaos=10", 
				"Plains" => "Poison=20; PoisonNova=8; ElementalChaos=20", 
				"Mistlands" => "Poison=25; PoisonNova=10; ElementalChaos=75", 
				"AshLands" => "Poison=1; PoisonNova=0.5; ElementalChaos=20", 
				"Ocean" => "Poison=3; PoisonNova=1; ElementalChaos=10", 
				_ => "", 
			};
		}
	}
	internal enum CreatureAffinity
	{
		Neutral,
		Cold,
		Fire,
		Poison,
		Spirit,
		Arcane
	}
	internal static class EcologyPolicy
	{
		internal static readonly string[] Seasons = new string[4] { "winter", "spring", "summer", "autumn" };

		internal static readonly string[] Biomes = new string[9] { "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "AshLands", "DeepNorth", "Ocean" };

		internal static bool TryParsePatch(string text, out Dictionary<string, float> patch, out string error)
		{
			patch = new Dictionary<string, float>(StringComparer.Ordinal);
			error = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return true;
			}
			string[] array = text.Split(';');
			foreach (string text2 in array)
			{
				if (!string.IsNullOrWhiteSpace(text2))
				{
					string[] parts = text2.Split('=');
					string text3 = ((parts.Length == 2) ? SeasonalPolicy.Categories.Keys.FirstOrDefault((string n) => string.Equals(n, parts[0].Trim(), StringComparison.OrdinalIgnoreCase)) : null);
					if (text3 == null || !float.TryParse(parts[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result) || result < 0f || result > 100f || patch.ContainsKey(text3))
					{
						error = "Invalid or duplicate modifier assignment: '" + text2 + "'. Use Modifier=0..100, separated by semicolons (decimal point).";
						patch.Clear();
						return false;
					}
					patch.Add(text3, result);
				}
			}
			return true;
		}

		internal static bool TryAffinity(string value, out CreatureAffinity affinity)
		{
			foreach (CreatureAffinity value2 in Enum.GetValues(typeof(CreatureAffinity)))
			{
				if (string.Equals(value2.ToString(), value?.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					affinity = value2;
					return true;
				}
			}
			affinity = CreatureAffinity.Neutral;
			return false;
		}

		internal static bool TryResolve(string season, string biome, CreatureAffinity affinity, IDictionary<string, float> baseline, string biomePatch, string affinityPatch, string habitatLimits, string prefabPatch, out Dictionary<string, float> chances, out string error)
		{
			chances = null;
			error = null;
			if (!Seasons.Contains(season) || !Biomes.Contains(biome) || !Enum.IsDefined(typeof(CreatureAffinity), affinity))
			{
				error = "Unknown season, biome or affinity.";
				return false;
			}
			if (baseline == null || baseline.Any((KeyValuePair<string, float> p) => !SeasonalPolicy.Categories.ContainsKey(p.Key) || float.IsNaN(p.Value) || float.IsInfinity(p.Value) || p.Value < 0f || p.Value > 100f))
			{
				error = "Seasonal baseline must contain known modifiers with finite values between 0 and 100.";
				return false;
			}
			Dictionary<string, float> dictionary = new Dictionary<string, float>(baseline, StringComparer.Ordinal);
			string[] array = new string[4] { biomePatch, affinityPatch, habitatLimits, prefabPatch };
			string[] array2 = new string[4] { "Biome", "Affinity", "HabitatLimits", "Prefab" };
			for (int num = 0; num < array.Length; num++)
			{
				if (!TryParsePatch(array[num], out var patch, out error))
				{
					error = array2[num] + ": " + error;
					return false;
				}
				if (num == 2 && patch.Keys.Any((string n) => n != "Poison" && n != "PoisonNova" && n != "ElementalChaos"))
				{
					error = "HabitatLimits only accepts Poison, PoisonNova and ElementalChaos.";
					return false;
				}
				foreach (KeyValuePair<string, float> item in patch)
				{
					if (num != 2)
					{
						dictionary[item.Key] = item.Value;
					}
					else if (affinity != CreatureAffinity.Poison || (!(item.Key == "Poison") && !(item.Key == "PoisonNova")))
					{
						dictionary[item.Key] = Math.Min(dictionary.TryGetValue(item.Key, out var value) ? value : 0f, item.Value);
					}
				}
			}
			if (!SeasonalPolicy.ValidProfile(season, dictionary))
			{
				error = "Effective primary percentages exceed 100; profile rejected without normalization.";
				return false;
			}
			chances = dictionary;
			return true;
		}

		internal static Dictionary<string, double> SelectionProbabilities(string season, IDictionary<string, float> chances, float global, string fallback = null)
		{
			Dictionary<string, double> dictionary = new Dictionary<string, double>();
			if (!SeasonalPolicy.ValidProfile(season, chances))
			{
				return dictionary;
			}
			string[] primary = SeasonalPolicy.Primary(season);
			double num = (double)SeasonalPolicy.Percent(global) / 100.0;
			float value3;
			double num2 = primary.Sum((string n) => (!chances.TryGetValue(n, out value3)) ? 0.0 : ((double)SeasonalPolicy.Percent(value3)));
			double num3 = SeasonalPolicy.Categories.Keys.Where((string n) => !primary.Contains(n)).Sum((string n) => (!chances.TryGetValue(n, out value3)) ? 0.0 : ((double)SeasonalPolicy.Percent(value3)));
			foreach (string key in SeasonalPolicy.Categories.Keys)
			{
				float value;
				double num4 = (chances.TryGetValue(key, out value) ? SeasonalPolicy.Percent(value) : 0f);
				dictionary[key] = num * (primary.Contains(key) ? num4 : ((100.0 - num2) * num4 / Math.Max(100.0, num3)));
			}
			if (fallback != null && SeasonalPolicy.Categories.TryGetValue(fallback, out var value2) && value2 == 1)
			{
				dictionary[fallback] += num * (100.0 - num2) * Math.Max(0.0, 100.0 - num3) / 100.0;
			}
			return dictionary;
		}

		internal static string Describe(string season, string biome, string prefab, CreatureAffinity affinity, IDictionary<string, float> chances, float global, string fallback = null)
		{
			Dictionary<string, double> dictionary = SelectionProbabilities(season, chances, global, fallback);
			return "season=" + season + "; biome=" + biome + "; prefab=" + prefab + "; affinity=" + affinity.ToString() + "; selection % (before SLS acceptance): " + string.Join(", ", from p in dictionary.Where((KeyValuePair<string, double> p) => p.Value > 0.0).OrderBy<KeyValuePair<string, double>, string>((KeyValuePair<string, double> p) => p.Key, StringComparer.Ordinal)
				select p.Key + "=" + p.Value.ToString("0.###", CultureInfo.InvariantCulture)) + "; none=" + (100.0 - dictionary.Values.Sum()).ToString("0.###", CultureInfo.InvariantCulture);
		}
	}
	[BepInPlugin("TheLionCid.SeasonalSLSBridge", "Seasonal SLS Bridge", "1.4.3")]
	[BepInDependency("MidnightsFX.StarLevelSystem", "1.14.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "TheLionCid.SeasonalSLSBridge";

		public const string PluginName = "Seasonal SLS Bridge";

		public const string PluginVersion = "1.4.3";

		internal const string ProcessedKey = "TheLionCid.SeasonalSLSBridge.processed";

		internal const string SeasonKey = "TheLionCid.SeasonalSLSBridge.season";

		internal static Plugin Instance;

		private Harmony _harmony;

		internal bool ApiReady;

		private EcologyConfig _ecology;

		private readonly HashSet<string> _reportedErrors = new HashSet<string>();

		private readonly Dictionary<string, ConfigEntry<float>> _winter = new Dictionary<string, ConfigEntry<float>>();

		private readonly Dictionary<string, ConfigEntry<float>> _spring = new Dictionary<string, ConfigEntry<float>>();

		private readonly Dictionary<string, ConfigEntry<float>> _summer = new Dictionary<string, ConfigEntry<float>>();

		private readonly Dictionary<string, ConfigEntry<float>> _autumn = new Dictionary<string, ConfigEntry<float>>();

		private static readonly string[] AllModifiers = SeasonalPolicy.Categories.Keys.ToArray();

		internal ConfigEntry<float> SeasonalRollChance;

		internal ConfigEntry<float> DelaySeconds;

		internal ConfigEntry<bool> IncludeTamed;

		internal ConfigEntry<bool> LogAppliedModifiers;

		internal ConfigEntry<bool> EnsureSeasonalModifier;

		internal ConfigEntry<string> ExcludedPrefabs;

		internal ConfigEntry<string> AllowedPrefabs;

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			Instance = this;
			SeasonalRollChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SeasonalRollChance", 100f, new ConfigDescription("Modifier roll gate only. With EnsureSeasonalModifier enabled, every passed valid roll selects a modifier; SLS must still accept it. Birth-season titles are independent, including at 0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			EnsureSeasonalModifier = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnsureSeasonalModifier", true, "Fill empty passed rolls with a Minor: winter Big, spring Fast, summer ResistFire, autumn Alert. This separate backup applies even if all profile weights are zero. False restores empty rolls. Never bypasses the global gate, filters or invalid profiles.");
			DelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DelaySeconds", 0.75f, new ConfigDescription("Delay before the server attempts the seasonal roll.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			IncludeTamed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeTamed", false, "Explicit opt-in for tamed creatures. Players, bosses and traders are always excluded.");
			LogAppliedModifiers = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogAppliedModifiers", true, "Log accepted seasonal modifiers.");
			ExcludedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "ExcludedPrefabs", "Haldor,Hildir,BogWitch", "Comma-separated exact prefab names; case-insensitive. Exclusions take priority.");
			AllowedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "AllowedPrefabs", "", "Optional comma-separated exact prefab allowlist. Empty allows all otherwise eligible creatures.");
			ConfigureSeasons();
			_ecology = new EcologyConfig(((BaseUnityPlugin)this).Config);
			ApiReady = SlsApi.Validate(out var reason);
			if (!ApiReady)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)reason);
			}
			VisualSystem.Initialize(this);
			_harmony = new Harmony("TheLionCid.SeasonalSLSBridge");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Seasonal SLS Bridge 1.4.3: " + reason));
		}

		private void OnDestroy()
		{
			VisualSystem.Shutdown();
			((MonoBehaviour)this).StopAllCoroutines();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void ConfigureSeasons()
		{
			string[] seasons = EcologyPolicy.Seasons;
			foreach (string text in seasons)
			{
				string text2 = char.ToUpperInvariant(text[0]) + text.Substring(1);
				Dictionary<string, float> dictionary = SeasonalDefaults.ForSeason(text);
				Dictionary<string, ConfigEntry<float>> seasonDictionary = GetSeasonDictionary(text);
				string[] allModifiers = AllModifiers;
				foreach (string text3 in allModifiers)
				{
					seasonDictionary[text3] = ((BaseUnityPlugin)this).Config.Bind<float>(text2, text3 + "Chance", dictionary[text3], "Seasonal baseline. Primary entries are absolute percentages; others are conditional secondary weights. Ecology profiles can explicitly replace these values, including zero. Disable Ecology.Enabled to use this baseline directly.");
				}
			}
		}

		internal static string GetSeason()
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return null;
			}
			List<string> list = new List<string>();
			string[] array = new string[3] { "winter", "spring", "summer" };
			foreach (string text in array)
			{
				if (ZoneSystem.instance.GetGlobalKey("season_" + text))
				{
					list.Add(text);
				}
			}
			if (ZoneSystem.instance.GetGlobalKey("season_fall") || ZoneSystem.instance.GetGlobalKey("season_autumn"))
			{
				list.Add("autumn");
			}
			if (list.Count != 1)
			{
				return null;
			}
			return list[0];
		}

		internal Dictionary<string, ConfigEntry<float>> GetSeasonDictionary(string season)
		{
			return season switch
			{
				"winter" => _winter, 
				"spring" => _spring, 
				"summer" => _summer, 
				"autumn" => _autumn, 
				_ => null, 
			};
		}

		internal static bool HasAuthority(Character creature, out ZDO zdo)
		{
			zdo = null;
			if ((Object)(object)creature == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			ZNetView component = ((Component)creature).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
			{
				return false;
			}
			zdo = component.GetZDO();
			return zdo != null;
		}

		internal bool Eligible(Character creature)
		{
			if ((Object)(object)creature == (Object)null || creature.IsDead() || creature.IsPlayer() || creature.IsBoss())
			{
				return false;
			}
			if (!IncludeTamed.Value && creature.IsTamed())
			{
				return false;
			}
			if ((Object)(object)((Component)creature).GetComponent<Trader>() != (Object)null)
			{
				return false;
			}
			return SeasonalPolicy.PrefabAllowed(Utils.GetPrefabName(((Component)creature).gameObject), AllowedPrefabs.Value, ExcludedPrefabs.Value);
		}

		internal void VisualWarning(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Visuals: " + message));
		}

		private void ReportError(string error)
		{
			if (_reportedErrors.Count < 100 && _reportedErrors.Add(error))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)error);
			}
		}

		internal IEnumerator RollAfterDelay(Character creature, string birthSeason, Vector3 birthPosition, string prefab)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			yield return (object)new WaitForSeconds(Math.Min(30f, SeasonalPolicy.Percent(DelaySeconds.Value)));
			if (!ApiReady || !HasAuthority(creature, out var zdo) || !Eligible(creature) || zdo.GetBool("TheLionCid.SeasonalSLSBridge.processed", false))
			{
				yield break;
			}
			Dictionary<string, float> dictionary = GetSeasonDictionary(birthSeason)?.ToDictionary((KeyValuePair<string, ConfigEntry<float>> x) => x.Key, (KeyValuePair<string, ConfigEntry<float>> x) => x.Value.Value);
			if (dictionary == null)
			{
				yield break;
			}
			Dictionary<string, float> chances = dictionary;
			string biome = "legacy";
			CreatureAffinity affinity = CreatureAffinity.Neutral;
			if (_ecology.Enabled.Value)
			{
				if (!WorldContext.TryGetBiome(birthPosition, out biome))
				{
					ReportError("Ecology: no supported biome at birth for " + prefab + "; skipped.");
					yield break;
				}
				if (!_ecology.TryResolve(birthSeason, biome, prefab, dictionary, out affinity, out chances, out var error))
				{
					ReportError("Ecology " + birthSeason + "/" + biome + "/" + prefab + ": " + error);
					yield break;
				}
			}
			if (!SeasonalPolicy.ValidProfile(birthSeason, chances))
			{
				ReportError("Invalid " + birthSeason + " primary chances: sum exceeds 100%. No roll performed.");
				yield break;
			}
			string fallback = (EnsureSeasonalModifier.Value ? SeasonalDefaults.FallbackModifier(birthSeason) : null);
			if (_ecology.LogDecisions.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[Ecology] " + EcologyPolicy.Describe(birthSeason, biome, prefab, affinity, chances, SeasonalRollChance.Value, fallback)));
			}
			zdo.Set("TheLionCid.SeasonalSLSBridge.processed", true);
			string text = SeasonalPolicy.Roll(birthSeason, chances, SeasonalRollChance.Value, () => Random.value, fallback);
			if (text == null || !HasAuthority(creature, out zdo) || !Eligible(creature))
			{
				yield break;
			}
			if (SlsApi.TryAdd(creature, text, out var detail))
			{
				if (LogAppliedModifiers.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Seasonal modifier: {text} ({SeasonalPolicy.Categories[text]}) -> {creature.m_name} {SeasonalPolicy.Title(birthSeason)}; biome={biome}; affinity={affinity}; {detail}");
				}
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Seasonal modifier " + text + " rejected: " + detail));
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class CharacterAwakePatch
	{
		private static void Postfix(Character __instance)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null || !Plugin.HasAuthority(__instance, out var zdo) || !SpawnTracking.IsNew(((Component)__instance).GetComponent<ZNetView>()) || !instance.Eligible(__instance))
			{
				return;
			}
			string text = zdo.GetString("TheLionCid.SeasonalSLSBridge.season", "");
			if (string.IsNullOrEmpty(text))
			{
				text = Plugin.GetSeason();
				if (text == null)
				{
					return;
				}
				zdo.Set("TheLionCid.SeasonalSLSBridge.season", text);
			}
			if (instance.ApiReady && !zdo.GetBool("TheLionCid.SeasonalSLSBridge.processed", false))
			{
				((MonoBehaviour)instance).StartCoroutine(instance.RollAfterDelay(__instance, text, ((Component)__instance).transform.position, Utils.GetPrefabName(((Component)__instance).gameObject)));
			}
		}
	}
	[HarmonyPatch(typeof(Character), "GetHoverName")]
	internal static class CharacterHoverNamePatch
	{
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "MidnightsFX.StarLevelSystem" })]
		private static void Postfix(Character __instance, ref string __result)
		{
			if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && !string.IsNullOrEmpty(__result))
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					__result = SeasonalPolicy.AppendTitle(__result, component.GetZDO().GetString("TheLionCid.SeasonalSLSBridge.season", ""));
				}
			}
		}
	}
	internal static class SeasonalDefaults
	{
		internal static string FallbackModifier(string season)
		{
			return season switch
			{
				"winter" => "Big", 
				"spring" => "Fast", 
				"summer" => "ResistFire", 
				"autumn" => "Alert", 
				_ => null, 
			};
		}

		internal static Dictionary<string, float> ForSeason(string season)
		{
			Dictionary<string, float> dictionary = SeasonalPolicy.Categories.Keys.ToDictionary((string n) => n, (string n) => 0f);
			switch (season)
			{
			case "winter":
				dictionary["Frost"] = 95f;
				dictionary["ResistFrost"] = 25f;
				dictionary["Big"] = 25f;
				dictionary["StaminaDrain"] = 15f;
				dictionary["ResistPierce"] = 10f;
				dictionary["ResistSlash"] = 10f;
				dictionary["ResistBlunt"] = 10f;
				dictionary["EitrDrain"] = 5f;
				break;
			case "spring":
				dictionary["Poison"] = 65f;
				dictionary["ElementalChaos"] = 25f;
				dictionary["PoisonNova"] = 20f;
				dictionary["ResistPoison"] = 15f;
				dictionary["Fast"] = 20f;
				dictionary["Evolving"] = 10f;
				dictionary["Splitter"] = 5f;
				break;
			case "summer":
				dictionary["Fire"] = 70f;
				dictionary["FireNova"] = 20f;
				dictionary["ResistFire"] = 20f;
				dictionary["Lightning"] = 15f;
				dictionary["Fast"] = 15f;
				dictionary["Brutal"] = 10f;
				dictionary["Big"] = 10f;
				break;
			case "autumn":
				dictionary["Lightning"] = 40f;
				dictionary["Poison"] = 30f;
				dictionary["ElementalChaos"] = 20f;
				dictionary["SoulEater"] = 15f;
				dictionary["ResistSpirit"] = 10f;
				dictionary["Alert"] = 15f;
				dictionary["ResistPoison"] = 10f;
				dictionary["Big"] = 5f;
				break;
			}
			return dictionary;
		}
	}
	internal static class SeasonalPolicy
	{
		internal static readonly IReadOnlyDictionary<string, int> Categories = BuildCategories();

		private static Dictionary<string, int> BuildCategories()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			string[] array = new string[11]
			{
				"Brutal", "ElementalChaos", "Fire", "Frost", "Poison", "Lightning", "Splitter", "SoulEater", "ResistPierce", "ResistSlash",
				"ResistBlunt"
			};
			foreach (string key in array)
			{
				dictionary.Add(key, 0);
			}
			array = new string[13]
			{
				"ResistFire", "ResistFrost", "ResistPoison", "ResistSpirit", "FireNova", "PoisonNova", "Lootbags", "Alert", "Big", "Fast",
				"StaminaDrain", "Evolving", "EitrDrain"
			};
			foreach (string key2 in array)
			{
				dictionary.Add(key2, 1);
			}
			return dictionary;
		}

		internal static float Percent(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Max(0f, Math.Min(100f, value));
			}
			return 0f;
		}

		internal static string[] Primary(string season)
		{
			return season switch
			{
				"winter" => new string[1] { "Frost" }, 
				"spring" => new string[2] { "Poison", "ElementalChaos" }, 
				"summer" => new string[2] { "Fire", "Lightning" }, 
				"autumn" => new string[3] { "Lightning", "Poison", "ElementalChaos" }, 
				_ => Array.Empty<string>(), 
			};
		}

		internal static bool ValidProfile(string season, IDictionary<string, float> chances)
		{
			if (Primary(season).Length != 0)
			{
				return Primary(season).Sum((string n) => Chance(chances, n)) <= 100f;
			}
			return false;
		}

		private static float Chance(IDictionary<string, float> chances, string name)
		{
			if (!chances.TryGetValue(name, out var value))
			{
				return 0f;
			}
			return Percent(value);
		}

		private static double Unit(Func<float> random)
		{
			return Math.Max(0.0, Math.Min(0.999999999, random()));
		}

		internal static string Roll(string season, IDictionary<string, float> chances, float global, Func<float> random, string fallback = null)
		{
			if (!ValidProfile(season, chances) || Unit(random) * 100.0 >= (double)Percent(global))
			{
				return null;
			}
			string[] primary = Primary(season);
			double num = Unit(random) * 100.0;
			string[] array = primary;
			foreach (string text in array)
			{
				num -= (double)Chance(chances, text);
				if (num < 0.0)
				{
					return text;
				}
			}
			string[] array2 = Categories.Keys.Where((string n) => !primary.Contains(n)).OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToArray();
			double val = ((IEnumerable<string>)array2).Sum((Func<string, double>)((string n) => Chance(chances, n)));
			num = Unit(random) * Math.Max(100.0, val);
			array = array2;
			foreach (string text2 in array)
			{
				num -= (double)Chance(chances, text2);
				if (num < 0.0)
				{
					return text2;
				}
			}
			if (fallback == null || !Categories.TryGetValue(fallback, out var value) || value != 1)
			{
				return null;
			}
			return fallback;
		}

		internal static bool PrefabAllowed(string prefab, string allowed, string excluded)
		{
			if (string.IsNullOrWhiteSpace(prefab))
			{
				return false;
			}
			if (InList(prefab, "Haldor,Hildir,BogWitch") || InList(prefab, excluded))
			{
				return false;
			}
			if (!string.IsNullOrWhiteSpace(allowed))
			{
				return InList(prefab, allowed);
			}
			return true;
		}

		private static bool InList(string prefab, string list)
		{
			return (list ?? "").Split(',').Any((string n) => string.Equals(n.Trim(), prefab, StringComparison.OrdinalIgnoreCase));
		}

		internal static string AppendTitle(string name, string season)
		{
			string text = Title(season);
			if (string.IsNullOrEmpty(name) || text == null || name.EndsWith(" " + text, StringComparison.Ordinal))
			{
				return name;
			}
			return name + " " + text;
		}

		internal static string Title(string season)
		{
			return season switch
			{
				"winter" => "Vetrbarn", 
				"spring" => "Várbarn", 
				"summer" => "Sumarbarn", 
				"autumn" => "Haustbarn", 
				_ => null, 
			};
		}
	}
	internal static class SlsApi
	{
		internal static bool Validate(out string reason)
		{
			Type typeFromHandle = typeof(API);
			return SlsContract.Validate(typeFromHandle, typeof(Character), typeFromHandle.Assembly.GetName().Version, out reason);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool TryAdd(Character creature, string modifier, out string detail)
		{
			detail = "No authority or unsupported modifier.";
			if (!Plugin.HasAuthority(creature, out var _) || !Plugin.Instance.Eligible(creature) || !SeasonalPolicy.Categories.TryGetValue(modifier, out var value))
			{
				return false;
			}
			try
			{
				if (!API.IsAvailable)
				{
					detail = "API unavailable.";
					return false;
				}
				List<string> possibleModifiers = API.GetPossibleModifiers(value);
				if (possibleModifiers == null || !possibleModifiers.Contains<string>(modifier, StringComparer.Ordinal))
				{
					detail = $"Not configured in SLS category {value}.";
					return false;
				}
				Dictionary<string, int> creaturesModifiers = API.GetCreaturesModifiers(creature);
				if (creaturesModifiers == null)
				{
					detail = "Creature modifier state unavailable.";
					return false;
				}
				if (creaturesModifiers.ContainsKey(modifier))
				{
					detail = "Modifier already present; retained without adding another.";
					return true;
				}
				bool flag = API.AddModifierToTargetCreature(creature, modifier, value, true);
				detail = (flag ? "Accepted by SLS." : "SLS returned false.");
				return flag;
			}
			catch (Exception ex)
			{
				detail = ex.GetBaseException().Message;
				return false;
			}
		}
	}
	internal static class SlsContract
	{
		internal static bool Validate(Type api, Type character, Version version, out string reason)
		{
			if (version != new Version(1, 14, 0, 0) && version != new Version(1, 15, 0, 0) && version != new Version(1, 16, 0, 0))
			{
				reason = "SLS API disabled: verified versions are 1.14.0.0, 1.15.0.0 and 1.16.0.0; found " + version?.ToString() + ".";
				return false;
			}
			try
			{
				Require(api, "get_IsAvailable", typeof(bool), Type.EmptyTypes);
				Require(api, "GetPossibleModifiers", typeof(List<string>), new Type[1] { typeof(int) });
				Require(api, "GetCreaturesModifiers", typeof(Dictionary<string, int>), new Type[1] { character });
				Require(api, "AddModifierToTargetCreature", typeof(bool), new Type[4]
				{
					character,
					typeof(string),
					typeof(int),
					typeof(bool)
				});
			}
			catch (Exception ex)
			{
				reason = "SLS API disabled: incompatible contract for " + version?.ToString() + ": " + ex.Message;
				return false;
			}
			reason = "SLS " + version?.ToString() + " verified API; server + ZDO owner only.";
			return true;
		}

		private static void Require(Type api, string name, Type result, Type[] parameters)
		{
			MethodInfo method = api.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, null, parameters, null);
			if (method == null || method.IsGenericMethod || method.ReturnType != result || !(from p in method.GetParameters()
				select p.ParameterType).SequenceEqual(parameters))
			{
				throw new InvalidOperationException("Missing exact public static signature: " + name);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetView), "Awake")]
	internal static class SpawnTracking
	{
		private static readonly ConditionalWeakTable<ZNetView, object> NewViews = new ConditionalWeakTable<ZNetView, object>();

		[HarmonyPriority(800)]
		private static void Prefix(out bool __state)
		{
			__state = ZNetView.m_initZDO == null;
		}

		private static void Postfix(ZNetView __instance, bool __state)
		{
			if (__state && (Object)(object)__instance != (Object)null && __instance.IsValid() && __instance.IsOwner() && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				NewViews.GetValue(__instance, (ZNetView _) => new object());
			}
		}

		internal static bool IsNew(ZNetView view)
		{
			object value;
			if ((Object)(object)view != (Object)null)
			{
				return NewViews.TryGetValue(view, out value);
			}
			return false;
		}
	}
	internal sealed class VisualSlot
	{
		internal string Prefab;

		internal string Folder;

		internal string RendererPath;

		internal string Material;

		internal string File;

		internal string VanillaHash;

		internal string Property;

		internal bool Linear;

		internal int Slot;

		internal bool IsColor => File.EndsWith(".color", StringComparison.OrdinalIgnoreCase);
	}
	internal sealed class VisualCatalog
	{
		private readonly Dictionary<string, List<VisualSlot>> _creatures = new Dictionary<string, List<VisualSlot>>(StringComparer.Ordinal);

		internal IReadOnlyList<VisualSlot> Find(string prefab)
		{
			if (!_creatures.TryGetValue(prefab, out var value))
			{
				return Array.Empty<VisualSlot>();
			}
			return value;
		}

		internal static bool SafeName(string name)
		{
			if (!string.IsNullOrWhiteSpace(name) && Regex.IsMatch(name, "^[a-zA-Z0-9_.-]+$") && name != ".")
			{
				return name != "..";
			}
			return false;
		}

		internal static VisualCatalog Read(string path)
		{
			VisualCatalog visualCatalog = new VisualCatalog();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (string item in File.ReadLines(path))
			{
				if (!string.IsNullOrWhiteSpace(item) && !item.StartsWith("#", StringComparison.Ordinal))
				{
					string[] p = item.Split('\t');
					if ((p.Length != 7 && p.Length != 8 && p.Length != 9) || !SafeName(p[0]) || !int.TryParse(p[2], out var result) || result < 0 || result > 63 || !SafeName(p[4]) || (!p[4].EndsWith(".png", StringComparison.OrdinalIgnoreCase) && !p[4].EndsWith(".color", StringComparison.OrdinalIgnoreCase)) || !Regex.IsMatch(p[5], "^[a-fA-F0-9]{64}$") || !Regex.IsMatch(p[6], "^_[a-zA-Z0-9_]+$") || (p.Length >= 8 && p[7] != "0" && p[7] != "1") || (p.Length == 9 && !SafeName(p[8])) || string.IsNullOrWhiteSpace(p[3]) || p[1].StartsWith("/", StringComparison.Ordinal) || p[1].Contains("\\") || p[1].Split('/').Any((string s) => s == ".." || s == "."))
					{
						throw new InvalidDataException("Invalid visual catalog row: " + item);
					}
					string text = p[0] + "/" + p[1] + "/" + result + "/" + p[6];
					if (!hashSet.Add(text))
					{
						throw new InvalidDataException("Duplicate renderer slot: " + text);
					}
					if (!visualCatalog._creatures.TryGetValue(p[0], out var value))
					{
						value = (visualCatalog._creatures[p[0]] = new List<VisualSlot>());
					}
					if (value.Any((VisualSlot e) => e.File == p[4] && !string.Equals(e.VanillaHash, p[5], StringComparison.OrdinalIgnoreCase)))
					{
						throw new InvalidDataException("Conflicting vanilla hashes for " + p[0] + "/" + p[4]);
					}
					string text2 = ((p.Length == 9) ? p[8] : p[0]);
					string text3 = text2 + "/" + p[4];
					if (dictionary.TryGetValue(text3, out var value2) && !string.Equals(value2, p[5], StringComparison.OrdinalIgnoreCase))
					{
						throw new InvalidDataException("Conflicting shared-folder hashes: " + text3);
					}
					dictionary[text3] = p[5];
					value.Add(new VisualSlot
					{
						Prefab = p[0],
						Folder = text2,
						RendererPath = p[1],
						Slot = result,
						Material = p[3],
						File = p[4],
						VanillaHash = p[5],
						Property = p[6],
						Linear = (p.Length >= 8 && p[7] == "1")
					});
				}
			}
			return visualCatalog;
		}

		internal static IEnumerable<string> Candidates(string season, IDictionary<string, int> modifiers)
		{
			if (modifiers == null)
			{
				return Array.Empty<string>();
			}
			string[] primary = SeasonalPolicy.Primary(season);
			int value;
			return from p in (from p in modifiers
					where SeasonalPolicy.Categories.TryGetValue(p.Key, out value) && value == p.Value && VariantScopes(season, p.Key).Any()
					orderby (ElementScope(p.Key) == null) ? ((p.Key == "SoulEater") ? 1 : ((!(p.Key == "Brutal")) ? ((!(p.Key == "Big")) ? 4 : 3) : 2)) : 0, (Array.IndexOf(primary, p.Key) < 0) ? int.MaxValue : Array.IndexOf(primary, p.Key)
					select p).ThenBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal)
				select p.Key;
		}

		internal static IEnumerable<KeyValuePair<string, string>> CandidateVariants(string season, IDictionary<string, int> modifiers)
		{
			return Candidates(season, modifiers).SelectMany((string modifier) => from scope in VariantScopes(season, modifier)
				select new KeyValuePair<string, string>(modifier, scope));
		}

		internal static IEnumerable<string> VariantScopes(string season, string modifier)
		{
			if (modifier == "SoulEater" || modifier == "Brutal")
			{
				yield return "generic";
			}
			else if (SeasonalPolicy.Categories.ContainsKey(modifier ?? ""))
			{
				bool knownSeason = SeasonalPolicy.Title(season) != null;
				if (knownSeason)
				{
					yield return season;
				}
				string text = ElementScope(modifier);
				if (text != null && (!knownSeason || text != season))
				{
					yield return text;
				}
			}
		}

		private static string ElementScope(string modifier)
		{
			switch (modifier)
			{
			case "Frost":
				return "winter";
			case "Poison":
			case "ElementalChaos":
			case "PoisonNova":
				return "spring";
			case "Fire":
			case "Lightning":
			case "FireNova":
				return "summer";
			default:
				return null;
			}
		}
	}
	internal static class VisualFiles
	{
		internal static byte[] ReadEditedPng(string path, string vanillaHash, int maxDimension)
		{
			FileInfo fileInfo = new FileInfo(path);
			if (!fileInfo.Exists)
			{
				return null;
			}
			if (fileInfo.Length > 67108864)
			{
				throw new InvalidDataException("Visual PNG exceeds 64 MiB: " + path);
			}
			byte[] array = File.ReadAllBytes(path);
			using (SHA256 sHA = SHA256.Create())
			{
				if (string.Equals(BitConverter.ToString(sHA.ComputeHash(array)).Replace("-", ""), vanillaHash, StringComparison.OrdinalIgnoreCase))
				{
					return null;
				}
			}
			if (array.Length < 24 || array[0] != 137 || array[1] != 80 || array[2] != 78 || array[3] != 71 || array[4] != 13 || array[5] != 10 || array[6] != 26 || array[7] != 10 || Dimension(array, 16) < 1 || Dimension(array, 20) < 1 || Dimension(array, 16) > maxDimension || Dimension(array, 20) > maxDimension)
			{
				throw new InvalidDataException("Invalid visual PNG or dimensions exceed " + maxDimension + ": " + path);
			}
			return array;
		}

		internal static float[] ReadEditedColor(string path, string vanillaHash)
		{
			FileInfo fileInfo = new FileInfo(path);
			if (!fileInfo.Exists)
			{
				return null;
			}
			if (fileInfo.Length > 1024)
			{
				throw new InvalidDataException("Color file exceeds 1 KiB: " + path);
			}
			byte[] array = File.ReadAllBytes(path);
			using (SHA256 sHA = SHA256.Create())
			{
				if (string.Equals(BitConverter.ToString(sHA.ComputeHash(array)).Replace("-", ""), vanillaHash, StringComparison.OrdinalIgnoreCase))
				{
					return null;
				}
			}
			string[] array2 = Encoding.UTF8.GetString(array).Trim('\ufeff').Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
			if (array2.Length != 4)
			{
				throw new InvalidDataException("Expected four RGBA numbers: " + path);
			}
			float[] array3 = new float[4];
			for (int i = 0; i < 4; i++)
			{
				if (!float.TryParse(array2[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array3[i]) || float.IsNaN(array3[i]) || float.IsInfinity(array3[i]) || array3[i] < 0f || array3[i] > (float)((i == 3) ? 1 : 64))
				{
					throw new InvalidDataException("RGBA requires finite RGB 0..64 and alpha 0..1: " + path);
				}
			}
			return array3;
		}

		private static uint Dimension(byte[] b, int i)
		{
			return (uint)((b[i] << 24) | (b[i + 1] << 16) | (b[i + 2] << 8) | b[i + 3]);
		}

		internal static long EstimatedTextureBytes(byte[] b)
		{
			return (long)Dimension(b, 16) * (long)Dimension(b, 20) * 4 * 4 / 3;
		}
	}
	internal sealed class VisualMaterial : IDisposable
	{
		private readonly Renderer _renderer;

		private readonly Material _original;

		private readonly Dictionary<string, Texture> _textures;

		private readonly Dictionary<string, Color> _colors;

		private readonly HashSet<string> _enabledKeywords = new HashSet<string>();

		private readonly int _slot;

		private Material _private;

		internal VisualMaterial(Renderer renderer, int slot, string material, Texture texture)
			: this(renderer, slot, material, new Dictionary<string, Texture> { { "_MainTex", texture } }, new Dictionary<string, Color>())
		{
		}

		internal VisualMaterial(Renderer renderer, int slot, string material, IDictionary<string, Texture> textures, IDictionary<string, Color> colors)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			_renderer = renderer;
			_slot = slot;
			_textures = new Dictionary<string, Texture>(textures);
			_colors = new Dictionary<string, Color>(colors);
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (slot < 0 || slot >= sharedMaterials.Length || (Object)(object)sharedMaterials[slot] == (Object)null || !MatchesMaterial(((Object)sharedMaterials[slot]).name, material))
			{
				throw new InvalidOperationException("Visual renderer/material no longer matches catalog: " + material);
			}
			if (renderer.HasPropertyBlock())
			{
				throw new InvalidOperationException("Visual renderer already has a property block; keeping normal appearance.");
			}
			_original = sharedMaterials[slot];
			foreach (string key in _textures.Keys)
			{
				RequireProperty(key);
			}
			foreach (string key2 in _colors.Keys)
			{
				RequireProperty(key2);
			}
			_private = new Material(_original);
			try
			{
				if (((Object)_private).GetInstanceID() >= 0)
				{
					throw new InvalidOperationException("Visual material is not an isolated runtime instance.");
				}
				((Object)_private).name = "SSB_Visual_" + ((Object)_private).GetInstanceID();
				((Object)_private).hideFlags = (HideFlags)52;
				ApplyOverrides();
				sharedMaterials[slot] = _private;
				renderer.sharedMaterials = sharedMaterials;
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		internal void Refresh()
		{
			_private.CopyPropertiesFromMaterial(_original);
			ApplyOverrides();
		}

		private void ApplyOverrides()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<string, Texture> texture in _textures)
			{
				_private.SetTexture(texture.Key, texture.Value);
			}
			foreach (KeyValuePair<string, Color> color2 in _colors)
			{
				_private.SetColor(color2.Key, color2.Value);
			}
			if (_textures.ContainsKey("_BumpMap"))
			{
				EnableKeyword("_NORMALMAP");
			}
			if (_textures.ContainsKey("_MetallicGlossMap"))
			{
				EnableKeyword("_METALLICGLOSSMAP");
			}
			if (_textures.ContainsKey("_DetailAlbedoMap") || _textures.ContainsKey("_DetailNormalMap"))
			{
				EnableKeyword("_DETAIL_MULX2");
			}
			if (!_textures.ContainsKey("_EmissionMap") && !_colors.ContainsKey("_EmissionColor"))
			{
				return;
			}
			EnableKeyword("_EMISSION");
			if (_textures.ContainsKey("_EmissionMap") && !_colors.ContainsKey("_EmissionColor") && _private.HasProperty("_EmissionColor"))
			{
				Color color = _private.GetColor("_EmissionColor");
				if (color.r == 0f && color.g == 0f && color.b == 0f)
				{
					_colors["_EmissionColor"] = Color.white;
					_private.SetColor("_EmissionColor", Color.white);
				}
			}
		}

		private void EnableKeyword(string keyword)
		{
			if (!_original.IsKeywordEnabled(keyword))
			{
				_enabledKeywords.Add(keyword);
			}
			_private.EnableKeyword(keyword);
		}

		private void RequireProperty(string property)
		{
			if (!_original.HasProperty(property))
			{
				throw new InvalidOperationException("Shader does not support " + property + " on " + ((Object)_original).name);
			}
		}

		private static bool MatchesMaterial(string actual, string expected)
		{
			if (!(actual == expected) && !(actual == expected + " (Instance)") && !(actual == expected + " (SLSColor)") && !(actual == expected + " (Instance) (SLSColor)") && !(actual == expected + " (SLSColor) (Instance)"))
			{
				return actual == expected + " (Instance) (SLSColor) (Instance)";
			}
			return true;
		}

		internal bool IsIntact()
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_renderer == (Object)null || (Object)(object)_private == (Object)null)
			{
				return false;
			}
			Material[] sharedMaterials = _renderer.sharedMaterials;
			if (_slot >= sharedMaterials.Length || (Object)(object)sharedMaterials[_slot] != (Object)(object)_private || _renderer.HasPropertyBlock())
			{
				return false;
			}
			foreach (KeyValuePair<string, Texture> texture in _textures)
			{
				if ((Object)(object)_private.GetTexture(texture.Key) != (Object)(object)texture.Value)
				{
					return false;
				}
			}
			foreach (KeyValuePair<string, Color> color in _colors)
			{
				if (_private.GetColor(color.Key) != color.Value)
				{
					return false;
				}
			}
			return true;
		}

		public void Dispose()
		{
			if ((Object)(object)_private == (Object)null)
			{
				return;
			}
			try
			{
				if (!((Object)(object)_renderer != (Object)null))
				{
					return;
				}
				Material[] sharedMaterials = _renderer.sharedMaterials;
				bool flag = false;
				for (int i = 0; i < sharedMaterials.Length; i++)
				{
					if ((Object)(object)sharedMaterials[i] == (Object)(object)_private)
					{
						sharedMaterials[i] = _original;
						flag = true;
					}
					else if ((Object)(object)sharedMaterials[i] != (Object)null && ((Object)sharedMaterials[i]).GetInstanceID() < 0 && (((Object)sharedMaterials[i]).name == ((Object)_private).name + " (SLSColor)" || ((Object)sharedMaterials[i]).name == ((Object)_private).name + " (Instance) (SLSColor)"))
					{
						RestoreInheritedOverrides(sharedMaterials[i]);
					}
				}
				if (flag)
				{
					_renderer.sharedMaterials = sharedMaterials;
				}
			}
			finally
			{
				Object.Destroy((Object)(object)_private);
				_private = null;
			}
		}

		private void RestoreInheritedOverrides(Material slsClone)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<string, Texture> texture in _textures)
			{
				if (slsClone.HasProperty(texture.Key) && (Object)(object)slsClone.GetTexture(texture.Key) == (Object)(object)texture.Value)
				{
					slsClone.SetTexture(texture.Key, _original.GetTexture(texture.Key));
				}
			}
			foreach (string enabledKeyword in _enabledKeywords)
			{
				if (!_original.IsKeywordEnabled(enabledKeyword))
				{
					slsClone.DisableKeyword(enabledKeyword);
				}
			}
			foreach (KeyValuePair<string, Color> color in _colors)
			{
				if (slsClone.HasProperty(color.Key) && slsClone.GetColor(color.Key) == color.Value)
				{
					slsClone.SetColor(color.Key, _original.GetColor(color.Key));
				}
			}
		}
	}
	internal static class VisualSystem
	{
		internal static bool Ready;

		internal static ConfigEntry<bool> Enabled;

		private static ConfigEntry<int> _maxDimension;

		private static ConfigEntry<int> _cacheMiB;

		private static ConfigEntry<FilterMode> _textureFilter;

		private static Plugin _plugin;

		private static VisualCatalog _catalog;

		private static string _root;

		private static long _cachedBytes;

		private static readonly Dictionary<string, Texture2D> Textures = new Dictionary<string, Texture2D>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Color?> Colors = new Dictionary<string, Color?>(StringComparer.Ordinal);

		private static readonly HashSet<string> Reported = new HashSet<string>();

		internal static void Initialize(Plugin plugin)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Invalid comparison between Unknown and I4
			_plugin = plugin;
			Ready = false;
			Enabled = ((BaseUnityPlugin)plugin).Config.Bind<bool>("Visuals", "Enabled", true, "Enable local custom creature textures. False restores normal Seasonality appearance; gameplay and seasonal modifiers are unaffected.");
			_textureFilter = ((BaseUnityPlugin)plugin).Config.Bind<FilterMode>("Visuals", "TextureFilter", (FilterMode)0, "Point preserves vanilla pixel detail. Bilinear and Trilinear smooth textures for HD packs. Restart after changing.");
			_maxDimension = ((BaseUnityPlugin)plugin).Config.Bind<int>("Visuals", "MaxTextureDimension", 4096, new ConfigDescription("Largest accepted PNG side in pixels. Restart after changing textures or limits.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(128, 8192), Array.Empty<object>()));
			_cacheMiB = ((BaseUnityPlugin)plugin).Config.Bind<int>("Visuals", "TextureCacheMiB", 256, new ConfigDescription("Approximate RGBA+mipmap budget for custom textures. Excess variants keep normal appearance; restart to clear cache.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 2048), Array.Empty<object>()));
			try
			{
				if ((int)SystemInfo.graphicsDeviceType != 4)
				{
					if (!Chainloader.PluginInfos.TryGetValue("RustyMods.Seasonality", out var value) || ((object)value.Instance).GetType().Assembly.GetName().Version != new Version(3, 8, 3, 0))
					{
						throw new InvalidOperationException("Visual compatibility has only been inspected with Seasonality 3.8.3; visual system disabled.");
					}
					_root = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)plugin).Info.Location), "SeasonalTextures");
					_catalog = VisualCatalog.Read(Path.Combine(_root, "catalog.tsv"));
					Ready = true;
				}
			}
			catch (Exception ex)
			{
				Warn(ex.GetBaseException().Message + " Gameplay remains enabled.");
			}
		}

		internal static void Warn(string message)
		{
			if (Reported.Count < 100 && Reported.Add(message))
			{
				_plugin?.VisualWarning(message);
			}
		}

		internal static IReadOnlyList<VisualSlot> Slots(string prefab)
		{
			return _catalog?.Find(prefab) ?? Array.Empty<VisualSlot>();
		}

		internal static Texture2D EditedTexture(VisualSlot slot, string season, string modifier)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(_root, slot.Folder, season, modifier, slot.File);
			if (Textures.TryGetValue(text, out var value))
			{
				return value;
			}
			Texture2D val = null;
			try
			{
				byte[] array = VisualFiles.ReadEditedPng(text, slot.VanillaHash, _maxDimension.Value);
				if (array != null)
				{
					long num = VisualFiles.EstimatedTextureBytes(array);
					if (_cachedBytes + num > (long)_cacheMiB.Value * 1024L * 1024)
					{
						throw new InvalidDataException("Visual cache budget exceeded: " + text);
					}
					val = new Texture2D(2, 2, (TextureFormat)4, true, slot.Linear);
					if (!ImageConversion.LoadImage(val, array, true))
					{
						throw new InvalidDataException("Cannot decode visual PNG: " + text);
					}
					((Texture)val).filterMode = (FilterMode)(Enum.IsDefined(typeof(FilterMode), _textureFilter.Value) ? ((int)_textureFilter.Value) : 0);
					((Texture)val).anisoLevel = 1;
					((Texture)val).mipMapBias = 0f;
					((Object)val).name = "SSB_" + slot.Prefab + "_" + season + "_" + modifier;
					((Object)val).hideFlags = (HideFlags)52;
					((Texture)val).wrapMode = (TextureWrapMode)0;
					_cachedBytes += num;
				}
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				val = null;
				Warn(ex.GetBaseException().Message);
			}
			Textures[text] = val;
			return val;
		}

		internal static Color? EditedColor(VisualSlot slot, string season, string modifier)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(_root, slot.Folder, season, modifier, slot.File);
			if (Colors.TryGetValue(text, out var value))
			{
				return value;
			}
			Color? val = null;
			try
			{
				float[] array = VisualFiles.ReadEditedColor(text, slot.VanillaHash);
				if (array != null)
				{
					val = new Color(array[0], array[1], array[2], array[3]);
				}
			}
			catch (Exception ex)
			{
				Warn(ex.GetBaseException().Message);
			}
			Colors[text] = val;
			return val;
		}

		internal static void Shutdown()
		{
			Ready = false;
			foreach (CreatureVisual item in new List<CreatureVisual>(CreatureVisual.Active))
			{
				item.Stop();
			}
			foreach (Texture2D value in Textures.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			Textures.Clear();
			Colors.Clear();
			Reported.Clear();
			_cachedBytes = 0L;
			_catalog = null;
			_plugin = null;
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class VisualAwakePatch
	{
		private static void Postfix(Character __instance)
		{
			if (!VisualSystem.Ready || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				if (VisualSystem.Slots(Utils.GetPrefabName(((Component)__instance).gameObject)).Count > 0 && (Object)(object)((Component)__instance).GetComponent<CreatureVisual>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<CreatureVisual>();
				}
			}
			catch (Exception ex)
			{
				VisualSystem.Warn("Attach: " + ex.GetBaseException().Message);
			}
		}
	}
	internal sealed class CreatureVisual : MonoBehaviour
	{
		internal static readonly HashSet<CreatureVisual> Active = new HashSet<CreatureVisual>();

		private readonly List<VisualMaterial> _materials = new List<VisualMaterial>();

		private Character _creature;

		private string _signature;

		private float _next;

		private bool _stopped;

		private void Awake()
		{
			_creature = ((Component)this).GetComponent<Character>();
			Active.Add(this);
		}

		private void Update()
		{
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			if (_stopped || Time.unscaledTime < _next)
			{
				return;
			}
			_next = Time.unscaledTime + 1f;
			try
			{
				if (!VisualSystem.Ready || !VisualSystem.Enabled.Value || (Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.ApiReady || !Plugin.Instance.Eligible(_creature))
				{
					Restore();
					_signature = null;
					return;
				}
				ZNetView component = ((Component)_creature).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !API.IsAvailable)
				{
					Restore();
					_signature = null;
					return;
				}
				ZDO zDO = component.GetZDO();
				string obj = (zDO.GetBool("TheLionCid.SeasonalSLSBridge.processed", false) ? zDO.GetString("TheLionCid.SeasonalSLSBridge.season", "") : "");
				Dictionary<string, int> creaturesModifiers = API.GetCreaturesModifiers(_creature);
				KeyValuePair<string, string>[] array = VisualCatalog.CandidateVariants(obj, creaturesModifiers).ToArray();
				string text = obj + "/" + string.Join(",", array.Select((KeyValuePair<string, string> v) => v.Key + ":" + v.Value));
				if (text == _signature)
				{
					foreach (VisualMaterial material in _materials)
					{
						if (!material.IsIntact())
						{
							Stop();
							VisualSystem.Warn("Another visual system changed a custom material; yielding for this creature.");
							break;
						}
						material.Refresh();
					}
					return;
				}
				Restore();
				_signature = text;
				IReadOnlyList<VisualSlot> readOnlyList = VisualSystem.Slots(Utils.GetPrefabName(((Component)this).gameObject));
				KeyValuePair<string, string>[] array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					KeyValuePair<string, string> keyValuePair = array2[num];
					string key = keyValuePair.Key;
					string value = keyValuePair.Value;
					Dictionary<VisualSlot, Texture2D> textures = new Dictionary<VisualSlot, Texture2D>();
					Dictionary<VisualSlot, Color> colors = new Dictionary<VisualSlot, Color>();
					foreach (VisualSlot item in readOnlyList)
					{
						if (item.IsColor)
						{
							Color? val = VisualSystem.EditedColor(item, value, key);
							if (val.HasValue)
							{
								colors.Add(item, val.Value);
							}
						}
						else
						{
							Texture2D val2 = VisualSystem.EditedTexture(item, value, key);
							if ((Object)(object)val2 != (Object)null)
							{
								textures.Add(item, val2);
							}
						}
					}
					if (textures.Count == 0 && colors.Count == 0)
					{
						continue;
					}
					foreach (var item2 in from e in textures.Keys.Concat(colors.Keys)
						group e by new { e.RendererPath, e.Slot, e.Material })
					{
						VisualSlot visualSlot = item2.First();
						Transform val3 = ((visualSlot.RendererPath.Length == 0) ? ((Component)this).transform : ((Component)this).transform.Find(visualSlot.RendererPath));
						Renderer val4 = (((Object)(object)val3 == (Object)null) ? null : ((Component)val3).GetComponent<Renderer>());
						if ((Object)(object)val4 == (Object)null)
						{
							VisualSystem.Warn("Visual renderer missing: " + visualSlot.Prefab + "/" + visualSlot.RendererPath);
							continue;
						}
						try
						{
							_materials.Add(new VisualMaterial(val4, visualSlot.Slot, visualSlot.Material, item2.Where(textures.ContainsKey).ToDictionary((VisualSlot e) => e.Property, (VisualSlot e) => (Texture)(object)textures[e]), item2.Where(colors.ContainsKey).ToDictionary((VisualSlot e) => e.Property, (VisualSlot e) => colors[e])));
						}
						catch (Exception ex)
						{
							VisualSystem.Warn(ex.GetBaseException().Message);
						}
					}
					if (_materials.Count != 0)
					{
						break;
					}
				}
			}
			catch (Exception ex2)
			{
				Stop();
				VisualSystem.Warn(ex2.GetBaseException().Message + " Normal appearance retained.");
			}
		}

		private void Restore()
		{
			foreach (VisualMaterial material in _materials)
			{
				try
				{
					material.Dispose();
				}
				catch (Exception ex)
				{
					VisualSystem.Warn("Restore: " + ex.GetBaseException().Message);
				}
			}
			_materials.Clear();
		}

		internal void Stop()
		{
			_stopped = true;
			Restore();
		}

		private void OnDisable()
		{
			Restore();
			_signature = null;
		}

		private void OnDestroy()
		{
			Restore();
			Active.Remove(this);
		}
	}
	internal static class WorldContext
	{
		internal unsafe static bool TryGetBiome(Vector3 birthPosition, out string biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Biome val = Heightmap.FindBiome(birthPosition);
			if ((int)val == 0 && WorldGenerator.instance != null)
			{
				val = WorldGenerator.instance.GetBiome(birthPosition);
			}
			biome = ((object)(*(Biome*)(&val))/*cast due to .constrained prefix*/).ToString();
			return EcologyPolicy.Biomes.Contains(biome);
		}
	}
}