Decompiled source of SaunaMod v2.1.0

plugins/SaunaMod.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SaunaMod")]
[assembly: AssemblyDescription("A stone sauna stove for Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SaunaMod")]
[assembly: AssemblyCopyright("Copyright © Nekitker 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("2.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.0.0")]
namespace SaunaMod;

[BepInPlugin("nikita.valheim.sauna", "SaunaMod", "2.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
internal class SaunaPlugin : BaseUnityPlugin
{
	private static float _skinRedness;

	private float _skinLastSteamTime;

	private int _skinLastTimeTier;

	private static Material _saunaBucketSourceMeadMaterial;

	private static Material _saunaBucketWaterMaterial;

	private static Material _saunaBucketEmptyLiquidMaterial;

	private static readonly Material[] _saunaBucketMeadMaterials = (Material[])(object)new Material[4];

	private static readonly Material[] _saunaBucketMugMeadMaterials = (Material[])(object)new Material[4];

	private const float SaunaWaterGlossiness = 0.72f;

	public const string PluginGUID = "nikita.valheim.sauna";

	public const string PluginName = "SaunaMod";

	public const string PluginVersion = "2.1.0";

	private const string ConfigFileName = "nekitker.saunamod.cfg";

	private const string DefaultStoveRecipe = "Wood:10,Coal:5,Stone:40";

	private const string DefaultWhisksRecipe = "FineWood:5,BronzeNails:1";

	private const string DefaultBucketRecipe = "Iron:5,FineWood:10";

	private const int SteamLayer = 30;

	private const int SmokeLayer = 31;

	private const float CheckInterval = 0.5f;

	private static float SteamTimeToBuff = 20f;

	private static float SteamHealPerSecond = 0.5f;

	public static float SteamDamagePerSecond = 4f;

	private static float DetectRadius = 0.6f;

	private const float SteamVfxDuration = 60f;

	private const float PlayerVfxHeight = 1.3f;

	private static readonly float[] WellSteamedTimeTiers = new float[3] { 300f, 600f, 900f };

	private ConfigFile _saunaConfig;

	private ConfigEntry<bool> _cfgEnableEditor;

	private ConfigEntry<bool> _cfgEnableSaunaComfort;

	private ConfigEntry<string> _cfgStoveRecipe;

	private ConfigEntry<string> _cfgWhisksRecipe;

	private ConfigEntry<string> _cfgBucketRecipe;

	private ConfigEntry<float> _cfgSteamTimeToBuff;

	private ConfigEntry<float> _cfgSteamHealPerSecond;

	private ConfigEntry<float> _cfgDetectRadius;

	private ConfigEntry<float> _cfgSteamGrace;

	private ConfigEntry<float> _cfgWellSteamedTier1Minutes;

	private ConfigEntry<float> _cfgWellSteamedTier2Minutes;

	private ConfigEntry<float> _cfgWellSteamedTier3Minutes;

	private ConfigEntry<float> _cfgMeadDurationMultiplier;

	private ConfigEntry<float> _cfgSteamLifetime;

	private ConfigEntry<float> _cfgSteamFadeTime;

	private ConfigEntry<float> _cfgSteamSpread;

	private ConfigEntry<float> _cfgSteamRise;

	private ConfigEntry<float> _cfgSteamForce;

	private ConfigEntry<float> _cfgSteamPush;

	private ConfigEntry<float> _cfgSteamCloudSize;

	private ConfigEntry<int> _cfgCloudsPerPour;

	private ConfigEntry<int> _cfgCloudsPerPourWithBucket;

	private ConfigEntry<int> _cfgMaxClouds;

	public static int WellSteamedHash;

	private static GameObject _prefabContainer;

	private static GameObject _steamVfxPrefab;

	private static GameObject _stovePrefab;

	private static GameObject _wrisksPrefab;

	private static Sprite _wrisksFallbackIcon;

	private static GameObject _bucketPrefab;

	private static int _iconRenderCount;

	private static SaunaPlugin _instance;

	private static SE_Stats _steaming;

	private static SE_WellSteamed _wellSteamed;

	private static SE_Burning _tooHot;

	private float _checkTimer;

	private float _steamTime;

	private int _timeTier;

	private float _gapTime;

	private bool _worldReady;

	private static readonly CustomLocalization _loc = LocalizationManager.Instance.GetLocalization();

	private string StoveRecipeValue => (_cfgStoveRecipe != null) ? _cfgStoveRecipe.Value : "Wood:10,Coal:5,Stone:40";

	private string WhisksRecipeValue => (_cfgWhisksRecipe != null) ? _cfgWhisksRecipe.Value : "FineWood:5,BronzeNails:1";

	private string BucketRecipeValue => (_cfgBucketRecipe != null) ? _cfgBucketRecipe.Value : "Iron:5,FineWood:10";

	internal static bool EditorEnabled { get; private set; }

	internal static bool SaunaComfortEnabled { get; private set; } = true;

	private string FindAssetDir(string sub)
	{
		string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), sub);
		if (Directory.Exists(text))
		{
			return text;
		}
		try
		{
			string[] directories = Directory.GetDirectories(Paths.PluginPath, sub, SearchOption.AllDirectories);
			if (directories.Length != 0)
			{
				Logger.LogWarning((object)("'" + sub + "' not next to dll, using " + directories[0]));
				return directories[0];
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("asset search failed: " + ex.Message));
		}
		Logger.LogWarning((object)("folder '" + sub + "' not found, expected at " + text));
		return null;
	}

	private IEnumerator LoadPourSound()
	{
		string dir = FindAssetDir("sounds");
		if (string.IsNullOrEmpty(dir))
		{
			yield break;
		}
		string[] files = new string[3] { "pour.wav", "pour.ogg", "pour.mp3" };
		string[] array = files;
		foreach (string file in array)
		{
			string path = Path.Combine(dir, file);
			if (!File.Exists(path))
			{
				continue;
			}
			AudioType type = (AudioType)13;
			if (file.EndsWith(".wav"))
			{
				type = (AudioType)20;
			}
			else if (file.EndsWith(".ogg"))
			{
				type = (AudioType)14;
			}
			string url = new Uri(path).AbsoluteUri;
			UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, type);
			try
			{
				yield return req.SendWebRequest();
				if ((int)req.result != 1)
				{
					Logger.LogWarning((object)("sound load failed " + file + ": " + req.error));
					continue;
				}
				AudioClip clip = DownloadHandlerAudioClip.GetContent(req);
				if ((Object)(object)clip == (Object)null || clip.length <= 0f)
				{
					Logger.LogWarning((object)("sound decoded empty: " + file));
					continue;
				}
				((Object)clip).name = "sauna_pour";
				SaunaStove.PourClip = clip;
				Logger.LogInfo((object)$"pour sound loaded: {file}, {clip.length:0.00}s");
				yield break;
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}
		Logger.LogWarning((object)("no pour sound file in " + dir));
	}

	private static ConfigDescription SyncedConfigDescription(string description, AcceptableValueBase acceptableValues = null)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		return new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} });
	}

	private void OnSaunaConfigSettingChanged(object sender, SettingChangedEventArgs args)
	{
		ApplyConfigValues();
		ApplyRecipeConfigToRegisteredPieces();
	}

	private void BindConfig()
	{
		_cfgEnableEditor = _saunaConfig.Bind<bool>("General", "EnableEditor", false, SyncedConfigDescription("Enables the in-game SaunaMod live editor. Press F7 to open it. Intended for testing and tuning."));
		_cfgEnableSaunaComfort = _saunaConfig.Bind<bool>("Comfort", "EnableSaunaComfort", true, SyncedConfigDescription("Enables conditional sauna comfort. Whisks and bucket each add +1 comfort only near a burning sauna stove while sheltered."));
		_cfgStoveRecipe = _saunaConfig.Bind<string>("Recipes", "SaunaStove", "Wood:10,Coal:5,Stone:40", SyncedConfigDescription("Crafting recipe for the sauna stove. Format: ItemPrefab:Amount,ItemPrefab:Amount. Example: Wood:10,Coal:5,Stone:40."));
		_cfgWhisksRecipe = _saunaConfig.Bind<string>("Recipes", "SaunaWhisks", "FineWood:5,BronzeNails:1", SyncedConfigDescription("Crafting recipe for the sauna whisks. Format: ItemPrefab:Amount,ItemPrefab:Amount. Example: FineWood:5,BronzeNails:1."));
		_cfgBucketRecipe = _saunaConfig.Bind<string>("Recipes", "SaunaBucket", "Iron:5,FineWood:10", SyncedConfigDescription("Crafting recipe for the sauna bucket. Format: ItemPrefab:Amount,ItemPrefab:Amount. Example: Iron:5,FineWood:10."));
		_cfgSteamTimeToBuff = _saunaConfig.Bind<float>("Gameplay", "SecondsToWellSteamed", 20f, SyncedConfigDescription("Seconds of accumulated contact with sauna steam required to gain or advance Well Steamed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 300f)));
		_cfgSteamHealPerSecond = _saunaConfig.Bind<float>("Gameplay", "SteamHealPerSecond", 0.5f, SyncedConfigDescription("Health restored per second while the player is physically inside sauna steam. Set to 0 to disable healing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
		_cfgDetectRadius = _saunaConfig.Bind<float>("Gameplay", "SteamDetectionRadius", 0.6f, SyncedConfigDescription("Radius in metres used to detect whether the player is touching a sauna steam cloud.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f)));
		_cfgSteamGrace = _saunaConfig.Bind<float>("Gameplay", "SteamContactGraceSeconds", 3f, SyncedConfigDescription("How long a short gap between steam clouds may last without resetting steaming progress.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f)));
		_cfgWellSteamedTier1Minutes = _saunaConfig.Bind<float>("WellSteamed", "Tier1DurationMinutes", 5f, SyncedConfigDescription("Duration of the first Well Steamed time tier in minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f)));
		_cfgWellSteamedTier2Minutes = _saunaConfig.Bind<float>("WellSteamed", "Tier2DurationMinutes", 10f, SyncedConfigDescription("Duration of the second Well Steamed time tier in minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f)));
		_cfgWellSteamedTier3Minutes = _saunaConfig.Bind<float>("WellSteamed", "Tier3DurationMinutes", 15f, SyncedConfigDescription("Duration of the third Well Steamed time tier in minutes. This tier requires sauna whisks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f)));
		_cfgMeadDurationMultiplier = _saunaConfig.Bind<float>("Mead", "DurationMultiplier", 1.2f, SyncedConfigDescription("Multiplier applied to the normal duration of resistance mead distributed through the sauna bucket.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f)));
		_cfgSteamLifetime = _saunaConfig.Bind<float>("Steam", "CloudLifetimeSeconds", 40f, SyncedConfigDescription("Lifetime of a sauna steam cloud before it begins to fade.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(25f, 180f)));
		_cfgSteamFadeTime = _saunaConfig.Bind<float>("Steam", "CloudFadeSeconds", 4.4f, SyncedConfigDescription("Time a steam cloud takes to fade after its lifetime ends.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f)));
		_cfgSteamSpread = _saunaConfig.Bind<float>("Steam", "HorizontalSpread", 1f, SyncedConfigDescription("Horizontal target speed of steam clouds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
		_cfgSteamRise = _saunaConfig.Bind<float>("Steam", "RiseSpeed", 1.45f, SyncedConfigDescription("Vertical target speed of steam clouds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
		_cfgSteamForce = _saunaConfig.Bind<float>("Steam", "MovementForce", 0.25f, SyncedConfigDescription("How quickly steam accelerates toward its target movement.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
		_cfgSteamPush = _saunaConfig.Bind<float>("Steam", "CloudPush", 0.5f, SyncedConfigDescription("How strongly overlapping steam clouds separate from each other.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
		_cfgSteamCloudSize = _saunaConfig.Bind<float>("Steam", "CloudSize", 2.9f, SyncedConfigDescription("Physical size of each steam cloud. This mainly affects distribution and detection, not the smoke sprite itself.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f)));
		_cfgCloudsPerPour = _saunaConfig.Bind<int>("Steam", "CloudsPerPour", 30, SyncedConfigDescription("Number of steam clouds created by a normal pour.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200)));
		_cfgCloudsPerPourWithBucket = _saunaConfig.Bind<int>("Steam", "CloudsPerPourWithBucket", 50, SyncedConfigDescription("Number of steam clouds created when the sauna has the bucket upgrade.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200)));
		_cfgMaxClouds = _saunaConfig.Bind<int>("Steam", "MaxClouds", 100, SyncedConfigDescription("Global smoke/steam cloud cap used by SaunaMod. Very high values may be visually wasteful or affect performance.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1000)));
	}

	private void ApplyConfigValues()
	{
		EditorEnabled = _cfgEnableEditor != null && _cfgEnableEditor.Value;
		SaunaComfortEnabled = _cfgEnableSaunaComfort == null || _cfgEnableSaunaComfort.Value;
		SteamTimeToBuff = Mathf.Max(1f, _cfgSteamTimeToBuff.Value);
		SteamHealPerSecond = Mathf.Max(0f, _cfgSteamHealPerSecond.Value);
		DetectRadius = Mathf.Max(0.1f, _cfgDetectRadius.Value);
		SteamTuning.Grace = Mathf.Max(0f, _cfgSteamGrace.Value);
		WellSteamedTimeTiers[0] = Mathf.Max(30f, _cfgWellSteamedTier1Minutes.Value * 60f);
		WellSteamedTimeTiers[1] = Mathf.Max(WellSteamedTimeTiers[0], _cfgWellSteamedTier2Minutes.Value * 60f);
		WellSteamedTimeTiers[2] = Mathf.Max(WellSteamedTimeTiers[1], _cfgWellSteamedTier3Minutes.Value * 60f);
		SaunaMeadTuning.DurationMultiplier = Mathf.Max(0.1f, _cfgMeadDurationMultiplier.Value);
		SteamTuning.Lifetime = Mathf.Max(25f, _cfgSteamLifetime.Value);
		SteamTuning.FadeTime = Mathf.Max(0.1f, _cfgSteamFadeTime.Value);
		SteamTuning.Spread = Mathf.Max(0f, _cfgSteamSpread.Value);
		SteamTuning.Rise = Mathf.Max(0f, _cfgSteamRise.Value);
		SteamTuning.Force = Mathf.Max(0f, _cfgSteamForce.Value);
		SteamTuning.Push = Mathf.Max(0f, _cfgSteamPush.Value);
		SteamTuning.CloudSize = Mathf.Max(0.1f, _cfgSteamCloudSize.Value);
		SteamTuning.CloudsPerPour = Mathf.Max(1, _cfgCloudsPerPour.Value);
		SteamTuning.CloudsPerPourWithBucket = Mathf.Max(1, _cfgCloudsPerPourWithBucket.Value);
		SteamTuning.MaxClouds = Mathf.Max(10, _cfgMaxClouds.Value);
	}

	private static RequirementConfig[] ParseRecipeConfig(string recipe, string fallbackRecipe, string label)
	{
		RequirementConfig[] array = TryParseRecipeConfig(recipe);
		if (array != null && RecipeItemsAvailable(array))
		{
			return array;
		}
		Logger.LogWarning((object)(label + " recipe '" + recipe + "' is invalid or contains an unknown item; using default '" + fallbackRecipe + "'"));
		array = TryParseRecipeConfig(fallbackRecipe);
		return (RequirementConfig[])(((object)array) ?? ((object)new RequirementConfig[0]));
	}

	private static bool RecipeItemsAvailable(RequirementConfig[] requirements)
	{
		if (requirements == null)
		{
			return true;
		}
		foreach (RequirementConfig val in requirements)
		{
			if ((Object)(object)FindRecipeItem(val.Item) == (Object)null)
			{
				return false;
			}
		}
		return true;
	}

	private static ItemDrop FindRecipeItem(string itemName)
	{
		GameObject val = (((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items.Count > 0) ? ObjectDB.instance.GetItemPrefab(itemName) : PrefabManager.Instance.GetPrefab(itemName));
		return ((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null;
	}

	private static RequirementConfig[] TryParseRecipeConfig(string recipe)
	{
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Expected O, but got Unknown
		if (recipe == null)
		{
			return null;
		}
		string text = recipe.Trim();
		if (text.Length == 0)
		{
			return (RequirementConfig[])(object)new RequirementConfig[0];
		}
		List<RequirementConfig> list = new List<RequirementConfig>();
		string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
		string[] array2 = array;
		foreach (string text2 in array2)
		{
			string text3 = text2.Trim();
			int num = text3.LastIndexOf(':');
			if (num <= 0 || num >= text3.Length - 1)
			{
				return null;
			}
			string text4 = text3.Substring(0, num).Trim();
			string s = text3.Substring(num + 1).Trim();
			if (text4.Length == 0 || !int.TryParse(s, out var result) || result < 0)
			{
				return null;
			}
			if (result != 0)
			{
				list.Add(new RequirementConfig
				{
					Item = text4,
					Amount = result,
					Recover = true
				});
			}
		}
		return list.ToArray();
	}

	private void ApplyRecipeConfigToRegisteredPieces()
	{
		if (!((Object)(object)ObjectDB.instance == (Object)null))
		{
			ApplyRecipeToPiece(_stovePrefab, StoveRecipeValue, "Wood:10,Coal:5,Stone:40", "sauna_stove");
			ApplyRecipeToPiece(_bucketPrefab, BucketRecipeValue, "Iron:5,FineWood:10", "sauna_bucket");
			GameObject val = ResolveSaunaWrisksRuntimePrefab();
			if ((Object)(object)val != (Object)null)
			{
				_wrisksPrefab = val;
			}
			ApplyRecipeToPiece(_wrisksPrefab, WhisksRecipeValue, "FineWood:5,BronzeNails:1", "sauna_wrisks");
		}
	}

	private static void ApplyRecipeToPiece(GameObject prefab, string recipe, string fallbackRecipe, string label)
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Expected O, but got Unknown
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		Piece component = prefab.GetComponent<Piece>();
		if ((Object)(object)component == (Object)null)
		{
			return;
		}
		RequirementConfig[] array = ParseRecipeConfig(recipe, fallbackRecipe, label);
		List<Requirement> list = new List<Requirement>();
		RequirementConfig[] array2 = array;
		foreach (RequirementConfig val in array2)
		{
			ItemDrop val2 = FindRecipeItem(val.Item);
			if ((Object)(object)val2 == (Object)null)
			{
				Logger.LogWarning((object)(label + " recipe item '" + val.Item + "' was not found; using default recipe"));
				if (!string.Equals(recipe, fallbackRecipe, StringComparison.Ordinal))
				{
					ApplyRecipeToPiece(prefab, fallbackRecipe, fallbackRecipe, label);
				}
				return;
			}
			list.Add(new Requirement
			{
				m_resItem = val2,
				m_amount = val.Amount,
				m_amountPerLevel = 0,
				m_recover = val.Recover
			});
		}
		component.m_resources = list.ToArray();
	}

	private void OnVanillaPrefabsAvailable()
	{
		PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
		CreateStatusEffects();
		AddSaunaStove();
		AddSaunaWrisks();
		AddSaunaBucket();
		AddPlayerComponent();
		PieceManager.OnPiecesRegistered -= OnPiecesRegistered;
		PieceManager.OnPiecesRegistered += OnPiecesRegistered;
	}

	private void AddPlayerComponent()
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab("Player");
		if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<SaunaPlayer>() == (Object)null)
		{
			prefab.AddComponent<SaunaPlayer>();
		}
	}

	private void CreateStatusEffects()
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Expected O, but got Unknown
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		try
		{
			_tooHot = ScriptableObject.CreateInstance<SE_Burning>();
			InitBurnEffect(_tooHot, "SaunaTooHot", "se_sauna_too_hot", 0f);
			_steaming = ScriptableObject.CreateInstance<SE_Stats>();
			InitEffect(_steaming, "SaunaSteaming", "se_sauna_steaming", 0f);
			_wellSteamed = ScriptableObject.CreateInstance<SE_WellSteamed>();
			InitEffect((SE_Stats)(object)_wellSteamed, "SaunaWellSteamed", "se_sauna_wellsteamed", WellSteamedTimeTiers[0]);
			WellSteamedHash = ((StatusEffect)_wellSteamed).NameHash();
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)_tooHot, false));
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)_steaming, false));
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)_wellSteamed, false));
			Logger.LogInfo((object)$"effects created: wellSteamed={WellSteamedHash}");
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"status effects failed: {arg}");
		}
	}

	private void InitEffect(SE_Stats se, string objectName, string token, float ttl)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		((Object)se).name = objectName;
		((StatusEffect)se).m_name = "$" + token;
		((StatusEffect)se).m_tooltip = "$" + token + "_tooltip";
		((StatusEffect)se).m_ttl = ttl;
		((StatusEffect)se).m_startMessage = "$" + token + "_start";
		((StatusEffect)se).m_startMessageType = (MessageType)1;
		se.m_healthRegenMultiplier = 1f;
		se.m_staminaRegenMultiplier = 1f;
		se.m_eitrRegenMultiplier = 1f;
	}

	private void InitBurnEffect(SE_Burning se, string objectName, string token, float ttl)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		((Object)se).name = objectName;
		((StatusEffect)se).m_name = "$" + token;
		((StatusEffect)se).m_tooltip = "$" + token + "_tooltip";
		((StatusEffect)se).m_ttl = ttl;
		((StatusEffect)se).m_startMessage = "$" + token + "_start";
		((StatusEffect)se).m_startMessageType = (MessageType)1;
		se.m_fireDamagePerHit = SteamDamagePerSecond;
		((StatusEffect)se).m_flashIcon = true;
	}

	private void ReadWetPenalty()
	{
		StatusEffect obj = FindEffect("Wet");
		SE_Stats val = (SE_Stats)(object)((obj is SE_Stats) ? obj : null);
		if ((Object)(object)val == (Object)null)
		{
			Logger.LogWarning((object)"Wet effect not found");
			return;
		}
		SE_WellSteamed.WetStaminaMultiplier = val.m_staminaRegenMultiplier;
		SE_WellSteamed.WetHealthMultiplier = val.m_healthRegenMultiplier;
		SE_WellSteamed.WetEitrMultiplier = val.m_eitrRegenMultiplier;
		Logger.LogInfo((object)($"Wet compensated: stamina={val.m_staminaRegenMultiplier}, " + $"health={val.m_healthRegenMultiplier}, eitr={val.m_eitrRegenMultiplier}"));
	}

	private void FindSteamVfx()
	{
		StatusEffect val = FindEffect("Smoked");
		if ((Object)(object)val == (Object)null || val.m_startEffects == null || val.m_startEffects.m_effectPrefabs == null)
		{
			Logger.LogWarning((object)"Smoked effects not found");
			return;
		}
		EffectData[] effectPrefabs = val.m_startEffects.m_effectPrefabs;
		foreach (EffectData val2 in effectPrefabs)
		{
			if (val2 != null && !((Object)(object)val2.m_prefab == (Object)null) && (Object)(object)val2.m_prefab.GetComponentInChildren<ParticleSystem>(true) != (Object)null && (Object)(object)_steamVfxPrefab == (Object)null)
			{
				_steamVfxPrefab = val2.m_prefab;
			}
		}
		Logger.LogInfo((object)(((Object)(object)_steamVfxPrefab != (Object)null) ? ("player vfx source: " + ((Object)_steamVfxPrefab).name) : "no particle vfx among Smoked effects"));
	}

	private StatusEffect FindEffect(string objectName)
	{
		foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects)
		{
			if ((Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == objectName)
			{
				return statusEffect;
			}
		}
		return null;
	}

	public static void SpawnSteamVfxOn(GameObject target)
	{
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_steamVfxPrefab == (Object)null || (Object)(object)target == (Object)null || (Object)(object)_prefabContainer == (Object)null || (Object)(object)target.transform.Find("vfx_sauna_player_steam") != (Object)null)
		{
			return;
		}
		try
		{
			GameObject val = Object.Instantiate<GameObject>(_steamVfxPrefab, _prefabContainer.transform);
			((Object)val).name = "vfx_sauna_player_steam";
			TimedDestruction[] componentsInChildren = val.GetComponentsInChildren<TimedDestruction>(true);
			foreach (TimedDestruction val2 in componentsInChildren)
			{
				Object.DestroyImmediate((Object)(object)val2);
			}
			ZSyncTransform[] componentsInChildren2 = val.GetComponentsInChildren<ZSyncTransform>(true);
			foreach (ZSyncTransform val3 in componentsInChildren2)
			{
				Object.DestroyImmediate((Object)(object)val3);
			}
			ZNetView[] componentsInChildren3 = val.GetComponentsInChildren<ZNetView>(true);
			foreach (ZNetView val4 in componentsInChildren3)
			{
				Object.DestroyImmediate((Object)(object)val4);
			}
			ParticleSystem[] componentsInChildren4 = val.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val5 in componentsInChildren4)
			{
				MainModule main = val5.main;
				((MainModule)(ref main)).loop = true;
			}
			SaunaVfxLife saunaVfxLife = val.AddComponent<SaunaVfxLife>();
			saunaVfxLife.Remaining = 60f;
			val.transform.SetParent(target.transform, false);
			val.transform.localPosition = new Vector3(0f, 1.3f, 0f);
			val.transform.localRotation = Quaternion.identity;
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("vfx spawn failed: " + ex.Message));
		}
	}

	private void Update()
	{
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: 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_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_steaming == (Object)null || (Object)(object)_wellSteamed == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			_steamTime = 0f;
			_gapTime = 0f;
			_timeTier = 0;
			_worldReady = false;
			ResetSkinRedness();
			return;
		}
		if (!_worldReady)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			_worldReady = true;
			ApplySteamCollisions();
			EnsureIcons();
			ReadWetPenalty();
			FindSteamVfx();
			StoveVisual.CollectCandidates();
			SaunaStove.RebuildAll();
			EnsureSaunaWrisksRuntimeRegistration(localPlayer);
			((MonoBehaviour)this).StartCoroutine(RefreshWrisksAfterSpawn(localPlayer));
			RefreshPieceIcon();
			RefreshWrisksIcon();
			RefreshBucketIcon();
		}
		SaunaEditor.Update();
		FreezeWellSteamed(localPlayer);
		_checkTimer += Time.deltaTime;
		if (_checkTimer < 0.5f)
		{
			return;
		}
		float checkTimer = _checkTimer;
		_checkTimer = 0f;
		SEMan sEMan = ((Character)localPlayer).GetSEMan();
		UpdateSkinRedness(localPlayer, sEMan, checkTimer);
		int num = 1073741824;
		Vector3 topPoint = ((Character)localPlayer).GetTopPoint();
		Vector3 position = ((Component)localPlayer).transform.position;
		bool flag = sEMan.HaveStatusEffect(SEMan.s_statusEffectShelter) && (Physics.CheckSphere(topPoint + Vector3.up * 0.1f, DetectRadius, num) || Physics.CheckSphere(topPoint - Vector3.up * 0.5f, DetectRadius, num) || Physics.CheckSphere(position + Vector3.up * 0.5f, DetectRadius, num));
		List<ItemData> equippedItems = ((Humanoid)localPlayer).m_inventory.GetEquippedItems();
		equippedItems.RemoveAll((ItemData item) => ((int)item.m_shared.m_itemType == 19 || (int)item.m_shared.m_itemType == 3 || (int)item.m_shared.m_itemType == 14 || (int)item.m_shared.m_itemType == 22 || (int)item.m_shared.m_itemType == 5 || (int)item.m_shared.m_itemType == 15 || (int)item.m_shared.m_itemType == 9 || (int)item.m_shared.m_itemType == 24 || (int)item.m_shared.m_itemType == 18) && (int)item.m_shared.m_itemType != 17);
		bool flag2 = equippedItems.Count > 0;
		if (flag)
		{
			if (!sEMan.HaveStatusEffect(((StatusEffect)_steaming).NameHash()))
			{
				sEMan.AddStatusEffect((StatusEffect)(object)_steaming, false, 0, 0f, (short)(-1));
			}
			if (flag2)
			{
				if (!sEMan.HaveStatusEffect(((StatusEffect)_tooHot).NameHash()))
				{
					((Character)localPlayer).Message((MessageType)2, _loc.TryTranslate("$msg_sauna_too_hot"), 0, (Sprite)null, false);
					sEMan.AddStatusEffect((StatusEffect)(object)_tooHot, false, 0, 0f, (short)(-1));
				}
			}
			else
			{
				((Character)localPlayer).Heal(SteamHealPerSecond * checkTimer, false);
				_steamTime += checkTimer + _gapTime;
				_gapTime = 0f;
				if (_steamTime >= SteamTimeToBuff)
				{
					_steamTime = 0f;
					GrantWellSteamed(localPlayer, sEMan);
				}
			}
		}
		else if (_gapTime + checkTimer < SteamTuning.Grace && sEMan.HaveStatusEffect(((StatusEffect)_steaming).NameHash()))
		{
			_gapTime += checkTimer;
		}
		else
		{
			if (sEMan.HaveStatusEffect(((StatusEffect)_tooHot).NameHash()))
			{
				sEMan.RemoveStatusEffect(((StatusEffect)_tooHot).NameHash(), true);
			}
			sEMan.RemoveStatusEffect(((StatusEffect)_steaming).NameHash(), true);
			_steamTime = 0f;
			_gapTime = 0f;
			_timeTier = 0;
		}
		if (!flag2 && sEMan.HaveStatusEffect(((StatusEffect)_tooHot).NameHash()))
		{
			sEMan.RemoveStatusEffect(((StatusEffect)_tooHot).NameHash(), true);
		}
	}

	private void FreezeWellSteamed(Player player)
	{
		if ((Object)(object)_steaming == (Object)null)
		{
			return;
		}
		SEMan sEMan = ((Character)player).GetSEMan();
		if (sEMan.HaveStatusEffect(((StatusEffect)_steaming).NameHash()))
		{
			StatusEffect statusEffect = sEMan.GetStatusEffect(WellSteamedHash);
			if ((Object)(object)statusEffect != (Object)null)
			{
				statusEffect.m_time = Mathf.Max(0f, statusEffect.m_time - Time.deltaTime);
			}
		}
	}

	private void GrantWellSteamed(Player player, SEMan seman)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		StatusEffect statusEffect = seman.GetStatusEffect(WellSteamedHash);
		float num = (((Object)(object)statusEffect != (Object)null) ? statusEffect.GetRemaningTime() : 0f);
		SE_WellSteamed sE_WellSteamed = statusEffect as SE_WellSteamed;
		int num2 = (((Object)(object)sE_WellSteamed != (Object)null) ? sE_WellSteamed.TimeTier : 0);
		int num3 = ((!((Object)(object)sE_WellSteamed != (Object)null)) ? 1 : Mathf.Clamp(sE_WellSteamed.SaunaTier, 1, 3));
		int wellSteamedSaunaTierNear = SaunaStove.GetWellSteamedSaunaTierNear(((Component)player).transform.position);
		int num4 = Mathf.Max(num3, wellSteamedSaunaTierNear);
		for (int i = 0; i < WellSteamedTimeTiers.Length; i++)
		{
			if (num + 1f >= WellSteamedTimeTiers[i])
			{
				num2 = Mathf.Max(num2, i + 1);
			}
		}
		int num5 = MaxEarnableTimeTier(wellSteamedSaunaTierNear);
		_timeTier = Mathf.Max(num2, Mathf.Min(num2 + 1, num5));
		_timeTier = Mathf.Clamp(_timeTier, 1, WellSteamedTimeTiers.Length);
		float num6 = Mathf.Max(WellSteamedTimeTiers[_timeTier - 1], num);
		seman.AddStatusEffect((StatusEffect)(object)_wellSteamed, true, 0, 0f, (short)(-1));
		seman.RemoveStatusEffect(SEMan.s_statusEffectCold, true);
		StatusEffect statusEffect2 = seman.GetStatusEffect(WellSteamedHash);
		if ((Object)(object)statusEffect2 != (Object)null)
		{
			statusEffect2.m_ttl = num6;
			SE_WellSteamed sE_WellSteamed2 = statusEffect2 as SE_WellSteamed;
			if ((Object)(object)sE_WellSteamed2 != (Object)null)
			{
				sE_WellSteamed2.TimeTier = _timeTier;
				sE_WellSteamed2.SaunaTier = num4;
			}
			statusEffect2.m_name = "$se_sauna_wellsteamed";
			statusEffect2.m_tooltip = ((num4 >= 2) ? "$se_sauna_wellsteamed_tooltip_whisks" : "$se_sauna_wellsteamed_tooltip");
		}
		if (_timeTier > num2 && _timeTier > 1)
		{
			if (_timeTier >= num5)
			{
				((Character)player).Message((MessageType)2, _loc.TryTranslate("$msg_sauna_max_tier"), 0, (Sprite)null, false);
			}
			else
			{
				((Character)player).Message((MessageType)1, _loc.TryTranslate("$msg_sauna_tier"), 0, (Sprite)null, false);
			}
		}
		SaunaPlayer component = ((Component)player).GetComponent<SaunaPlayer>();
		if ((Object)(object)component != (Object)null)
		{
			component.Broadcast();
		}
		else
		{
			SpawnSteamVfxOn(((Component)player).gameObject);
		}
		Logger.LogInfo((object)($"Well Steamed: timeTier={_timeTier} (had {num2}), " + $"saunaTier={num4} (near={wellSteamedSaunaTierNear}, had={num3}), " + $"ttl={num6:0}, was={num:0}"));
	}

	private static int MaxEarnableTimeTier(int saunaTier)
	{
		return (saunaTier >= 2) ? WellSteamedTimeTiers.Length : Mathf.Min(2, WellSteamedTimeTiers.Length);
	}

	private void ResetSkinRedness()
	{
		_skinRedness = 0f;
		_skinLastSteamTime = 0f;
		_skinLastTimeTier = 0;
	}

	private void UpdateSkinRedness(Player player, SEMan seman, float elapsed)
	{
		float num = ((_timeTier != _skinLastTimeTier || !(_steamTime >= _skinLastSteamTime)) ? (Mathf.Max(0f, SteamTimeToBuff - _skinLastSteamTime) + _steamTime) : (_steamTime - _skinLastSteamTime));
		_skinLastSteamTime = _steamTime;
		_skinLastTimeTier = _timeTier;
		if (seman.HaveStatusEffect(((StatusEffect)_steaming).NameHash()))
		{
			if (num > 0f)
			{
				HeatSkin(player, seman, num);
			}
		}
		else
		{
			_skinRedness = Mathf.Max(0f, _skinRedness - elapsed / Mathf.Max(1f, SkinRednessTuning.CoolSeconds));
		}
		ApplySkinRedness(player);
	}

	private void HeatSkin(Player player, SEMan seman, float progress)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		int num = MaxEarnableTimeTier(SaunaStove.GetWellSteamedSaunaTierNear(((Component)player).transform.position));
		if (_timeTier >= num)
		{
			_skinRedness = 1f;
			return;
		}
		SE_WellSteamed sE_WellSteamed = seman.GetStatusEffect(WellSteamedHash) as SE_WellSteamed;
		int num2 = Mathf.Max(_timeTier, ((Object)(object)sE_WellSteamed != (Object)null) ? sE_WellSteamed.TimeTier : 0);
		int num3 = Mathf.Clamp(Mathf.Max(num2, Mathf.Min(num2 + 1, num)), 1, WellSteamedTimeTiers.Length);
		float num4 = Mathf.Max(0f, SteamTimeToBuff - _steamTime) + (float)Mathf.Max(0, num - num3) * SteamTimeToBuff;
		_skinRedness += (1f - _skinRedness) * Mathf.Clamp01(progress / (num4 + progress));
	}

	private static float DisplayedSkinRedness()
	{
		return (SaunaEditor.Active && SkinRednessTuning.Preview > 0) ? 1f : _skinRedness;
	}

	internal static void ApplySkinRedness(Player player)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)player == (Object)null) && !((Object)(object)((Humanoid)player).m_visEquipment == (Object)null))
		{
			((Humanoid)player).m_visEquipment.SetSkinColor(SkinRednessTuning.Apply(player.m_skinColor, DisplayedSkinRedness()));
		}
	}

	private void AddLocalization()
	{
		CustomLocalization loc = _loc;
		string text = "English";
		loc.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "piece_sauna_stove", "Sauna stove" },
			{ "piece_sauna_stove_desc", "A stone sauna stove. Pour on water and enjoy a proper steam." },
			{ "piece_sauna_pour", "Pour water" },
			{ "msg_sauna_pour", "Water hits the hot stones" },
			{ "msg_sauna_notburning", "The stove is cold" },
			{ "msg_sauna_wait", "The stones need to heat up again" },
			{ "msg_sauna_tier", "The heat sinks deeper" },
			{ "msg_sauna_max_tier", "Now that’s a proper steam!" },
			{ "msg_sauna_bucket_use_mead", "Pour in mead" },
			{ "msg_sauna_bucket_choose_mead", "More than one resistance mead is available. Aim at the bucket and use the one you want from the hotbar" },
			{ "msg_sauna_bucket_no_mead", "You have no supported resistance mead" },
			{ "msg_sauna_bucket_resistance_only", "Only resistance mead is suitable for the sauna" },
			{ "msg_sauna_bucket_contains", "Infused with" },
			{ "msg_sauna_bucket_full", "The sauna bucket already contains mead" },
			{ "msg_sauna_bucket_filled", "Added {0} to the sauna bucket" },
			{ "msg_sauna_mead_aroma", "The aroma of {0} fills the sauna" },
			{ "msg_sauna_too_hot", "Too Hot! Take your clothes off" },
			{ "se_sauna_too_hot", "Too hot" },
			{ "se_sauna_too_hot_tooltip", "You need to be naked in the sauna, so that you don't get too hot!" },
			{ "se_sauna_too_hot_start", "You are feeling too hot!" },
			{ "se_sauna_steaming", "Steaming" },
			{ "se_sauna_steaming_tooltip", "The steam slowly restores health and eases whatever ails you." },
			{ "se_sauna_steaming_start", "You step into the steam" },
			{ "se_sauna_wellsteamed", "Well steamed" },
			{ "se_sauna_wellsteamed_tooltip", "The heat stays with you.\nYou do not feel the cold." },
			{ "se_sauna_wellsteamed_tooltip_whisks", "The heat stays with you.\nYou do not feel the cold.\nSauna whisks make wetness harmless and unlock 15-minute warming." },
			{ "se_sauna_wellsteamed_start", "You are well steamed" },
			{ "piece_sauna_wrisks", "Sauna whisks" },
			{ "piece_sauna_wrisks_desc", "A pair of birch sauna whisks. They help you steam properly and shrug off the discomfort of being wet." },
			{ "piece_sauna_bucket", "Sauna bucket with ladle" },
			{ "piece_sauna_bucket_desc", "Lets you throw more water on the stones and infuse the steam with mead, so you can enjoy its aroma and absorb its effects." }
		});
		CustomLocalization loc2 = _loc;
		text = "Russian";
		loc2.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "piece_sauna_stove", "Банная печь" },
			{ "piece_sauna_stove_desc", "Каменная банная печь. Поддай пару и как следует пропарься." },
			{ "piece_sauna_pour", "Поддать пару" },
			{ "msg_sauna_pour", "Вода шипит на раскалённых камнях" },
			{ "msg_sauna_notburning", "Печь не растоплена" },
			{ "msg_sauna_wait", "Камни ещё не раскалились" },
			{ "msg_sauna_tier", "Жар проникает глубже" },
			{ "msg_sauna_max_tier", "С лёгким паром!" },
			{ "msg_sauna_bucket_use_mead", "Подлить медовуху" },
			{ "msg_sauna_bucket_choose_mead", "У тебя несколько защитных медовух. Наведи на ведро и используй нужную медовуху с панели быстрого доступа" },
			{ "msg_sauna_bucket_no_mead", "У тебя нет подходящей защитной медовухи" },
			{ "msg_sauna_bucket_resistance_only", "Для бани подходит только защитная медовуха" },
			{ "msg_sauna_bucket_contains", "Добавлено" },
			{ "msg_sauna_bucket_full", "В банном ведре уже есть медовуха" },
			{ "msg_sauna_bucket_filled", "В банное ведро добавлено: {0}" },
			{ "msg_sauna_mead_aroma", "Аромат {0} наполняет парную" },
			{ "msg_sauna_too_hot", "Слишком жарко! Сними одежду" },
			{ "se_sauna_too_hot", "Слишком жарко" },
			{ "se_sauna_too_hot_tooltip", "В бане парятся без одежды, иначе станет слишком жарко!" },
			{ "se_sauna_too_hot_start", "Тебе слишком жарко!" },
			{ "se_sauna_steaming", "Пропаривание" },
			{ "se_sauna_steaming_tooltip", "Пар понемногу восстанавливает здоровье и выбивает из тебя всякую заразу." },
			{ "se_sauna_steaming_start", "Ты зашёл в пар" },
			{ "se_sauna_wellsteamed", "Пропарен" },
			{ "se_sauna_wellsteamed_tooltip", "Тепло держится в теле.\nХолод не берёт." },
			{ "se_sauna_wellsteamed_tooltip_whisks", "Тепло держится в теле.\nХолод не берёт.\nБанные веники снимают неудобства от сырости и открывают прогрев на 15 минут." },
			{ "se_sauna_wellsteamed_start", "Хорошо пропарился" },
			{ "piece_sauna_wrisks", "Банные веники" },
			{ "piece_sauna_wrisks_desc", "Пара берёзовых веников. Помогают как следует пропариться и не страдать от сырости." },
			{ "piece_sauna_bucket", "Ведро с ковшом" },
			{ "piece_sauna_bucket_desc", "Позволяет поддать больше воды и подлить медовуху, чтобы насладиться ароматным паром и получить её защитный эффект." }
		});
		CustomLocalization loc3 = _loc;
		text = "German";
		loc3.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "piece_sauna_stove", "Saunaofen" },
			{ "piece_sauna_stove_desc", "Ein steinerner Saunaofen. Mach einen Aufguss und schwitz dich ordentlich aus." },
			{ "piece_sauna_pour", "Aufguss machen" },
			{ "msg_sauna_pour", "Wasser zischt auf den heißen Steinen" },
			{ "msg_sauna_notburning", "Der Ofen ist kalt" },
			{ "msg_sauna_wait", "Die Steine müssen erst wieder heiß werden" },
			{ "msg_sauna_tier", "Die Hitze dringt tiefer" },
			{ "msg_sauna_max_tier", "Das war ein guter Aufguss!" },
			{ "msg_sauna_bucket_use_mead", "Met dazugießen" },
			{ "msg_sauna_bucket_choose_mead", "Du hast mehrere Widerstandsmete. Ziele auf den Eimer und benutze den gewünschten Met aus der Schnellleiste" },
			{ "msg_sauna_bucket_no_mead", "Du hast keinen passenden Widerstandsmet" },
			{ "msg_sauna_bucket_resistance_only", "Für die Sauna eignet sich nur Widerstandsmet" },
			{ "msg_sauna_bucket_contains", "Enthält" },
			{ "msg_sauna_bucket_full", "Im Saunaeimer ist bereits Met" },
			{ "msg_sauna_bucket_filled", "{0} wurde in den Saunaeimer gegeben" },
			{ "msg_sauna_mead_aroma", "Der Duft von {0} erfüllt die Sauna" },
			{ "msg_sauna_too_hot", "Zu heiß! Zieh deine Kleidung aus" },
			{ "se_sauna_too_hot", "Zu heiß" },
			{ "se_sauna_too_hot_tooltip", "In der Sauna musst du nackt sein, sonst wird es dir zu heiß!" },
			{ "se_sauna_too_hot_start", "Dir ist zu heiß!" },
			{ "se_sauna_steaming", "Saunadampf" },
			{ "se_sauna_steaming_tooltip", "Der Dampf stellt langsam Gesundheit wieder her und lindert, was dich plagt." },
			{ "se_sauna_steaming_start", "Du trittst in den Dampf" },
			{ "se_sauna_wellsteamed", "Gut durchgewärmt" },
			{ "se_sauna_wellsteamed_tooltip", "Die Wärme bleibt im Körper.\nKälte macht dir nichts aus." },
			{ "se_sauna_wellsteamed_tooltip_whisks", "Die Wärme bleibt im Körper.\nKälte macht dir nichts aus.\nBirkenquasten machen Nässe harmlos und schalten 15 Minuten Wärme frei." },
			{ "se_sauna_wellsteamed_start", "Du bist gut durchgewärmt" },
			{ "piece_sauna_wrisks", "Saunabirkenquaste" },
			{ "piece_sauna_wrisks_desc", "Ein Paar Birkenquasten. Sie helfen dir, dich ordentlich durchzuwärmen, und Nässe macht dir nichts mehr aus." },
			{ "piece_sauna_bucket", "Saunaeimer mit Kelle" },
			{ "piece_sauna_bucket_desc", "Damit kannst du mehr Wasser aufgießen und Met hinzufügen, um den aromatischen Dampf zu genießen und seine Schutzwirkung aufzunehmen." }
		});
		CustomLocalization loc4 = _loc;
		text = "Spanish";
		loc4.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "piece_sauna_stove", "Estufa de sauna" },
			{ "piece_sauna_stove_desc", "Una estufa de sauna de piedra. Echa agua sobre las piedras y date un buen baño de vapor." },
			{ "piece_sauna_pour", "Echar agua" },
			{ "msg_sauna_pour", "El agua sisea sobre las piedras calientes" },
			{ "msg_sauna_notburning", "La estufa está fría" },
			{ "msg_sauna_wait", "Las piedras tienen que volver a calentarse" },
			{ "msg_sauna_tier", "El calor penetra más hondo" },
			{ "msg_sauna_max_tier", "¡Eso sí que es un buen baño de vapor!" },
			{ "msg_sauna_bucket_use_mead", "Añadir hidromiel" },
			{ "msg_sauna_bucket_choose_mead", "Tienes varios hidromieles de resistencia. Apunta al cubo y usa el que quieras desde la barra rápida" },
			{ "msg_sauna_bucket_no_mead", "No tienes un hidromiel de resistencia compatible" },
			{ "msg_sauna_bucket_resistance_only", "Solo el hidromiel de resistencia sirve para la sauna" },
			{ "msg_sauna_bucket_contains", "Contiene" },
			{ "msg_sauna_bucket_full", "El cubo de sauna ya contiene hidromiel" },
			{ "msg_sauna_bucket_filled", "Has añadido {0} al cubo de sauna" },
			{ "msg_sauna_mead_aroma", "El aroma de {0} llena la sauna" },
			{ "msg_sauna_too_hot", "¡Demasiado calor! Quítate la ropa" },
			{ "se_sauna_too_hot", "Demasiado calor" },
			{ "se_sauna_too_hot_tooltip", "En la sauna hay que estar desnudo, ¡si no, pasarás demasiado calor!" },
			{ "se_sauna_too_hot_start", "¡Tienes demasiado calor!" },
			{ "se_sauna_steaming", "Baño de vapor" },
			{ "se_sauna_steaming_tooltip", "El vapor restaura poco a poco la salud y te hace sudar cualquier mal." },
			{ "se_sauna_steaming_start", "Entras en el vapor" },
			{ "se_sauna_wellsteamed", "Bien templado" },
			{ "se_sauna_wellsteamed_tooltip", "El calor permanece en tu cuerpo.\nEl frío no te afecta." },
			{ "se_sauna_wellsteamed_tooltip_whisks", "El calor permanece en tu cuerpo.\nEl frío no te afecta.\nLos ramos de sauna anulan las molestias de estar mojado y desbloquean 15 minutos de calor." },
			{ "se_sauna_wellsteamed_start", "Has entrado bien en calor" },
			{ "piece_sauna_wrisks", "Ramos de abedul para sauna" },
			{ "piece_sauna_wrisks_desc", "Un par de ramos de abedul. Te ayudan a darte un buen baño de vapor y a no sufrir las molestias de estar mojado." },
			{ "piece_sauna_bucket", "Cubo de sauna con cucharón" },
			{ "piece_sauna_bucket_desc", "Permite echar más agua sobre las piedras y añadir hidromiel para disfrutar de su aroma en el vapor y absorber su efecto protector." }
		});
		CustomLocalization loc5 = _loc;
		text = "Finnish";
		loc5.AddTranslation(ref text, new Dictionary<string, string>
		{
			{ "piece_sauna_stove", "Kiuas" },
			{ "piece_sauna_stove_desc", "Kivinen kiuas. Heitä löylyä ja nauti kunnon saunasta." },
			{ "piece_sauna_pour", "Heitä löylyä" },
			{ "msg_sauna_pour", "Vesi sihahtaa kuumille kiville" },
			{ "msg_sauna_notburning", "Kiuas on kylmä" },
			{ "msg_sauna_wait", "Kivet eivät ole vielä tarpeeksi kuumia" },
			{ "msg_sauna_tier", "Lämpö painuu syvemmälle" },
			{ "msg_sauna_max_tier", "Nyt tuli kunnon löylyt!" },
			{ "msg_sauna_bucket_use_mead", "Kaada simaa kiuluun" },
			{ "msg_sauna_bucket_choose_mead", "Sinulla on useita vastustuskykyä antavia simoja. Tähtää kiuluun ja käytä haluamaasi pikapalkista" },
			{ "msg_sauna_bucket_no_mead", "Sinulla ei ole sopivaa vastustussimaa" },
			{ "msg_sauna_bucket_resistance_only", "Vain vastustuskykyä antava sima sopii saunaan" },
			{ "msg_sauna_bucket_contains", "Sisältää" },
			{ "msg_sauna_bucket_full", "Saunakiulussa on jo simaa" },
			{ "msg_sauna_bucket_filled", "{0} kaadettiin saunakiuluun" },
			{ "msg_sauna_mead_aroma", "Höyryssä tuoksuu {0}" },
			{ "msg_sauna_too_hot", "Liian Kuuma! Ota vaatteet pois" },
			{ "se_sauna_too_hot", "Liian kuuma" },
			{ "se_sauna_too_hot_tooltip", "Saunassa täytyy olla alasti, jottei tule liian kuuma!" },
			{ "se_sauna_too_hot_start", "Sinulla on liian kuuma!" },
			{ "se_sauna_steaming", "Löylyissä" },
			{ "se_sauna_steaming_tooltip", "Löyly palauttaa hiljalleen terveyttä ja karkottaa kolotukset." },
			{ "se_sauna_steaming_start", "Astut löylyihin" },
			{ "se_sauna_wellsteamed", "Hyvin saunottu" },
			{ "se_sauna_wellsteamed_tooltip", "Lämpö pysyy kehossa.\nKylmä ei tunnu missään." },
			{ "se_sauna_wellsteamed_tooltip_whisks", "Lämpö pysyy kehossa.\nKylmä ei tunnu missään.\nKoivuvihdat poistavat märkyyden haitat ja avaavat 15 minuutin lämmön." },
			{ "se_sauna_wellsteamed_start", "Olet lämmin läpikotaisin" },
			{ "piece_sauna_wrisks", "Koivuvihdat" },
			{ "piece_sauna_wrisks_desc", "Pari koivuvihtaa. Niillä saat kunnon löylyt, eikä märkyys enää haittaa." },
			{ "piece_sauna_bucket", "Saunakiulu ja kauha" },
			{ "piece_sauna_bucket_desc", "Tällä saat heitettyä enemmän löylyä ja voit lisätä simaa, jolloin sen tuoksu ja suojaava vaikutus kulkevat höyryn mukana." }
		});
	}

	private void AddSaunaBucket()
	{
		//IL_0002: 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_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Expected O, but got Unknown
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Expected O, but got Unknown
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0294: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_031e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Expected O, but got Unknown
		//IL_034f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0370: Unknown result type (might be due to invalid IL or missing references)
		//IL_0391: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0419: Unknown result type (might be due to invalid IL or missing references)
		//IL_042d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0441: Unknown result type (might be due to invalid IL or missing references)
		//IL_0471: Unknown result type (might be due to invalid IL or missing references)
		//IL_0485: Unknown result type (might be due to invalid IL or missing references)
		//IL_0499: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0521: Unknown result type (might be due to invalid IL or missing references)
		//IL_0535: Unknown result type (might be due to invalid IL or missing references)
		//IL_0549: Unknown result type (might be due to invalid IL or missing references)
		//IL_0579: Unknown result type (might be due to invalid IL or missing references)
		//IL_058d: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0629: Unknown result type (might be due to invalid IL or missing references)
		//IL_063d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0651: Unknown result type (might be due to invalid IL or missing references)
		//IL_0681: Unknown result type (might be due to invalid IL or missing references)
		//IL_0695: Unknown result type (might be due to invalid IL or missing references)
		//IL_06a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0701: Unknown result type (might be due to invalid IL or missing references)
		//IL_0731: Unknown result type (might be due to invalid IL or missing references)
		//IL_0745: Unknown result type (might be due to invalid IL or missing references)
		//IL_0759: Unknown result type (might be due to invalid IL or missing references)
		//IL_0789: Unknown result type (might be due to invalid IL or missing references)
		//IL_079d: Unknown result type (might be due to invalid IL or missing references)
		//IL_07b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_07e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_07f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0809: Unknown result type (might be due to invalid IL or missing references)
		//IL_0839: Unknown result type (might be due to invalid IL or missing references)
		//IL_084d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0861: Unknown result type (might be due to invalid IL or missing references)
		//IL_08dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_08f9: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			PieceConfig val = new PieceConfig
			{
				Name = "$piece_sauna_bucket",
				Description = "$piece_sauna_bucket_desc",
				PieceTable = PieceTables.Hammer,
				Category = PieceCategories.Furniture,
				CraftingStation = CraftingStations.Workbench,
				Requirements = ParseRecipeConfig(BucketRecipeValue, "Iron:5,FineWood:10", "sauna_bucket")
			};
			CustomPiece val2 = new CustomPiece("sauna_bucket", true, val);
			val2.FixReference = true;
			MeshRenderer component = val2.PiecePrefab.GetComponent<MeshRenderer>();
			if ((Object)(object)component != (Object)null)
			{
				((Renderer)component).enabled = false;
			}
			ApplyBucketFloorPlacement(val2.PiecePrefab);
			GameObject val3 = new GameObject("sauna_bucket_visual_root");
			val3.transform.SetParent(val2.PiecePrefab.transform, false);
			val3.transform.localPosition = new Vector3(-0.171f, 1.282f, 2.045f);
			val3.transform.localRotation = Quaternion.identity;
			val3.transform.localScale = Vector3.one;
			GameObject mugVisual = AddLoadedMeshVisual(val3.transform, "fi_vil_container_ale_mug", null, "prop_Tankard", new Vector3(0.698f, -1.287f, -1.557f), Quaternion.Euler(-3.2E-05f, 318.3131f, 3.1E-05f), new Vector3(1.069931f, 1.08225f, 1.0486f));
			Renderer mugRenderer = PrepareMugEmpty(mugVisual);
			AddLoadedMeshVisual(val3.transform, "meadcauldron_broken.003_Cube.014 (2)", "meadcauldron_broken.003_Cube.014", null, new Vector3(0.824516f, -0.732448f, -2.750395f), Quaternion.Euler(358.2979f, 243.1259f, 332.7032f), new Vector3(0.810494f, 1.256816f, 0.729256f));
			AddSoftReferencedBucketVisual(val3.transform, new Vector3(0.214f, -1.296f, -1.948f), Quaternion.identity, new Vector3(2.746935f, 2.44169f, 2.789405f));
			AddHotTubColliderVisual(val3.transform, "default", new Vector3(0.510797f, -1.104215f, -2.561304f), Quaternion.Euler(357.713f, 240.7826f, 57.50727f), new Vector3(0.15094f, 0.19033f, 0.155687f));
			GameObject val4 = AddBucketWaterSurface(val3.transform, new Vector3(0.211f, -0.839f, -1.942f), Quaternion.identity, new Vector3(0.828164f, 0.002846f, 0.844305f));
			GameObject val5 = AddBucketInfusionSurface(val3.transform, new Vector3(0.211f, -0.847f, -1.942f), Quaternion.identity, new Vector3(0.828164f, 0.002846f, 0.844305f));
			if ((Object)(object)val2.PiecePrefab.GetComponent<SaunaBucket>() == (Object)null)
			{
				val2.PiecePrefab.AddComponent<SaunaBucket>();
			}
			SaunaBucketLiquidVisual saunaBucketLiquidVisual = val2.PiecePrefab.AddComponent<SaunaBucketLiquidVisual>();
			saunaBucketLiquidVisual.Configure(((Object)(object)val4 != (Object)null) ? val4.GetComponent<MeshRenderer>() : null, ((Object)(object)val5 != (Object)null) ? val5.GetComponent<MeshRenderer>() : null, mugRenderer, 1);
			GameObject val6 = new GameObject("oak_wrisk");
			val6.transform.SetParent(val3.transform, false);
			val6.transform.localPosition = new Vector3(-4.219f, 3.939f, 0.517f);
			val6.transform.localRotation = Quaternion.Euler(12.38466f, 180f, 141.7517f);
			val6.transform.localScale = new Vector3(3.975999f, 4.348866f, 4.108019f);
			AddLoadedMeshVisual(val6.transform, "Plane_062", "Plane_062", "Oak_Sapling", new Vector3(0.046f, 1.179049f, 0.825689f), Quaternion.Euler(314.7355f, 329.9362f, 312.5505f), new Vector3(0.258351f, 0.256806f, 0.259855f));
			AddLoadedMeshVisual(val6.transform, "Oak_Sapling001", "Oak_Sapling001", "Oak_Sapling", new Vector3(0.234228f, 1.253305f, 0.834163f), Quaternion.Euler(0f, 122.2157f, 0f), new Vector3(0.075168f, 0.097723f, 0.089222f));
			AddLoadedMeshVisual(val6.transform, "Plane_061", "Plane_061", "Oak_Sapling", new Vector3(0.301976f, 1.155071f, 0.864759f), Quaternion.Euler(351.9135f, 353.9866f, 37.26222f), new Vector3(0.257912f, 0.260185f, 0.256919f));
			AddLoadedMeshVisual(val6.transform, "Plane_058", "Plane_058", "Oak_Sapling", new Vector3(0.170801f, 1.095263f, 0.793844f), Quaternion.Euler(23.74482f, 52.64099f, 4.288773f), new Vector3(0.256095f, 0.262132f, 0.256851f));
			AddLoadedMeshVisual(val6.transform, "Plane_059", "Plane_059", "Oak_Sapling", new Vector3(0.222552f, 1.162431f, 0.738886f), Quaternion.Euler(21.43836f, 300.6433f, 324.7045f), new Vector3(0.258f, 0.260573f, 0.256456f));
			AddLoadedMeshVisual(val6.transform, "Plane_060", "Plane_060", "Oak_Sapling", new Vector3(0.2046f, 1.1921f, 0.8974f), Quaternion.Euler(320.7024f, 5.064512f, 353.5978f), new Vector3(0.25516f, 0.260683f, 0.259216f));
			AddLoadedMeshVisual(val6.transform, "Plane_060 (2)", "Plane_060", "Oak_Sapling", new Vector3(0.1529f, 1.2025f, 0.8251f), Quaternion.Euler(313.6828f, 298.696f, 342.3445f), new Vector3(0.25516f, 0.260683f, 0.259216f));
			AddLoadedMeshVisual(val6.transform, "Oak_Sapling001 (1)", "Oak_Sapling001", "Oak_Sapling", new Vector3(0.236828f, 1.251935f, 0.837939f), Quaternion.Euler(0f, 110.3317f, 0f), new Vector3(0.075259f, 0.097723f, 0.089115f));
			AddLoadedMeshVisual(val6.transform, "Oak_Sapling001 (3)", "Oak_Sapling001", "Oak_Sapling", new Vector3(0.228611f, 1.251603f, 0.838198f), Quaternion.Euler(2.105434f, 110.3317f, 2.393955f), new Vector3(0.075259f, 0.097723f, 0.089115f));
			AddLoadedMeshVisual(val6.transform, "Oak_Sapling001 (2)", "Oak_Sapling001", "Oak_Sapling", new Vector3(0.234381f, 1.254092f, 0.842921f), Quaternion.Euler(0f, 75.63401f, 0f), new Vector3(0.075292f, 0.097723f, 0.089076f));
			AddLoadedMeshVisual(val6.transform, "Plane_060 (1)", "Plane_060", "Oak_Sapling", new Vector3(0.159711f, 1.180474f, 0.824456f), Quaternion.Euler(315.3681f, 291.1149f, 347.418f), new Vector3(0.256884f, 0.258953f, 0.25917f));
			AddPrefabPathVisual(val6.transform, "hood (7)", "HelmetSweatBand", "attach/hood", new Vector3(0.2323f, 1.2741f, 0.833f), Quaternion.Euler(4.39467f, 0f, 0f), new Vector3(0.073999f, 0.083264f, 0.064492f));
			AddPrefabPathVisual(val6.transform, "hood (6)", "HelmetSweatBand", "attach/hood", new Vector3(0.2321f, 1.2624f, 0.8348f), Quaternion.Euler(348.3143f, 0.114809f, 357.6877f), new Vector3(0.076819f, 0.082224f, 0.05776f));
			AddPrefabPathVisual(val6.transform, "hood (8)", "HelmetSweatBand", "attach/hood", new Vector3(0.2324f, 1.2821f, 0.833f), Quaternion.Euler(0f, 0f, 352.4707f), new Vector3(0.08079f, 0.073925f, 0.063359f));
			MakeSaunaWrisksVegetationStatic(val3.transform);
			Collider[] componentsInChildren = val2.PiecePrefab.GetComponentsInChildren<Collider>(true);
			foreach (Collider val7 in componentsInChildren)
			{
				if ((Object)(object)val7 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val7);
				}
			}
			BoxCollider val8 = val3.AddComponent<BoxCollider>();
			((Collider)val8).isTrigger = false;
			val8.center = new Vector3(0.21f, -0.95f, -1.94f);
			val8.size = new Vector3(0.97f, 0.7f, 1f);
			if ((Object)(object)val3.GetComponent<SaunaBucketInteractionProxy>() == (Object)null)
			{
				val3.AddComponent<SaunaBucketInteractionProxy>();
			}
			val2.Piece.m_icon = FindFirstPieceIcon("piece_chest_wood", "piece_table_round", "piece_stool", "piece_cauldron");
			if ((Object)(object)val2.Piece.m_icon == (Object)null)
			{
				Logger.LogError((object)"sauna_bucket: no icon available");
				return;
			}
			if (!PieceManager.Instance.AddPiece(val2))
			{
				Logger.LogError((object)"sauna_bucket: PieceManager rejected the piece");
				return;
			}
			_bucketPrefab = val2.PiecePrefab;
			ApplyRecipeToPiece(_bucketPrefab, BucketRecipeValue, "Iron:5,FineWood:10", "sauna_bucket");
			Logger.LogInfo((object)"sauna_bucket registered (gameplay + liquid infusion)");
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"sauna_bucket failed: {arg}");
		}
	}

	private void ApplyBucketFloorPlacement(GameObject targetPrefab)
	{
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: 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_01b0: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = null;
		string[] array = new string[4] { "piece_table_round", "piece_stool", "piece_chest_wood", "piece_chair" };
		string[] array2 = array;
		foreach (string text in array2)
		{
			val = PrefabManager.Instance.GetPrefab(text);
			if ((Object)(object)val != (Object)null)
			{
				break;
			}
		}
		Piece val2 = (((Object)(object)targetPrefab != (Object)null) ? targetPrefab.GetComponent<Piece>() : null);
		Piece val3 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
		if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null)
		{
			val2.m_groundPiece = false;
			val2.m_allowAltGroundPlacement = false;
			val2.m_groundOnly = false;
			val2.m_cultivatedGroundOnly = false;
			val2.m_waterPiece = false;
			val2.m_clipGround = val3.m_clipGround;
			val2.m_clipEverything = val3.m_clipEverything;
			val2.m_noClipping = val3.m_noClipping;
			val2.m_noInWater = true;
			val2.m_notOnWood = false;
			val2.m_notOnTiltingSurface = true;
			val2.m_notOnFloor = false;
			val2.m_inCeilingOnly = false;
			val2.m_onlyInTeleportArea = val3.m_onlyInTeleportArea;
			val2.m_allowedInDungeons = val3.m_allowedInDungeons;
			val2.m_spaceRequirement = 0f;
			val2.m_placeEffect = val3.m_placeEffect;
		}
		WearNTear val4 = (((Object)(object)targetPrefab != (Object)null) ? targetPrefab.GetComponent<WearNTear>() : null);
		WearNTear val5 = (((Object)(object)val != (Object)null) ? val.GetComponent<WearNTear>() : null);
		if ((Object)(object)val4 != (Object)null && (Object)(object)val5 != (Object)null)
		{
			val4.m_health = val5.m_health;
			val4.m_materialType = val5.m_materialType;
			val4.m_damages = val5.m_damages;
			val4.m_burnable = val5.m_burnable;
			val4.m_noSupportWear = val5.m_noSupportWear;
			val4.m_noRoofWear = val5.m_noRoofWear;
			val4.m_supports = false;
			val4.m_destroyedEffect = val5.m_destroyedEffect;
			val4.m_hitEffect = val5.m_hitEffect;
			val4.m_switchEffect = val5.m_switchEffect;
		}
	}

	private static GameObject AddLoadedMeshVisual(Transform parent, string objectName, string meshName, string preferredPrefabName, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		if (!FindLoadedMeshRenderer(objectName, meshName, preferredPrefabName, out var sourceMf, out var sourceMr))
		{
			Logger.LogWarning((object)("sauna_bucket: visual source not found: object='" + objectName + "', mesh='" + meshName + "', prefab='" + preferredPrefabName + "'"));
			return null;
		}
		GameObject val = new GameObject(objectName);
		val.transform.SetParent(parent, false);
		val.transform.localPosition = position;
		val.transform.localRotation = rotation;
		val.transform.localScale = scale;
		val.layer = ((Component)parent).gameObject.layer;
		MeshFilter val2 = val.AddComponent<MeshFilter>();
		val2.sharedMesh = sourceMf.sharedMesh;
		MeshRenderer val3 = val.AddComponent<MeshRenderer>();
		((Renderer)val3).sharedMaterials = ((Renderer)sourceMr).sharedMaterials;
		((Renderer)val3).shadowCastingMode = ((Renderer)sourceMr).shadowCastingMode;
		((Renderer)val3).receiveShadows = ((Renderer)sourceMr).receiveShadows;
		return val;
	}

	private static void RememberBucketMeadSource(Material sourceLiquid)
	{
		if ((Object)(object)_saunaBucketSourceMeadMaterial == (Object)null && (Object)(object)sourceLiquid != (Object)null)
		{
			_saunaBucketSourceMeadMaterial = sourceLiquid;
		}
	}

	private static Material FindBucketMeadSource()
	{
		if ((Object)(object)_saunaBucketSourceMeadMaterial != (Object)null)
		{
			return _saunaBucketSourceMeadMaterial;
		}
		Material val = FindLoadedMaterialByName("mead") ?? FindLoadedMaterialByName("Mead");
		RememberBucketMeadSource(val);
		return val;
	}

	private static Renderer PrepareMugEmpty(GameObject mugVisual)
	{
		if ((Object)(object)mugVisual == (Object)null)
		{
			return null;
		}
		Renderer[] componentsInChildren = mugVisual.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Material[] sharedMaterials = val.sharedMaterials;
			if (sharedMaterials != null && sharedMaterials.Length >= 2 && !((Object)(object)sharedMaterials[1] == (Object)null))
			{
				RememberBucketMeadSource(sharedMaterials[1]);
				Material bucketEmptyLiquidMaterial = GetBucketEmptyLiquidMaterial();
				if ((Object)(object)bucketEmptyLiquidMaterial == (Object)null)
				{
					Logger.LogWarning((object)("sauna_bucket: could not create empty mug liquid from '" + ((Object)sharedMaterials[1]).name + "'"));
					return val;
				}
				Material[] array = (Material[])sharedMaterials.Clone();
				string text = (((Object)(object)array[1] != (Object)null) ? ((Object)array[1]).name : "<null>");
				array[1] = bucketEmptyLiquidMaterial;
				val.sharedMaterials = array;
				Logger.LogInfo((object)("sauna_bucket: mug slot 1 '" + text + "' -> EMPTY on renderer '" + ((Object)val).name + "'"));
				return val;
			}
		}
		Logger.LogWarning((object)"sauna_bucket: mug exists, but no liquid material slot was found");
		return null;
	}

	private static void SetTransparentStandardMode(Material material)
	{
		if (!((Object)(object)material == (Object)null) && material.HasProperty("_Mode"))
		{
			material.SetFloat("_Mode", 3f);
			material.SetOverrideTag("RenderType", "Transparent");
			material.SetInt("_SrcBlend", 5);
			material.SetInt("_DstBlend", 10);
			material.SetInt("_ZWrite", 0);
			material.DisableKeyword("_ALPHATEST_ON");
			material.EnableKeyword("_ALPHABLEND_ON");
			material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			material.renderQueue = 3000;
		}
	}

	private static void SetMaterialColor(Material material, Color color)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)material == (Object)null))
		{
			if (material.HasProperty("_Color"))
			{
				material.SetColor("_Color", color);
			}
			else
			{
				material.color = color;
			}
		}
	}

	internal static Material GetBucketWaterMaterial()
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Expected O, but got Unknown
		if ((Object)(object)_saunaBucketWaterMaterial != (Object)null)
		{
			return _saunaBucketWaterMaterial;
		}
		Material val = FindBucketMeadSource();
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		Material val2 = new Material(val);
		((Object)val2).name = "SaunaBucket_Water";
		if (val2.HasProperty("_MainTex"))
		{
			val2.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
		}
		if (val2.HasProperty("_BumpMap"))
		{
			val2.SetTexture("_BumpMap", (Texture)null);
		}
		val2.DisableKeyword("_NORMALMAP");
		if (val2.HasProperty("_Metallic"))
		{
			val2.SetFloat("_Metallic", 0f);
		}
		if (val2.HasProperty("_Glossiness"))
		{
			val2.SetFloat("_Glossiness", 0.72f);
		}
		SetTransparentStandardMode(val2);
		_saunaBucketWaterMaterial = val2;
		ApplyBucketLiquidTuningToMaterials();
		string[] obj = new string[5]
		{
			"sauna_bucket: private water material created from '",
			((Object)val).name,
			"', shader='",
			null,
			null
		};
		Shader shader = val.shader;
		obj[3] = ((shader != null) ? ((Object)shader).name : null);
		obj[4] = "'";
		Logger.LogInfo((object)string.Concat(obj));
		return val2;
	}

	internal static Material GetBucketEmptyLiquidMaterial()
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_saunaBucketEmptyLiquidMaterial != (Object)null)
		{
			return _saunaBucketEmptyLiquidMaterial;
		}
		Material val = FindBucketMeadSource();
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		Material val2 = new Material(val);
		((Object)val2).name = "SaunaBucket_EmptyLiquid";
		if (val2.HasProperty("_MainTex"))
		{
			val2.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
		}
		SetTransparentStandardMode(val2);
		SetMaterialColor(val2, new Color(1f, 1f, 1f, 0f));
		_saunaBucketEmptyLiquidMaterial = val2;
		return val2;
	}

	internal static Material GetBucketMeadMaterial(int type)
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		type = Mathf.Clamp(type, 1, 3);
		if ((Object)(object)_saunaBucketMeadMaterials[type] != (Object)null)
		{
			return _saunaBucketMeadMaterials[type];
		}
		Material val = FindBucketMeadSource();
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		Material val2 = new Material(val);
		((Object)val2).name = type switch
		{
			2 => "SaunaBucket_Mead_Frost", 
			1 => "SaunaBucket_Mead_Poison", 
			_ => "SaunaBucket_Mead_Fire", 
		};
		SetTransparentStandardMode(val2);
		_saunaBucketMeadMaterials[type] = val2;
		ApplyBucketLiquidTuningToMaterials();
		return val2;
	}

	internal static Material GetBucketMugMeadMaterial(int type)
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		type = Mathf.Clamp(type, 1, 3);
		if ((Object)(object)_saunaBucketMugMeadMaterials[type] != (Object)null)
		{
			return _saunaBucketMugMeadMaterials[type];
		}
		Material val = FindBucketMeadSource();
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		Material val2 = new Material(val);
		((Object)val2).name = type switch
		{
			2 => "SaunaBucket_MugMead_Frost", 
			1 => "SaunaBucket_MugMead_Poison", 
			_ => "SaunaBucket_MugMead_Fire", 
		};
		SetTransparentStandardMode(val2);
		SetMaterialColor(val2, SaunaBucketLiquidTuning.MugMeadColor(type));
		_saunaBucketMugMeadMaterials[type] = val2;
		return val2;
	}

	internal static void ApplyBucketLiquidTuningToMaterials()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_saunaBucketWaterMaterial != (Object)null)
		{
			SetMaterialColor(_saunaBucketWaterMaterial, SaunaBucketLiquidTuning.WaterColor);
		}
		for (int i = 1; i <= 3; i++)
		{
			Material val = _saunaBucketMeadMaterials[i];
			if ((Object)(object)val != (Object)null)
			{
				SetMaterialColor(val, SaunaBucketLiquidTuning.MeadColor(i));
			}
			Material val2 = _saunaBucketMugMeadMaterials[i];
			if ((Object)(object)val2 != (Object)null)
			{
				SetMaterialColor(val2, SaunaBucketLiquidTuning.MugMeadColor(i));
			}
		}
		if ((Object)(object)_saunaBucketEmptyLiquidMaterial != (Object)null)
		{
			SetMaterialColor(_saunaBucketEmptyLiquidMaterial, new Color(1f, 1f, 1f, 0f));
		}
	}

	internal static void RefreshBucketLiquidVisuals()
	{
		SaunaBucketLiquidVisual.RefreshAll();
	}

	private static GameObject AddBucketWaterSurface(Transform parent, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: 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)
		Material bucketWaterMaterial = GetBucketWaterMaterial();
		if ((Object)(object)bucketWaterMaterial == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: water surface skipped because base material 'mead' was not found");
			return null;
		}
		GameObject val = GameObject.CreatePrimitive((PrimitiveType)2);
		((Object)val).name = "Cylinder";
		val.transform.SetParent(parent, false);
		val.transform.localPosition = position;
		val.transform.localRotation = rotation;
		val.transform.localScale = scale;
		val.layer = ((Component)parent).gameObject.layer;
		Collider component = val.GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component);
		}
		MeshRenderer component2 = val.GetComponent<MeshRenderer>();
		if ((Object)(object)component2 != (Object)null)
		{
			((Renderer)component2).sharedMaterial = bucketWaterMaterial;
			((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component2).receiveShadows = false;
		}
		Logger.LogInfo((object)$"sauna_bucket: water Cylinder added at {position} scale={scale}");
		return val;
	}

	private static GameObject AddBucketInfusionSurface(Transform parent, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		Material bucketMeadMaterial = GetBucketMeadMaterial(Mathf.Clamp(SaunaBucketLiquidTuning.Preview, 1, 3));
		if ((Object)(object)bucketMeadMaterial == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: infusion Cylinder_low skipped because base material 'mead' was not found");
			return null;
		}
		GameObject val = GameObject.CreatePrimitive((PrimitiveType)2);
		((Object)val).name = "Cylinder_low";
		val.transform.SetParent(parent, false);
		val.transform.localPosition = position;
		val.transform.localRotation = rotation;
		val.transform.localScale = scale;
		val.layer = ((Component)parent).gameObject.layer;
		Collider component = val.GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component);
		}
		MeshRenderer component2 = val.GetComponent<MeshRenderer>();
		if ((Object)(object)component2 != (Object)null)
		{
			((Renderer)component2).sharedMaterial = bucketMeadMaterial;
			((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component2).receiveShadows = false;
			((Renderer)component2).enabled = SaunaBucketLiquidTuning.Preview != 0;
		}
		Logger.LogInfo((object)$"sauna_bucket: infusion Cylinder_low added at {position} scale={scale}, preview={SaunaBucketLiquidTuning.Preview}");
		return val;
	}

	private static GameObject AddSoftReferencedBucketVisual(Transform parent, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Expected O, but got Unknown
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0216: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		Mesh val = null;
		Material val2 = null;
		ShadowCastingMode shadowCastingMode = (ShadowCastingMode)1;
		bool receiveShadows = true;
		try
		{
			val = Cache.GetPrefab<Mesh>("fi_vil_container_bucket01");
			if ((Object)(object)val != (Object)null)
			{
				Logger.LogInfo((object)("sauna_bucket: bucket mesh resolved by PrefabManager.Cache: '" + ((Object)val).name + "'"));
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("sauna_bucket: PrefabManager.Cache mesh lookup failed: " + ex.GetBaseException().Message));
		}
		GameObject val3 = null;
		if ((Object)(object)val == (Object)null)
		{
			val3 = LoadGameObjectByExactSoftAssetPath("Assets/3rd party/Fantasy_Interiors/Villages_&_Towns/Meshes/Props/Containers/fi_vil_container_bucket01.prefab");
			if ((Object)(object)val3 != (Object)null)
			{
				MeshFilter componentInChildren = val3.GetComponentInChildren<MeshFilter>(true);
				MeshRenderer val4 = (((Object)(object)componentInChildren != (Object)null) ? ((Component)componentInChildren).GetComponent<MeshRenderer>() : null);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					val = componentInChildren.sharedMesh;
				}
				if ((Object)(object)val4 != (Object)null)
				{
					if (((Renderer)val4).sharedMaterials != null && ((Renderer)val4).sharedMaterials.Length != 0)
					{
						val2 = ((Renderer)val4).sharedMaterials[0];
					}
					shadowCastingMode = ((Renderer)val4).shadowCastingMode;
					receiveShadows = ((Renderer)val4).receiveShadows;
				}
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			try
			{
				val2 = Cache.GetPrefab<Material>("fi_village_wood");
			}
			catch
			{
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			val2 = FindLoadedMaterialByName("fi_village_wood");
		}
		if ((Object)(object)val == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: fi_vil_container_bucket01 mesh still not found");
			return null;
		}
		if ((Object)(object)val2 == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: fi_village_wood material not found");
			return null;
		}
		GameObject val5 = new GameObject("fi_vil_container_bucket01");
		val5.transform.SetParent(parent, false);
		val5.transform.localPosition = position;
		val5.transform.localRotation = rotation;
		val5.transform.localScale = scale;
		val5.layer = ((Component)parent).gameObject.layer;
		MeshFilter val6 = val5.AddComponent<MeshFilter>();
		val6.sharedMesh = val;
		MeshRenderer val7 = val5.AddComponent<MeshRenderer>();
		((Renderer)val7).sharedMaterial = val2;
		((Renderer)val7).shadowCastingMode = shadowCastingMode;
		((Renderer)val7).receiveShadows = receiveShadows;
		Logger.LogInfo((object)("sauna_bucket: exact bucket added: mesh='" + ((Object)val).name + "', material='" + ((Object)val2).name + "'"));
		return val5;
	}

	private static GameObject LoadGameObjectByExactSoftAssetPath(string requestedPath)
	{
		try
		{
			Type type = Type.GetType("SoftReferenceableAssets.Runtime, SoftReferenceableAssets");
			Type type2 = Type.GetType("SoftReferenceableAssets.AssetID, SoftReferenceableAssets");
			Type type3 = Type.GetType("SoftReferenceableAssets.SoftReference`1, SoftReferenceableAssets");
			if (type == null || type2 == null || type3 == null)
			{
				Logger.LogWarning((object)"sauna_bucket: SoftReferenceableAssets reflection types not found");
				return null;
			}
			MethodInfo method = type.GetMethod("GetAllAssetPathsInBundleMappedToAssetID", BindingFlags.Static | BindingFlags.Public);
			if (method == null)
			{
				Logger.LogWarning((object)"sauna_bucket: Runtime.GetAllAssetPathsInBundleMappedToAssetID() not found");
				return null;
			}
			object obj = method.Invoke(null, null);
			if (!(obj is IEnumerable enumerable))
			{
				Logger.LogWarning((object)"sauna_bucket: asset path map is not enumerable");
				return null;
			}
			string b = NormalizeAssetPath(requestedPath);
			object obj2 = null;
			string arg = null;
			foreach (object item in enumerable)
			{
				if (item == null)
				{
					continue;
				}
				Type type4 = item.GetType();
				PropertyInfo property = type4.GetProperty("Key");
				PropertyInfo property2 = type4.GetProperty("Value");
				if (property == null || property2 == null)
				{
					continue;
				}
				string text = property.GetValue(item, null) as string;
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				string text2 = NormalizeAssetPath(text);
				bool flag = string.Equals(text2, b, StringComparison.OrdinalIgnoreCase);
				bool flag2 = text2.EndsWith("/fi_vil_container_bucket01.prefab", StringComparison.OrdinalIgnoreCase);
				if (flag || flag2)
				{
					arg = text;
					obj2 = property2.GetValue(item, null);
					if (flag)
					{
						break;
					}
				}
			}
			if (obj2 == null)
			{
				Logger.LogWarning((object)("sauna_bucket: exact asset path not present in runtime map: '" + requestedPath + "'"));
				return null;
			}
			Logger.LogInfo((object)$"sauna_bucket: exact asset path resolved: '{arg}', AssetID={obj2}");
			Type type5 = type3.MakeGenericType(typeof(GameObject));
			object obj3 = null;
			ConstructorInfo[] constructors = type5.GetConstructors();
			foreach (ConstructorInfo constructorInfo in constructors)
			{
				ParameterInfo[] parameters = constructorInfo.GetParameters();
				if (parameters.Length == 1 && parameters[0].ParameterType == type2)
				{
					obj3 = constructorInfo.Invoke(new object[1] { obj2 });
					break;
				}
			}
			if (obj3 == null)
			{
				Logger.LogWarning((object)"sauna_bucket: SoftReference<GameObject>(AssetID) constructor not found");
				return null;
			}
			MethodInfo method2 = type5.GetMethod("Load", Type.EmptyTypes);
			PropertyInfo property3 = type5.GetProperty("Asset");
			if (method2 == null || property3 == null)
			{
				Logger.LogWarning((object)"sauna_bucket: SoftReference Load/Asset API not found");
				return null;
			}
			method2.Invoke(obj3, null);
			object? value = property3.GetValue(obj3, null);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning((object)"sauna_bucket: exact SoftReference loaded but Asset is null");
				return null;
			}
			Logger.LogInfo((object)("sauna_bucket: exact path asset loaded: '" + ((Object)val).name + "'"));
			return val;
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("sauna_bucket: exact-path loader failed: " + ex.GetBaseException().Message));
			return null;
		}
	}

	private static string NormalizeAssetPath(string path)
	{
		return string.IsNullOrEmpty(path) ? string.Empty : path.Replace('\\', '/').Trim();
	}

	private static bool FindLoadedMeshRenderer(string objectName, string meshName, string preferredPrefabName, out MeshFilter sourceMf, out MeshRenderer sourceMr)
	{
		sourceMf = null;
		sourceMr = null;
		if (!string.IsNullOrEmpty(preferredPrefabName))
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab(preferredPrefabName);
			if ((Object)(object)prefab != (Object)null)
			{
				MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
				foreach (MeshFilter val in componentsInChildren)
				{
					MeshRenderer val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<MeshRenderer>() : null);
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null))
					{
						bool flag = string.IsNullOrEmpty(objectName) || ((Object)((Component)val).gameObject).name == objectName;
						bool flag2 = string.IsNullOrEmpty(meshName) || ((Object)val.sharedMesh).name == meshName;
						if (flag && flag2)
						{
							sourceMf = val;
							sourceMr = val2;
							return true;
						}
					}
				}
			}
		}
		MeshFilter[] array = Resources.FindObjectsOfTypeAll<MeshFilter>();
		MeshFilter[] array2 = array;
		foreach (MeshFilter val3 in array2)
		{
			if ((Object)(object)val3 == (Object)null || (Object)(object)val3.sharedMesh == (Object)null)
			{
				continue;
			}
			MeshRenderer component = ((Component)val3).GetComponent<MeshRenderer>();
			if (!((Object)(object)component == (Object)null))
			{
				bool flag3 = string.IsNullOrEmpty(objectName) || ((Object)((Component)val3).gameObject).name == objectName;
				bool flag4 = string.IsNullOrEmpty(meshName) || ((Object)val3.sharedMesh).name == meshName;
				if (flag3 && flag4)
				{
					sourceMf = val3;
					sourceMr = component;
					return true;
				}
			}
		}
		if (!string.IsNullOrEmpty(meshName))
		{
			MeshFilter[] array3 = array;
			foreach (MeshFilter val4 in array3)
			{
				if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.sharedMesh == (Object)null) && !(((Object)val4.sharedMesh).name != meshName))
				{
					MeshRenderer component2 = ((Component)val4).GetComponent<MeshRenderer>();
					if ((Object)(object)component2 != (Object)null)
					{
						sourceMf = val4;
						sourceMr = component2;
						return true;
					}
				}
			}
		}
		return false;
	}

	private static GameObject AddHotTubColliderVisual(Transform parent, string objectName, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Expected O, but got Unknown
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		MeshFilter val = null;
		MeshRenderer val2 = null;
		GameObject prefab = PrefabManager.Instance.GetPrefab("HotTub_Collider");
		if ((Object)(object)prefab != (Object)null)
		{
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val3 in componentsInChildren)
			{
				if ((Object)(object)val3 != (Object)null && (Object)(object)val3.sharedMesh != (Object)null && (((Object)((Component)val3).gameObject).name == objectName || ((Object)val3.sharedMesh).name == objectName))
				{
					MeshRenderer component = ((Component)val3).GetComponent<MeshRenderer>();
					if ((Object)(object)component != (Object)null)
					{
						val = val3;
						val2 = component;
						break;
					}
				}
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			MeshFilter[] array = Resources.FindObjectsOfTypeAll<MeshFilter>();
			foreach (MeshFilter val4 in array)
			{
				if ((Object)(object)val4 == (Object)null || (Object)(object)val4.sharedMesh == (Object)null || ((Object)((Component)val4).gameObject).name != objectName)
				{
					continue;
				}
				bool flag = false;
				Transform parent2 = ((Component)val4).transform.parent;
				while ((Object)(object)parent2 != (Object)null)
				{
					if (((Object)parent2).name == "HotTub_Collider")
					{
						flag = true;
						break;
					}
					parent2 = parent2.parent;
				}
				if (flag)
				{
					MeshRenderer component2 = ((Component)val4).GetComponent<MeshRenderer>();
					if ((Object)(object)component2 != (Object)null)
					{
						val = val4;
						val2 = component2;
						break;
					}
				}
			}
		}
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: HotTub_Collider/default visual not found");
			return null;
		}
		Material val5 = FindLoadedMaterialByName("woodmetal");
		if ((Object)(object)val5 == (Object)null)
		{
			Logger.LogWarning((object)"sauna_bucket: material 'woodmetal' not found");
			return null;
		}
		GameObject val6 = new GameObject(objectName);
		val6.transform.SetParent(parent, false);
		val6.transform.localPosition = position;
		val6.transform.localRotation = rotation;
		val6.transform.localScale = scale;
		val6.layer = ((Component)parent).gameObject.layer;
		MeshFilter val7 = val6.AddComponent<MeshFilter>();
		val7.sharedMesh = val.sharedMesh;
		MeshRenderer val8 = val6.AddComponent<MeshRenderer>();
		((Renderer)val8).sharedMaterial = val5;
		((Renderer)val8).shadowCastingMode = ((Renderer)val2).shadowCastingMode;
		((Renderer)val8).receiveShadows = ((Renderer)val2).receiveShadows;
		return val6;
	}

	private static GameObject AddPrefabPathVisual(Transform parent, string objectName, string prefabName, string sourcePath, Vector3 position, Quaternion rotation, Vector3 scale)
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		//IL_00b1: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName);
		Transform val = (((Object)(object)prefab != (Object)null) ? prefab.transform.Find(sourcePath) : null);
		MeshFilter val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<MeshFilter>() : null);
		MeshRenderer val3 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<MeshRenderer>() : null);
		if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val2.sharedMesh == (Object)null)
		{
			Logger.LogWarning((object)("sauna visual not found: " + prefabName + "/" + sourcePath));
			return null;
		}
		GameObject val4 = new GameObject(objectName);
		val4.transform.SetParent(parent, false);
		val4.transform.localPosition = position;
		val4.transform.localRotation = rotation;
		val4.transform.localScale = scale;
		val4.layer = ((Component)parent).gameObject.layer;
		MeshFilter val5 = val4.AddComponent<MeshFilter>();
		val5.sharedMesh = val2.sharedMesh;
		MeshRenderer val6 = val4.AddComponent<MeshRenderer>();
		Material val7 = null;
		if (string.Equals(prefabName, "Birch_Sapling", StringComparison.Ordinal) && !string.IsNullOrEmpty(sourcePath) && sourcePath.IndexOf("birchleafs", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			val7 = FindLoadedMaterialByName("birch_leaf");
			if ((Object)(object)val7 == (Object)null)
			{
				Logger.LogWarning((object)("sauna_wrisks: material 'birch_leaf' not found for " + objectName + "; falling back to Birch_Sapling material"));
			}
		}
		if ((Object)(object)val7 != (Object)null)
		{
			((Renderer)val6).sharedMaterial = val7;
		}
		else
		{
			((Renderer)val6).sharedMaterials = ((Renderer)val3).sharedMaterials;
		}
		((Renderer)val6).shadowCastingMode = ((Renderer)val3).shadowCastingMode;
		((Renderer)val6).receiveShadows = ((Renderer)val3).receiveShadows;
		return val4;
	}

	private Sprite FindFirstPieceIcon(params string[] prefabNames)
	{
		foreach (string text in prefabNames)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab(text);
			Piece val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Piece>() : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.m_icon != (Object)null)
			{
				return val.m_icon;
			}
		}
		return null;
	}

	private void AddSaunaStove()
	{
		//IL_0002: 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_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		try
		{
			PieceConfig val = new PieceConfig
			{
				Name = "$piece_sauna_stove",
				Description = "$piece_sauna_stove_desc",
				PieceTable = PieceTables.Hammer,
				Category = PieceCategories.Furniture,
				CraftingStation = CraftingStations.Workbench,
				Requirements = ParseRecipeConfig(StoveRecipeValue, "Wood:10,Coal:5,Stone:40", "sauna_stove")
			};
			CustomPiece val2 = new CustomPiece("sauna_stove", "fire_pit_iron", val);
			Fireplace component = val2.PiecePrefab.GetComponent<Fireplace>();
			if ((Object)(object)component == (Object)null)
			{
				Logger.LogError((object)"Fireplace component NOT found");
				return;
			}
			component.m_disableCoverCheck = true;
			ApplyStonePileBehavior(val2.PiecePrefab);
			HideOriginalMeshes(val2.PiecePrefab);
			SetupSteam(component);
			val2.PiecePrefab.AddComponent<SaunaStove>();
			if (!PieceManager.Instance.AddPiece(val2))
			{
				Logger.LogError((object)"sauna_stove: PieceManager rejected the piece");
				return;
			}
			_stovePrefab = val2.PiecePrefab;
			ApplyRecipeToPiece(_stovePrefab, StoveRecipeValue, "Wood:10,Coal:5,Stone:40", "sauna_stove");
			Logger.LogInfo((object)"sauna_stove registered");
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"sauna_stove failed: {arg}");
		}
	}

	private void ApplyStonePileBehavior(GameObject stovePrefab)
	{
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = PrefabManager.Instance.GetPrefab("stone_pile");
		if ((Object)(object)prefab == (Object)null)
		{
			Logger.LogWarning((object)"stone_pile prefab not found; keeping fire_pit_iron structure profile");
			return;
		}
		Piece component = prefab.GetComponent<Piece>();
		Piece component2 = stovePrefab.GetComponent<Piece>();
		if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null)
		{
			component2.m_groundPiece = component.m_groundPiece;
			component2.m_allowAltGroundPlacement = component.m_allowAltGroundPlacement;
			component2.m_groundOnly = component.m_groundOnly;
			component2.m_cultivatedGroundOnly = component.m_cultivatedGroundOnly;
			component2.m_waterPiece = component.m_waterPiece;
			component2.m_clipGround = component.m_clipGround;
			component2.m_clipEverything = component.m_clipEverything;
			component2.m_noClipping = component.m_noClipping;
			component2.m_noInWater = component.m_noInWater;
			component2.m_notOnWood = component.m_notOnWood;
			component2.m_notOnTiltingSurface = component.m_notOnTiltingSurface;
			component2.m_notOnFloor = component.m_notOnFloor;
			component2.m_inCeilingOnly = component.m_inCeilingOnly;
			component2.m_onlyInTeleportArea = component.m_onlyInTeleportArea;
			component2.m_allowedInDungeons = component.m_allowedInDungeons;
			component2.m_spaceRequirement = component.m_spaceRequirement;
			component2.m_placeEffect = component.m_placeEffect;
		}
		else
		{
			Logger.LogWarning((object)"stone_pile Piece component missing; placement profile not copied");
		}
		WearNTear component3 = prefab.GetComponent<WearNTear>();
		WearNTear component4 = stovePrefab.GetComponent<WearNTear>();
		if ((Object)(object)component3 != (Object)null && (Object)(object)component4 != (Object)null)
		{
			component4.m_health = component3.m_health;
			component4.m_materialType = component3.m_materialType;
			component4.m_damages = component3.m_damages;
			component4.m_burnable = component3.m_burnable;
			component4.m_noSupportWear = component3.m_noSupportWear;
			component4.m_noRoofWear = component3.m_noRoofWear;
			component4.m_supports = component3.m_supports;
			component4.m_destroyedEffect = component3.m_destroyedEffect;
			component4.m_hitEffect = component3.m_hitEffect;
			component4.m_switchEffect = component3.m_switchEffect;
			Logger.LogInfo((object)($"sauna structure copied from stone_pile: health={component4.m_health}, " + $"material={component4.m_materialType}, noSupportWear={component4.m_noSupportWear}, " + $"supports={component4.m_supports}, noRoofWear={component4.m_noRoofWear}"));
		}
		else
		{
			Logger.LogWarning((object)"stone_pile WearNTear missing; structure profile not copied");
		}
	}

	private void HideOriginalMeshes(GameObject stovePrefab)
	{
		MeshRenderer[] componentsInChildren = stovePrefa