Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SaunaMod v2.1.0
plugins/SaunaMod.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.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