Decompiled source of TheFloods v0.16.3

TheFloods.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace TheFloods;

[BepInPlugin("marc.thefloods", "The Broken Cycle", "0.16.3")]
public sealed class TheFloodsPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "marc.thefloods";

	public const string PluginName = "The Broken Cycle";

	public const string PluginVersion = "0.16.3";

	internal static TheFloodsPlugin Instance;

	private FloodConfig _config;

	private FloodDirector _director;

	private FloodVisuals _visuals;

	private FloodWaterAdapter _waterAdapter;

	private FloodEnvironmentAdapter _environmentAdapter;

	private bool _rpcRegistered;

	private bool _loggedReady;

	private bool _lateErrorLogged;

	internal void ReapplyWaterAfterNativeUpdate(WaterVolume volume)
	{
		if (_waterAdapter != null)
		{
			_waterAdapter.ReapplyAfterNativeUpdate(volume);
		}
	}

	private void LateUpdate()
	{
		try
		{
			if (_waterAdapter != null)
			{
				_waterAdapter.ReapplyForFrame();
				_waterAdapter.SyncPhysicsIfNeeded();
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("The Floods late water sync warning: " + ex.Message));
		}
		try
		{
			if (_director != null)
			{
				_director.LateTick();
			}
		}
		catch (Exception ex2)
		{
			if (!_lateErrorLogged)
			{
				_lateErrorLogged = true;
				((BaseUnityPlugin)this).Logger.LogError((object)("The Floods atmosphere error (logged once): " + ex2));
			}
		}
	}

	private void OnEnable()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Expected O, but got Unknown
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnCameraPreCull));
		Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(OnCameraPostRender));
	}

	private void OnDisable()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Expected O, but got Unknown
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnCameraPreCull));
		Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(OnCameraPostRender));
	}

	private void OnCameraPostRender(Camera camera)
	{
		try
		{
			EarthquakeSystem.Current?.OnPostRender(camera);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("The Floods earthquake render warning: " + ex.Message));
		}
	}

	private void OnCameraPreCull(Camera camera)
	{
		try
		{
			if (_waterAdapter != null)
			{
				_waterAdapter.ReapplyVisualsForRender();
			}
			EarthquakeSystem.Current?.OnPreCull(camera);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("The Floods camera water sync warning: " + ex.Message));
		}
	}

	private void Awake()
	{
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		_config = new FloodConfig(((BaseUnityPlugin)this).Config);
		_waterAdapter = new FloodWaterAdapter(((BaseUnityPlugin)this).Logger, _config);
		_environmentAdapter = new FloodEnvironmentAdapter(((BaseUnityPlugin)this).Logger, _config);
		_visuals = new FloodVisuals(_config);
		_director = new FloodDirector(((BaseUnityPlugin)this).Logger, _config, _waterAdapter, _environmentAdapter, _visuals);
		new Harmony("marc.thefloods").PatchAll(typeof(TheFloodsPlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"The Broken Cycle 0.16.3: loaded. Waiting for world network.");
	}

	private void Update()
	{
		try
		{
			if (!_rpcRegistered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<string>("TheFloods_State_01", (Action<long, string>)ReceiveStateRpc);
				ZRoutedRpc.instance.Register<string>("TheFloods_Message_01", (Action<long, string>)ReceiveMessageRpc);
				ZRoutedRpc.instance.Register<string>("TheFloods_Strike_01", (Action<long, string>)ReceiveStrikeRpc);
				_rpcRegistered = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"The Floods: routed RPC handlers registered.");
			}
			_director.Tick();
			if (!_loggedReady && _director.IsWorldReady)
			{
				_loggedReady = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"The Floods: world controller ready. Type 'floods help' in F5 for testing controls.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("The Floods Update error: " + ex));
		}
	}

	private void OnGUI()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Invalid comparison between Unknown and I4
		try
		{
			if (Event.current != null && (int)Event.current.type == 7)
			{
				_visuals.Draw(_director.CurrentState, _director.CurrentStormStrength, _director.CurrentWildfireIntensity, _director.CurrentStormApproach, _config);
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("The Floods sky overlay error: " + ex.Message));
		}
	}

	private void OnDestroy()
	{
		try
		{
			_waterAdapter.Dispose();
			if (_director != null)
			{
				_director.Dispose();
			}
			_environmentAdapter.ReleaseForcedEnvironment();
			_visuals.Dispose();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("The Floods cleanup warning: " + ex.Message));
		}
		finally
		{
			Instance = null;
		}
	}

	private static void ReceiveStateRpc(long sender, string payload)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			Instance._director.ReceiveState(payload);
		}
	}

	private static void ReceiveMessageRpc(long sender, string message)
	{
		if ((Object)(object)MessageHud.instance != (Object)null && !string.IsNullOrWhiteSpace(message))
		{
			MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false, true);
		}
	}

	private static void ReceiveStrikeRpc(long sender, string payload)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			Instance._director.ReceiveStrike(payload);
		}
	}

	internal bool TryHandleConsoleCommand(Terminal terminal)
	{
		string text = TerminalReflection.ReadInput(terminal);
		if (string.IsNullOrWhiteSpace(text))
		{
			return false;
		}
		string[] array = text.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
		bool flag = array.Length != 0 && array[0].Equals("floods", StringComparison.OrdinalIgnoreCase);
		bool flag2 = array.Length != 0 && array[0].Equals("strongwinds", StringComparison.OrdinalIgnoreCase);
		if (array.Length == 0 || (!flag && !flag2))
		{
			return false;
		}
		TerminalReflection.ClearInput(terminal);
		if (!_director.IsAuthoritative)
		{
			TerminalReflection.Write(terminal, "The Floods: commands must be run by the world host/server.");
			return true;
		}
		string text2 = (flag2 ? _director.HandleStrongWindsCommand(array.Skip(1).ToArray()) : _director.HandleCommand(array.Skip(1).ToArray()));
		TerminalReflection.Write(terminal, text2);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[console] " + text.Trim() + " -> " + text2));
		return true;
	}
}
internal sealed class FloodEventProfile
{
	internal readonly FloodEventType Type;

	internal readonly string DisplayName;

	internal readonly ConfigEntry<bool> Enabled;

	internal readonly ConfigEntry<float> HeightMeters;

	internal readonly ConfigEntry<float> MinimumHeightMeters;

	internal readonly ConfigEntry<float> MaximumHeightMeters;

	internal readonly ConfigEntry<float> OmenDays;

	internal readonly ConfigEntry<float> RisingDays;

	internal readonly ConfigEntry<float> PeakDays;

	internal readonly ConfigEntry<float> RecedingDays;

	internal readonly ConfigEntry<int> CooldownDays;

	internal readonly ConfigEntry<float> DailyChance;

	internal FloodEventProfile(ConfigFile config, FloodEventType type, string section, string displayName, float defaultHeight, float defaultMinimumHeight, float defaultMaximumHeight, float defaultOmenDays, float defaultRisingDays, float defaultPeakDays, float defaultRecedingDays, int defaultCooldownDays, float defaultDailyChance)
	{
		Type = type;
		DisplayName = displayName;
		Enabled = config.Bind<bool>(section, "Enabled", true, "Allow this event to occur naturally.");
		HeightMeters = config.Bind<float>(section, "HeightMeters", defaultHeight, "Legacy nominal height retained for older configs. Named events use MinimumHeightMeters and MaximumHeightMeters.");
		MinimumHeightMeters = config.Bind<float>(section, "MinimumHeightMeters", defaultMinimumHeight, "Lowest possible sea-level surge when this event begins. The exact height is rolled once and saved with the event.");
		MaximumHeightMeters = config.Bind<float>(section, "MaximumHeightMeters", defaultMaximumHeight, "Highest possible sea-level surge when this event begins. The exact height is rolled once and saved with the event.");
		OmenDays = config.Bind<float>(section, "OmenDays", defaultOmenDays, "How long the distant warning phase lasts in Valheim days.");
		RisingDays = config.Bind<float>(section, "RisingDays", defaultRisingDays, "How long the water takes to reach its height in Valheim days.");
		PeakDays = config.Bind<float>(section, "PeakDays", defaultPeakDays, "How long the event remains at full strength in Valheim days.");
		RecedingDays = config.Bind<float>(section, "RecedingDays", defaultRecedingDays, "How long the water takes to return in Valheim days.");
		CooldownDays = config.Bind<int>(section, "CooldownDays", defaultCooldownDays, "Minimum completed in-game days before this event can occur naturally again.");
		DailyChance = config.Bind<float>(section, "DailyChance", defaultDailyChance, "Natural chance for this event on each eligible in-game day. 0.01 = 1%.");
	}

	internal float GetMinimumHeight()
	{
		return Mathf.Min(MinimumHeightMeters.Value, MaximumHeightMeters.Value);
	}

	internal float GetMaximumHeight()
	{
		return Mathf.Max(MinimumHeightMeters.Value, MaximumHeightMeters.Value);
	}
}
internal sealed class FloodConfig
{
	internal readonly ConfigEntry<bool> Enabled;

	internal readonly ConfigEntry<int> MinimumWorldDay;

	internal readonly ConfigEntry<float> GameDaySeconds;

	internal readonly ConfigEntry<float> OmenDays;

	internal readonly ConfigEntry<float> RisingDays;

	internal readonly ConfigEntry<float> PeakDays;

	internal readonly ConfigEntry<float> RecedingDays;

	internal readonly ConfigEntry<float> MaximumSurgeMeters;

	internal readonly ConfigEntry<float> DebugMaximumSurgeMeters;

	internal readonly FloodEventProfile StormTide;

	internal readonly FloodEventProfile FlashSurge;

	internal readonly FloodEventProfile GreatFlood;

	internal readonly FloodEventProfile Drought;

	internal readonly FloodEventProfile Wildfire;

	internal readonly ConfigEntry<float> FirstOrdinaryEventMinimumHours;

	internal readonly ConfigEntry<float> FirstOrdinaryEventMaximumHours;

	internal readonly ConfigEntry<float> OrdinaryRecoveryMinimumHours;

	internal readonly ConfigEntry<float> OrdinaryRecoveryMaximumHours;

	internal readonly ConfigEntry<float> FlashSurgeRecoveryMinimumHours;

	internal readonly ConfigEntry<float> FlashSurgeRecoveryMaximumHours;

	internal readonly ConfigEntry<float> GreatFloodRecoveryMinimumHours;

	internal readonly ConfigEntry<float> GreatFloodRecoveryMaximumHours;

	internal readonly ConfigEntry<float> StormTideWeight;

	internal readonly ConfigEntry<float> DroughtWeight;

	internal readonly ConfigEntry<float> FlashSurgeWeight;

	internal readonly ConfigEntry<float> GreatFloodFirstEligibleWorldHours;

	internal readonly ConfigEntry<float> GreatFloodFirstTargetMinimumWorldHours;

	internal readonly ConfigEntry<float> GreatFloodFirstTargetMaximumWorldHours;

	internal readonly ConfigEntry<float> GreatFloodFirstForceByWorldHours;

	internal readonly ConfigEntry<float> GreatFloodRepeatMinimumHours;

	internal readonly ConfigEntry<float> GreatFloodRepeatMaximumHours;

	internal readonly ConfigEntry<float> GreatFloodRepeatForceByHours;

	internal readonly ConfigEntry<float> SevereDroughtChance;

	internal readonly ConfigEntry<float> SevereDroughtMinimumMeters;

	internal readonly ConfigEntry<float> SevereDroughtMaximumMeters;

	internal readonly ConfigEntry<bool> EnableFlashSurgeDrawdown;

	internal readonly ConfigEntry<float> FlashSurgeDrawdownMinimumMeters;

	internal readonly ConfigEntry<float> FlashSurgeDrawdownMaximumMeters;

	internal readonly ConfigEntry<bool> EnableLiveWaterLevel;

	internal readonly ConfigEntry<bool> EnableTides;

	internal readonly ConfigEntry<float> TideAmplitudeMeters;

	internal readonly ConfigEntry<float> TideCyclesPerDay;

	internal readonly ConfigEntry<bool> EnableWaterSurfaceQueryPatch;

	internal readonly ConfigEntry<bool> EnableOceanRendererLift;

	internal readonly ConfigEntry<float> OceanRendererMinimumSpan;

	internal readonly ConfigEntry<float> WaterSurfaceScanSeconds;

	internal readonly ConfigEntry<bool> EnableNativeThunderstorm;

	internal readonly ConfigEntry<string> NativeStormEnvironment;

	internal readonly ConfigEntry<float> NativeStormStartStrength;

	internal readonly ConfigEntry<float> SkyDarkening;

	internal readonly ConfigEntry<bool> EnableBlackHorizonStormBank;

	internal readonly ConfigEntry<float> HorizonStormBankOpacity;

	internal readonly ConfigEntry<float> HorizonStormBankHeight;

	internal readonly ConfigEntry<bool> EnableDistantLightningFlashes;

	internal readonly ConfigEntry<float> WildfireSkyTintStrength;

	internal readonly ConfigEntry<bool> EnableApproachingStormFront;

	internal readonly ConfigEntry<float> StormFrontDarkness;

	internal readonly ConfigEntry<float> StormFrontHorizonHeight;

	internal readonly ConfigEntry<bool> EnableCustomStormAudio;

	internal readonly ConfigEntry<float> MasterStormAudioVolume;

	internal readonly ConfigEntry<float> IndoorStormAudioVolume;

	internal readonly ConfigEntry<float> OutdoorWindAudioVolume;

	internal readonly ConfigEntry<float> AudioFadeDuration;

	internal readonly ConfigEntry<bool> DebugAudioLogging;

	internal readonly ConfigEntry<bool> EnableLightningStrikes;

	internal readonly ConfigEntry<bool> EnableAmbientBolts;

	internal readonly ConfigEntry<float> PlayerStrikeMinStrength;

	internal readonly ConfigEntry<float> StrikeGapMinSeconds;

	internal readonly ConfigEntry<float> StrikeGapMaxSeconds;

	internal readonly ConfigEntry<float> DirectHitChance;

	internal readonly ConfigEntry<float> StrikeGraceSeconds;

	internal readonly ConfigEntry<float> MinLightningDamage;

	internal readonly ConfigEntry<float> MaxLightningDamage;

	internal readonly ConfigEntry<float> WetDamageMultiplier;

	internal readonly ConfigEntry<float> LightningPushForce;

	internal readonly ConfigEntry<bool> RequireExposed;

	internal readonly ConfigEntry<bool> NonLethalLightning;

	internal readonly ConfigEntry<float> BoltLightIntensity;

	internal readonly ConfigEntry<float> BoltLightRange;

	internal readonly ConfigEntry<float> BoltDurationSeconds;

	internal readonly ConfigEntry<float> AmbientBoltsPerMinuteAtPeak;

	internal readonly ConfigEntry<bool> EnableThunderAudio;

	internal readonly ConfigEntry<float> ThunderVolume;

	internal readonly ConfigEntry<float> SpeedOfSoundMetersPerSecond;

	internal readonly ConfigEntry<string> RainEnvironmentNames;

	internal readonly ConfigEntry<float> DrynessRisePerHour;

	internal readonly ConfigEntry<float> DrynessRiseDroughtMultiplier;

	internal readonly ConfigEntry<float> DrynessRainResetSeconds;

	internal readonly ConfigEntry<float> RainWetStormStrength;

	internal readonly ConfigEntry<float> WildfireWeight;

	internal readonly ConfigEntry<float> SpontaneousDrynessThreshold;

	internal readonly ConfigEntry<float> SpontaneousPerWindowChance;

	internal readonly ConfigEntry<float> SpontaneousWindowMinimumHours;

	internal readonly ConfigEntry<float> SpontaneousWindowMaximumHours;

	internal readonly ConfigEntry<float> LightningIgnitionChance;

	internal readonly ConfigEntry<float> IgnitionMinRadius;

	internal readonly ConfigEntry<float> IgnitionMaxRadius;

	internal readonly ConfigEntry<int> WildfireStartLocations;

	internal readonly ConfigEntry<int> WildfireSequenceCount;

	internal readonly ConfigEntry<int> MaxFireNodes;

	internal readonly ConfigEntry<float> FireFrontSpeedMultiplier;

	internal readonly ConfigEntry<float> SpreadChance;

	internal readonly ConfigEntry<float> SpreadStepMeters;

	internal readonly ConfigEntry<float> NodeLifeSeconds;

	internal readonly ConfigEntry<float> NodeRadius;

	internal readonly ConfigEntry<float> PlayerFireDamagePerTick;

	internal readonly ConfigEntry<float> FireDamageIntervalSeconds;

	internal readonly ConfigEntry<bool> DamageStructures;

	internal readonly ConfigEntry<float> DownwindBias;

	internal readonly ConfigEntry<bool> EnableWildfireTreefall;

	internal readonly ConfigEntry<float> WildfireTreefallChance;

	internal readonly ConfigEntry<bool> EnableSpotFires;

	internal readonly ConfigEntry<float> SpotFireChance;

	internal readonly ConfigEntry<float> SpotFireIntervalSeconds;

	internal readonly ConfigEntry<float> SpotFireMinDistanceMeters;

	internal readonly ConfigEntry<float> SpotFireMaxDistanceMeters;

	internal readonly ConfigEntry<string> WildfireForcedEnvironment;

	internal readonly ConfigEntry<bool> EmberBedEnabled;

	internal readonly ConfigEntry<float> EmberBedRadiusMultiplier;

	internal readonly ConfigEntry<float> EmberBedOpacity;

	internal readonly ConfigEntry<float> FlameStretchLength;

	internal readonly ConfigEntry<float> MaxDetailedFireDistance;

	internal readonly ConfigEntry<WildfireVfxSourceMode> WildfireVfxMode;

	internal readonly ConfigEntry<bool> EnableParticlePackVfx;

	internal readonly ConfigEntry<string> FireAssetBundlePath;

	internal readonly ConfigEntry<string> FireTextureFolderPath;

	internal readonly ConfigEntry<float> FlameIntensityMultiplier;

	internal readonly ConfigEntry<float> SmokeIntensityMultiplier;

	internal readonly ConfigEntry<float> SparkIntensityMultiplier;

	internal readonly ConfigEntry<int> MaxActiveFireLights;

	internal readonly ConfigEntry<float> FireVisibleDistance;

	internal readonly ConfigEntry<bool> FullFireEffects;

	internal readonly ConfigEntry<bool> PerformanceLogging;

	internal readonly ConfigEntry<bool> EnableHeatShimmer;

	internal readonly ConfigEntry<float> HeatShimmerStrength;

	internal readonly ConfigEntry<float> HeatShimmerDistance;

	internal readonly ConfigEntry<bool> EnableSmokySky;

	internal readonly ConfigEntry<float> SmokySkyStrength;

	internal readonly ConfigEntry<float> SmokySkyRadius;

	internal readonly ConfigEntry<bool> EnableFallingAsh;

	internal readonly ConfigEntry<bool> EnableSmokePlumes;

	internal readonly ConfigEntry<bool> EnableScorchedGround;

	internal readonly ConfigEntry<float> ScorchedGroundRecoveryDays;

	internal readonly ConfigEntry<float> ScorchedGroundStrength;

	internal readonly ConfigEntry<int> MaxSmokePlumes;

	internal readonly ConfigEntry<bool> EnableEarthquakes;

	internal readonly ConfigEntry<int> EarthquakeMinimumWorldDay;

	internal readonly ConfigEntry<float> EarthquakeMinimumHours;

	internal readonly ConfigEntry<float> EarthquakeMaximumHours;

	internal readonly ConfigEntry<float> EarthquakeMinimumSeconds;

	internal readonly ConfigEntry<float> EarthquakeMaximumSeconds;

	internal readonly ConfigEntry<float> EarthquakeChanceFlashSurge;

	internal readonly ConfigEntry<float> EarthquakeChanceGreatFlood;

	internal readonly ConfigEntry<float> EarthquakeChanceOtherEvents;

	internal readonly ConfigEntry<bool> EarthquakeTilt;

	internal readonly ConfigEntry<float> EarthquakeMaxTiltDegrees;

	internal readonly ConfigEntry<bool> EarthquakeLevelOcean;

	internal readonly ConfigEntry<bool> EarthquakeUprightCreatures;

	internal readonly ConfigEntry<bool> EarthquakeLevelSky;

	internal readonly ConfigEntry<float> EarthquakeShakeStrength;

	internal readonly ConfigEntry<bool> EarthquakeSteepSliding;

	internal readonly ConfigEntry<float> EarthquakeHeaveTiltDegrees;

	internal readonly ConfigEntry<float> EarthquakeObjectSlide;

	internal readonly ConfigEntry<bool> EarthquakeTreefall;

	internal readonly ConfigEntry<float> EarthquakeTreesPerMinute;

	internal readonly ConfigEntry<int> EarthquakeMaxTreefalls;

	internal readonly ConfigEntry<float> EarthquakeTreefallRadius;

	internal readonly ConfigEntry<bool> EarthquakeDust;

	internal readonly ConfigEntry<float> EarthquakeRumbleVolume;

	internal readonly ConfigEntry<bool> EarthquakeUseBossSummonSound;

	internal readonly ConfigEntry<string> EarthquakeRumbleSound;

	internal readonly ConfigEntry<string> EarthquakeCrackSounds;

	internal readonly ConfigEntry<string> EarthquakeSoundFile;

	internal readonly ConfigEntry<bool> DebugWildfireVfxLogging;

	internal readonly ConfigEntry<float> MeadowsFlammability;

	internal readonly ConfigEntry<float> BlackForestFlammability;

	internal readonly ConfigEntry<float> PlainsFlammability;

	internal readonly ConfigEntry<float> SwampFlammability;

	internal readonly ConfigEntry<float> MountainFlammability;

	internal readonly ConfigEntry<float> MistlandsFlammability;

	internal readonly ConfigEntry<float> AshlandsFlammability;

	internal readonly ConfigEntry<float> DeepNorthFlammability;

	internal readonly ConfigEntry<float> OtherBiomeFlammability;

	internal readonly ConfigEntry<bool> EnableStrongWinds;

	internal readonly ConfigEntry<float> StrongWindsEventChance;

	internal readonly ConfigEntry<float> StrongWindsCooldownHours;

	internal readonly ConfigEntry<float> StrongWindsDurationMinutes;

	internal readonly ConfigEntry<int> StrongWindsMinimumWorldDay;

	internal readonly ConfigEntry<float> StrongWindsWindStrengthMultiplier;

	internal readonly ConfigEntry<bool> EnableTreefall;

	internal readonly ConfigEntry<float> TreefallChance;

	internal readonly ConfigEntry<float> TreefallDistanceFromPlayer;

	internal readonly ConfigEntry<int> MaxTreefallsPerPlayer;

	internal readonly ConfigEntry<int> MaxTreefallsGlobally;

	internal readonly ConfigEntry<float> TreefallCooldownPerZoneSeconds;

	internal readonly ConfigEntry<float> ProtectedBaseRadius;

	internal readonly ConfigEntry<float> FallingTreeDamageToPlayers;

	internal readonly ConfigEntry<float> FallingTreeDamageToCreatures;

	internal readonly ConfigEntry<bool> StrongWindStructureDamage;

	internal readonly ConfigEntry<float> WindDebrisAmount;

	internal readonly ConfigEntry<bool> StrongWindsDebugMode;

	internal readonly ConfigEntry<bool> StrongWindsDebugIgnoreBaseProtection;

	internal readonly ConfigEntry<bool> DebugLogging;

	internal readonly ConfigEntry<bool> VerboseWaterTileBindingLogs;

	internal readonly ConfigEntry<bool> StartTestEventOnWorldLoad;

	internal readonly ConfigEntry<string> TestStartPhase;

	internal FloodConfig(ConfigFile config)
	{
		//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d0: Expected O, but got Unknown
		//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0609: Expected O, but got Unknown
		//IL_0cd8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ce2: Expected O, but got Unknown
		//IL_0d05: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d0f: Expected O, but got Unknown
		//IL_0d5e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d68: Expected O, but got Unknown
		//IL_111c: Unknown result type (might be due to invalid IL or missing references)
		//IL_1126: Expected O, but got Unknown
		//IL_11a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_11b3: Expected O, but got Unknown
		//IL_11e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_11ec: Expected O, but got Unknown
		//IL_1237: Unknown result type (might be due to invalid IL or missing references)
		//IL_1241: Expected O, but got Unknown
		//IL_1270: Unknown result type (might be due to invalid IL or missing references)
		//IL_127a: Expected O, but got Unknown
		//IL_12d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_12e0: Expected O, but got Unknown
		//IL_1323: Unknown result type (might be due to invalid IL or missing references)
		//IL_132d: Expected O, but got Unknown
		//IL_135c: Unknown result type (might be due to invalid IL or missing references)
		//IL_1366: Expected O, but got Unknown
		//IL_1395: Unknown result type (might be due to invalid IL or missing references)
		//IL_139f: Expected O, but got Unknown
		//IL_13ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_13d8: Expected O, but got Unknown
		//IL_1407: Unknown result type (might be due to invalid IL or missing references)
		//IL_1411: Expected O, but got Unknown
		//IL_1440: Unknown result type (might be due to invalid IL or missing references)
		//IL_144a: Expected O, but got Unknown
		//IL_1479: Unknown result type (might be due to invalid IL or missing references)
		//IL_1483: Expected O, but got Unknown
		//IL_14b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_14bc: Expected O, but got Unknown
		//IL_1507: Unknown result type (might be due to invalid IL or missing references)
		//IL_1511: Expected O, but got Unknown
		//IL_1540: Unknown result type (might be due to invalid IL or missing references)
		//IL_154a: Expected O, but got Unknown
		//IL_15cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_15d7: Expected O, but got Unknown
		//IL_1622: Unknown result type (might be due to invalid IL or missing references)
		//IL_162c: Expected O, but got Unknown
		//IL_1677: Unknown result type (might be due to invalid IL or missing references)
		//IL_1681: Expected O, but got Unknown
		//IL_16a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_16b3: Expected O, but got Unknown
		//IL_16e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_16ec: Expected O, but got Unknown
		//IL_1737: Unknown result type (might be due to invalid IL or missing references)
		//IL_1741: Expected O, but got Unknown
		//IL_1808: Unknown result type (might be due to invalid IL or missing references)
		//IL_1812: Expected O, but got Unknown
		//IL_1841: Unknown result type (might be due to invalid IL or missing references)
		//IL_184b: Expected O, but got Unknown
		Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch for The Floods.");
		MinimumWorldDay = config.Bind<int>("Event Rules", "MinimumWorldDay", 6, "No natural water event can occur before this Valheim day. V0.8 defaults to day 6 so an ordinary event can arrive during an active early world without appearing on day one.");
		GameDaySeconds = config.Bind<float>("Timeline", "GameDaySecondsFallback", 1800f, "Fallback full Valheim day length used only if the game day API is unavailable.");
		OmenDays = config.Bind<float>("Timeline", "OmenDays", 0.5f, "Custom/debug event omen duration in Valheim days.");
		RisingDays = config.Bind<float>("Timeline", "RisingDays", 2f, "Custom/debug event rise duration in Valheim days.");
		PeakDays = config.Bind<float>("Timeline", "PeakDays", 2f, "Custom/debug event peak duration in Valheim days.");
		RecedingDays = config.Bind<float>("Timeline", "RecedingDays", 2f, "Custom/debug event receding duration in Valheim days.");
		MaximumSurgeMeters = config.Bind<float>("Flood Height", "MaximumSurgeMeters", 3.5f, "Height used by legacy floods start and debug test events.");
		DebugMaximumSurgeMeters = config.Bind<float>("Flood Height", "DebugMaximumSurgeMeters", 20f, "Safety cap for floods set <metres>.");
		StormTide = new FloodEventProfile(config, FloodEventType.StormTide, "Storm Tide", "Storm Tide", 2.5f, 1.5f, 5f, 0.066667f, 0.166667f, 0.2f, 0.2f, 7, 0.075f);
		FlashSurge = new FloodEventProfile(config, FloodEventType.FlashSurge, "Flash Surge", "Flash Surge", 10f, 6f, 12f, 0.035f, 0.066667f, 0.1f, 0.233333f, 20, 0.028f);
		GreatFlood = new FloodEventProfile(config, FloodEventType.GreatFlood, "The Great Flood", "The Great Flood", 12f, 8f, 16f, 0.233333f, 0.6f, 0.4f, 0.6f, 55, 0.0075f);
		Drought = new FloodEventProfile(config, FloodEventType.Drought, "Drought", "Drought", 4f, 3f, 5f, 0.1f, 0.3f, 0.4f, 0.3f, 10, 0.12f);
		Wildfire = new FloodEventProfile(config, FloodEventType.Wildfire, "Wildfire", "Wildfire", 0f, 0f, 0f, 0.02f, 0.5f, 0.35f, 0.4f, 12, 0.02f);
		FirstOrdinaryEventMinimumHours = config.Bind<float>("Water Cycle Scheduler", "FirstOrdinaryEventMinimumHours", 0.75f, "After MinimumWorldDay, the first ordinary event begins after a hidden random delay in this range. Hours are active server/world hours.");
		FirstOrdinaryEventMaximumHours = config.Bind<float>("Water Cycle Scheduler", "FirstOrdinaryEventMaximumHours", 2f, "Largest hidden delay after MinimumWorldDay before the first ordinary event can begin.");
		OrdinaryRecoveryMinimumHours = config.Bind<float>("Water Cycle Scheduler", "OrdinaryRecoveryMinimumHours", 1.5f, "Random quiet-world minimum after a Storm Tide or Drought ends before another ordinary event can begin.");
		OrdinaryRecoveryMaximumHours = config.Bind<float>("Water Cycle Scheduler", "OrdinaryRecoveryMaximumHours", 3.5f, "Random quiet-world maximum after a Storm Tide or Drought ends before another ordinary event can begin.");
		FlashSurgeRecoveryMinimumHours = config.Bind<float>("Water Cycle Scheduler", "FlashSurgeRecoveryMinimumHours", 2.5f, "Random quiet-world minimum after a Flash Surge ends before another ordinary event can begin.");
		FlashSurgeRecoveryMaximumHours = config.Bind<float>("Water Cycle Scheduler", "FlashSurgeRecoveryMaximumHours", 5f, "Random quiet-world maximum after a Flash Surge ends before another ordinary event can begin.");
		GreatFloodRecoveryMinimumHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodRecoveryMinimumHours", 6f, "Random quiet-world minimum after the Great Flood ends before any natural water event can begin.");
		GreatFloodRecoveryMaximumHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodRecoveryMaximumHours", 10f, "Random quiet-world maximum after the Great Flood ends before any natural water event can begin.");
		StormTideWeight = config.Bind<float>("Water Cycle Scheduler", "StormTideWeight", 45f, "Relative chance that the next ordinary water event is a Storm Tide.");
		DroughtWeight = config.Bind<float>("Water Cycle Scheduler", "DroughtWeight", 40f, "Relative chance that the next ordinary water event is a Drought.");
		FlashSurgeWeight = config.Bind<float>("Water Cycle Scheduler", "FlashSurgeWeight", 15f, "Relative chance that the next ordinary water event is a Flash Surge.");
		GreatFloodFirstEligibleWorldHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodFirstEligibleWorldHours", 8f, "The Great Flood cannot naturally occur before this much total active world age.");
		GreatFloodFirstTargetMinimumWorldHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodFirstTargetMinimumWorldHours", 15f, "Earliest target age for a world's first Great Flood. The exact target is hidden and randomized.");
		GreatFloodFirstTargetMaximumWorldHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodFirstTargetMaximumWorldHours", 25f, "Latest target age for a world's first Great Flood. The exact target is hidden and randomized.");
		GreatFloodFirstForceByWorldHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodFirstForceByWorldHours", 32f, "Safety limit: if a mature world still has not seen its first Great Flood, the scheduler waits for the next safe quiet window and begins one by this world age.");
		GreatFloodRepeatMinimumHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodRepeatMinimumHours", 35f, "Minimum active-world hours before another Great Flood can be scheduled after one completes.");
		GreatFloodRepeatMaximumHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodRepeatMaximumHours", 60f, "Maximum randomized active-world hours before another Great Flood is scheduled after one completes.");
		GreatFloodRepeatForceByHours = config.Bind<float>("Water Cycle Scheduler", "GreatFloodRepeatForceByHours", 72f, "Safety limit after a Great Flood: the next one will be allowed at the first safe quiet window by this many active-world hours later.");
		SevereDroughtChance = config.Bind<float>("Drought", "SevereDroughtChance", 0.2f, "Chance that a naturally scheduled Drought becomes a Severe Drought instead of the normal 3–5m retreat. 0.20 means 20%.");
		SevereDroughtMinimumMeters = config.Bind<float>("Drought", "SevereDroughtMinimumMeters", 5f, "Smallest sea retreat for a Severe Drought.");
		SevereDroughtMaximumMeters = config.Bind<float>("Drought", "SevereDroughtMaximumMeters", 7.5f, "Largest sea retreat for a Severe Drought.");
		RemoveLegacyDailyRollSettings(config);
		EnableFlashSurgeDrawdown = config.Bind<bool>("Flash Surge", "EnableDrawdown", true, "Before a Flash Surge, the sea can briefly pull back as a warning.");
		FlashSurgeDrawdownMinimumMeters = config.Bind<float>("Flash Surge", "DrawdownMinimumMeters", 1f, "Smallest pre-surge sea retreat in metres. Used only when EnableDrawdown is true.");
		FlashSurgeDrawdownMaximumMeters = config.Bind<float>("Flash Surge", "DrawdownMaximumMeters", 3f, "Largest pre-surge sea retreat in metres. Used only when EnableDrawdown is true.");
		EnableLiveWaterLevel = config.Bind<bool>("Flood Height", "EnableLiveWaterLevel", true, "Master switch for the live flood-water adapter. Use a copied test world first.");
		EnableTides = config.Bind<bool>("Tides", "EnableTides", true, "Natural tides: the sea rises and falls on a steady cycle tied to the in-game day, on top of any flood or drought. Needs EnableLiveWaterLevel.");
		TideAmplitudeMeters = config.Bind<float>("Tides", "TideAmplitudeMeters", 1f, new ConfigDescription("How far the sea rises above and falls below its normal level at high and low tide, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
		TideCyclesPerDay = config.Bind<float>("Tides", "TideCyclesPerDay", 2f, new ConfigDescription("High tides per in-game day. 2 = like real seas (two highs and two lows a day, about every 15 real minutes with Valheim's 30-minute day); 1 = one high and one low per day.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 8f), Array.Empty<object>()));
		EnableWaterSurfaceQueryPatch = config.Bind<bool>("Flood Height", "EnableWaterSurfaceQueryPatch", true, "Uses Valheim WaterVolume terrain-water offsets for live flood physics without editing terrain.");
		EnableOceanRendererLift = config.Bind<bool>("Flood Height", "EnableOceanRendererLift", true, "Raises terrain-water mesh children so the shoreline visibly floods. Does not move terrain.");
		OceanRendererMinimumSpan = config.Bind<float>("Flood Height", "OceanRendererMinimumSpan", 256f, "Legacy compatibility setting. Ocean tiles are identified through their linked Heightmap.");
		WaterSurfaceScanSeconds = config.Bind<float>("Flood Height", "WaterSurfaceScanSeconds", 3f, "How often the mod scans for newly loaded terrain water tiles.");
		EnableNativeThunderstorm = config.Bind<bool>("Storm Visuals", "EnableNativeThunderstorm", true, "Use Valheim's native thunderstorm during active flood phases.");
		NativeStormEnvironment = config.Bind<string>("Storm Visuals", "NativeStormEnvironment", "ThunderStorm", "Internal Valheim environment name to force during active flood phases.");
		NativeStormStartStrength = config.Bind<float>("Storm Visuals", "NativeStormStartStrength", 0.45f, "Storm strength at which native thunder, rain and lightning begin. The omen remains mostly distant and black before this.");
		SkyDarkening = config.Bind<float>("Storm Visuals", "SkyDarkening", 0.72f, "Strength of the storm-darkening overlay. 0 disables it.");
		EnableBlackHorizonStormBank = config.Bind<bool>("Storm Visuals", "EnableBlackHorizonStormBank", false, "Legacy screen-space storm-bank option. Disabled by default because it could form a hard black horizontal band on some displays.");
		HorizonStormBankOpacity = config.Bind<float>("Storm Visuals", "HorizonStormBankOpacity", 0.88f, "Opacity of the distant black storm bank.");
		HorizonStormBankHeight = config.Bind<float>("Storm Visuals", "HorizonStormBankHeight", 0.64f, "Vertical screen fraction occupied by the distant storm bank.");
		EnableDistantLightningFlashes = config.Bind<bool>("Storm Visuals", "EnableDistantLightningFlashes", true, "Adds subtle distant lightning flashes on top of Valheim's native thunderstorm.");
		WildfireSkyTintStrength = config.Bind<float>("Storm Visuals", "WildfireSkyTintStrength", 0.52f, "Strength of the warm smoke tint during a wildfire event.");
		EnableApproachingStormFront = config.Bind<bool>("Storm Visuals", "EnableApproachingStormFront", false, "Legacy screen-space storm-front option. Disabled by default because it could look like a black overlay band instead of a distant sky.");
		StormFrontDarkness = config.Bind<float>("Storm Visuals", "StormFrontDarkness", 0.95f, "Opacity of the approaching black storm front at full strength.");
		StormFrontHorizonHeight = config.Bind<float>("Storm Visuals", "StormFrontHorizonHeight", 0.72f, "Screen height used by the distant storm-wall cloud band.");
		EnableCustomStormAudio = config.Bind<bool>("Storm Audio", "Enable Custom Storm Audio", true, "Loads the three supplied MP3 storm loops from TheBrokenCycle/Audio.");
		MasterStormAudioVolume = config.Bind<float>("Storm Audio", "Master Storm Audio Volume", 0.85f, "Master volume multiplier for all custom storm audio.");
		IndoorStormAudioVolume = config.Bind<float>("Storm Audio", "Indoor Storm Audio Volume", 0.72f, "Volume multiplier for stormoutside.mp3 when the player is inside a sealed shelter.");
		OutdoorWindAudioVolume = config.Bind<float>("Storm Audio", "Outdoor Wind Audio Volume", 0.8f, "Volume multiplier for loudwind.mp3 and windthroughtrees.mp3 outdoors.");
		AudioFadeDuration = config.Bind<float>("Storm Audio", "Audio Fade Duration", 4.5f, "Seconds used for custom storm audio fades and crossfades.");
		DebugAudioLogging = config.Bind<bool>("Storm Audio", "Debug Audio Logging", false, "Writes custom storm audio load/playback diagnostics when enabled.");
		EnableLightningStrikes = config.Bind<bool>("Lightning", "EnableLightningStrikes", true, "Allows storms to create real ground lightning strikes. Direct strikes can damage and kill exposed players.");
		EnableAmbientBolts = config.Bind<bool>("Lightning", "EnableAmbientBolts", true, "Shows non-damaging procedural bolts around players during active storm phases.");
		PlayerStrikeMinStrength = config.Bind<float>("Lightning", "PlayerStrikeMinStrength", 0.55f, "Minimum storm strength before dangerous player-targeting lightning is allowed.");
		StrikeGapMinSeconds = config.Bind<float>("Lightning", "StrikeGapMinSeconds", 25f, "Shortest real-time gap between dangerous lightning checks at peak storm strength.");
		StrikeGapMaxSeconds = config.Bind<float>("Lightning", "StrikeGapMaxSeconds", 90f, "Longest real-time gap between dangerous lightning checks at the edge of a storm.");
		DirectHitChance = config.Bind<float>("Lightning", "DirectHitChance", 0.02f, "Chance that a dangerous strike hits an exposed player directly instead of landing nearby. Kept rare (2%) so a direct hit is possible but unusual; near misses still land close for the drama.");
		StrikeGraceSeconds = config.Bind<float>("Lightning", "StrikeGraceSeconds", 8f, "Minimum real-time grace period between damaging strikes on the same player.");
		MinLightningDamage = config.Bind<float>("Lightning", "MinLightningDamage", 40f, "Lightning damage near the dangerous-storm threshold.");
		MaxLightningDamage = config.Bind<float>("Lightning", "MaxLightningDamage", 140f, "Lightning damage at peak storm strength. This can kill low-health players.");
		WetDamageMultiplier = config.Bind<float>("Lightning", "WetDamageMultiplier", 1.5f, "Damage multiplier for wet players struck by lightning.");
		LightningPushForce = config.Bind<float>("Lightning", "LightningPushForce", 8f, "Knockback force applied by a direct lightning strike.");
		RequireExposed = config.Bind<bool>("Lightning", "RequireExposed", true, "Sheltered players are immune to direct lightning strikes.");
		NonLethalLightning = config.Bind<bool>("Lightning", "NonLethalLightning", false, "If true, direct lightning leaves the player at 1 HP instead of killing them.");
		BoltLightIntensity = config.Bind<float>("Lightning", "BoltLightIntensity", 6f, "World light intensity for procedural lightning bolts. V0.9.1 keeps this modest and uses the screen flash for most brightness.");
		BoltLightRange = config.Bind<float>("Lightning", "BoltLightRange", 55f, "World light range for procedural lightning bolts. Kept short to avoid bloom streaking.");
		BoltDurationSeconds = config.Bind<float>("Lightning", "BoltDurationSeconds", 0.48f, "How long a procedural bolt remains visible. The renderer enforces a visible hold so forks can be read by the eye.");
		AmbientBoltsPerMinuteAtPeak = config.Bind<float>("Lightning", "AmbientBoltsPerMinuteAtPeak", 12f, "Approximate number of non-damaging ambient bolts per minute at peak storm strength.");
		EnableThunderAudio = config.Bind<bool>("Lightning", "EnableThunderAudio", true, "Play distance-delayed thunder cracks after each lightning strike.");
		ThunderVolume = config.Bind<float>("Lightning", "ThunderVolume", 0.8f, "Volume multiplier for procedural thunder.");
		SpeedOfSoundMetersPerSecond = config.Bind<float>("Lightning", "SpeedOfSoundMetersPerSecond", 340f, "Used to compute the delay between flash and thunder.");
		RainEnvironmentNames = config.Bind<string>("Lightning", "RainEnvironmentNames", "Rain,LightRain,ThunderStorm,SwampRain,Mistlands_rain,Mistlands_thunder,Ashrain,Ashlands_ashrain", "Comma-separated environment names that count as wet rain for lightning ignition and wildfire suppression.");
		DrynessRisePerHour = config.Bind<float>("Dryness", "DrynessRisePerHour", 0.055f, "How much the saved dryness index rises per active world hour without rain.");
		DrynessRiseDroughtMultiplier = config.Bind<float>("Dryness", "DrynessRiseDroughtMultiplier", 3f, "Multiplier for dryness gain during drought phases.");
		DrynessRainResetSeconds = config.Bind<float>("Dryness", "DrynessRainResetSeconds", 900f, "Real active seconds of rain/storm needed to pull dryness from 1 toward 0.");
		RainWetStormStrength = config.Bind<float>("Dryness", "RainWetStormStrength", 0.62f, "Storm strength at which rain environments make the ground wet enough to block ignition.");
		WildfireWeight = config.Bind<float>("Wildfire", "WildfireWeight", 1f, "Reserved tuning weight for wildfire scheduling. Wildfires use their own dryness-driven schedule instead of the water-event pool.");
		SpontaneousDrynessThreshold = config.Bind<float>("Wildfire", "SpontaneousDrynessThreshold", 0.7f, "Dryness index required before spontaneous wildfire ignition can be scheduled.");
		SpontaneousPerWindowChance = config.Bind<float>("Wildfire", "SpontaneousPerWindowChance", 0.05f, "Chance per wildfire schedule window to ignite when dryness and biome fuel allow it.");
		SpontaneousWindowMinimumHours = config.Bind<float>("Wildfire", "SpontaneousWindowMinimumHours", 1f, "Shortest active-world delay between spontaneous wildfire ignition checks.");
		SpontaneousWindowMaximumHours = config.Bind<float>("Wildfire", "SpontaneousWindowMaximumHours", 3f, "Longest active-world delay between spontaneous wildfire ignition checks.");
		LightningIgnitionChance = config.Bind<float>("Wildfire", "LightningIgnitionChance", 0.25f, "Base chance that a dry lightning strike ignites a wildfire, before dryness and biome flammability are applied.");
		IgnitionMinRadius = config.Bind<float>("Wildfire", "IgnitionMinRadius", 40f, "Closest distance from the player that each of a wildfire's starting fires may be placed.");
		IgnitionMaxRadius = config.Bind<float>("Wildfire", "IgnitionMaxRadius", 120f, "Farthest distance from the player that each of a wildfire's starting fires may be placed.");
		WildfireSequenceCount = config.Bind<int>("Wildfire", "WildfireSequenceCount", 3, new ConfigDescription("How many fires each wildfire event burns, ONE AT A TIME. The event's spreading time (about 25 minutes) is split evenly between them: each fire spreads for its share, then dies down over a few seconds and the next one starts near the player. Only one fire is ever burning, which keeps the load steady. 1 = a single fire for the whole event.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
		WildfireStartLocations = config.Bind<int>("Wildfire", "WildfireStartLocations", 1, new ConfigDescription("FIRES AT ONCE (1-3, default 1). How many separate fires burn at the same time, spread evenly around the player 40-120 m away. Each fire gets its own MaxFireNodes budget, so 2 fires = twice the burning area and roughly twice the cost.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
		MaxFireNodes = config.Bind<int>("Wildfire", "MaxFireNodes", 220, "Most fire points burning at once. Each point is a full set of flames/smoke/light, so this is the main performance setting together with FireVisibleDistance. 220 points at the default 8 m spacing cover about 7,000 square metres. Lower = smoother, higher = bigger fires.");
		FireFrontSpeedMultiplier = config.Bind<float>("Wildfire", "FireFrontSpeedMultiplier", 0.55f, new ConfigDescription("SPEED SLIDER. How fast the fire front moves outward, as a multiple of the local player's sprint speed (read live from the game, about 7 m/s). Default 0.55 = about normal running pace: you can always get away if you keep moving, and sprinting gives a safety margin, but anyone who stops or gets cornered is in trouble; 1 = exactly run speed; 1.1 = slightly faster than you; 0.5 = half speed. The mod measures the real front speed each second and corrects toward this target (see 'floods status'). Also scales spot-fire frequency.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
		SpreadChance = config.Bind<float>("Wildfire", "SpreadChance", 1f, "Chance that a fire node spreads on each interval during active fire phases.");
		SpreadStepMeters = config.Bind<float>("Wildfire", "SpreadStepMeters", 8f, "Spacing of the fire points as the fire spreads (they keep at least 70% of this apart). Each flame is 11-16 m wide, so 8 m keeps the fire continuous with far fewer points than tighter spacing. Smaller = denser but much heavier. Front SPEED is set by FireFrontSpeedMultiplier, not by this.");
		NodeLifeSeconds = config.Bind<float>("Wildfire", "NodeLifeSeconds", 130f, "How long a fire node burns before burning out on its own. Shorter so trailing fire dies off quickly and frees budget for the advancing front - this is what lets the fire travel outward across the land rather than staying put. The Receding phase burns everything down faster still.");
		NodeRadius = config.Bind<float>("Wildfire", "NodeRadius", 6f, "Base radius around each node that shows fire and can damage the player. The visual front joins neighbouring nodes into a continuous fire line.");
		PlayerFireDamagePerTick = config.Bind<float>("Wildfire", "PlayerFireDamagePerTick", 10f, "Fire damage per tick to a player or creature standing in a wildfire (FireDamageIntervalSeconds apart). 10 per second hurts badly but leaves time to run out of the flames.");
		FireDamageIntervalSeconds = config.Bind<float>("Wildfire", "FireDamageIntervalSeconds", 1f, "Real seconds between player fire-damage ticks.");
		DamageStructures = config.Bind<bool>("Wildfire", "DamageStructures", false, "Reserved safety valve for future structure/tree fire damage. The v0.9.0 implementation leaves structures untouched.");
		DownwindBias = config.Bind<float>("Wildfire", "DownwindBias", 0.2f, "How strongly fire spread prefers the current wind direction. Low so the fire expands outward roughly evenly in all directions (a growing ring) with only a slight lean downwind.");
		EnableWildfireTreefall = config.Bind<bool>("Wildfire", "EnableWildfireTreefall", true, "Allows the wildfire to topple a small fraction of the trees it burns through (host-controlled native tree damage).");
		WildfireTreefallChance = config.Bind<float>("Wildfire", "WildfireTreefallChance", 0.1f, "Chance (0-1) that fire reaching a tree topples it. Default 0.1 = about 1 in 10 trees fall as the fire passes, so most stay standing but a few dramatically crash down.");
		EnableSpotFires = config.Bind<bool>("Wildfire", "EnableSpotFires", true, "Occasionally ignites a new fire near an existing one at a wider distance than normal spread, like embers landing ahead of the front. Still bound by MaxFireNodes.");
		SpotFireChance = config.Bind<float>("Wildfire", "SpotFireChance", 0.7f, "Chance on each spot-fire check to actually ignite one.");
		SpotFireIntervalSeconds = config.Bind<float>("Wildfire", "SpotFireIntervalSeconds", 2.5f, "Real seconds between spot-fire ignition checks during active spread.");
		SpotFireMinDistanceMeters = config.Bind<float>("Wildfire", "SpotFireMinDistanceMeters", 8f, "Minimum distance from an existing fire node that a spot fire can ignite.");
		SpotFireMaxDistanceMeters = config.Bind<float>("Wildfire", "SpotFireMaxDistanceMeters", 40f, "Maximum distance from an existing fire node that a spot fire can ignite.");
		WildfireForcedEnvironment = config.Bind<string>("Wildfire", "WildfireForcedEnvironment", "", "Optional Valheim environment to force during wildfire. Empty leaves natural weather alone.");
		EmberBedEnabled = config.Bind<bool>("Wildfire Visuals", "EmberBedEnabled", true, "Adds a connected alpha-blended ember bed under wildfire nodes.");
		EmberBedRadiusMultiplier = config.Bind<float>("Wildfire Visuals", "EmberBedRadiusMultiplier", 1.9f, "Ember-bed radius as a multiple of SpreadStepMeters.");
		EmberBedOpacity = config.Bind<float>("Wildfire Visuals", "EmberBedOpacity", 0.3f, "Base alpha for the wildfire ground ember glow. Kept subtle because the flat ground quad can clip a hard edge into sloped terrain; the point lights provide most of the ground illumination, which wraps slopes smoothly.");
		FlameStretchLength = config.Bind<float>("Wildfire Visuals", "FlameStretchLength", 4.2f, "Vertical stretch length for flame particles. Higher values make flames look taller/climbing.");
		MaxDetailedFireDistance = config.Bind<float>("Wildfire Visuals", "MaxDetailedFireDistance", 60f, "Distance in metres for full flame and heat-haze particle density before LOD reduces emissions.");
		WildfireVfxMode = config.Bind<WildfireVfxSourceMode>("Wildfire VFX", "WildfireVfxMode", WildfireVfxSourceMode.Auto, "Preferred wildfire visual source. Auto tries an optional AssetBundle, then optional extracted textures, then falls back to the built-in procedural visuals.");
		EnableParticlePackVfx = config.Bind<bool>("Wildfire VFX", "EnableParticlePackVfx", true, "Master switch for optional Unity Particle Pack-based wildfire VFX. When false, wildfires always use the procedural fallback regardless of WildfireVfxMode.");
		FireAssetBundlePath = config.Bind<string>("Wildfire VFX", "FireAssetBundlePath", "", "Optional explicit path to the fire VFX AssetBundle. Empty uses BepInEx/plugins/TheBrokenCycle/Assets/thebrokencycle_firevfx (or thebrokencycle_firevfx.bundle).");
		FireTextureFolderPath = config.Bind<string>("Wildfire VFX", "FireTextureFolderPath", "", "Optional explicit path to a folder of extracted fire PNG textures. Empty uses BepInEx/plugins/TheBrokenCycle/Assets/Fire.");
		FlameIntensityMultiplier = config.Bind<float>("Wildfire VFX", "FlameIntensityMultiplier", 1f, "Scales flame particle size and emission rate for both procedural and Particle Pack flames.");
		SmokeIntensityMultiplier = config.Bind<float>("Wildfire VFX", "SmokeIntensityMultiplier", 1f, "Scales smoke particle emission rate for both procedural and Particle Pack smoke.");
		SparkIntensityMultiplier = config.Bind<float>("Wildfire VFX", "SparkIntensityMultiplier", 1f, "Scales ember/spark particle emission rate. Sparks and ember gusts automatically become more prominent during Strong Winds.");
		MaxActiveFireLights = config.Bind<int>("Wildfire VFX", "MaxActiveFireLights", 10, "Hard cap on simultaneous real-time point lights across all wildfire nodes, nearest the player first. Each light makes everything it touches costlier to draw, so this is one of the biggest performance levers. Higher = broader firelight, more cost.");
		FireVisibleDistance = config.Bind<float>("Wildfire VFX", "FireVisibleDistance", 120f, new ConfigDescription("PERFORMANCE. Fire further than this from you is not drawn at all (no flames, smoke, sparks, light) - it still burns, spreads and does damage, and appears as soon as you come within range. The biggest single saving when a wildfire has spread far across the land. Lower = faster, higher = see distant fire.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 1000f), Array.Empty<object>()));
		FullFireEffects = config.Bind<bool>("Wildfire VFX", "FullFireEffects", false, "PERFORMANCE. Off (default): every fire point has flames and ground glow, about a third also smoke and a third sparks, and the subtle ember gusts and heat haze are skipped - about 1.7 particle systems per point instead of 5. On: every point gets all five layers (much heavier; only for strong PCs).");
		PerformanceLogging = config.Bind<bool>("Wildfire VFX", "PerformanceLogging", false, "Diagnostics: while an event is running, write a [perf] line to LogOutput.log every 3 seconds (FPS, worst frame, milliseconds per frame for each part of the mod, fire point counts). Turn on when reporting lag.");
		EnableHeatShimmer = config.Bind<bool>("Wildfire Atmosphere", "EnableHeatShimmer", true, "Near a wildfire the air shimmers with heat, using Valheim's own Ashlands heat-distortion camera effect. Lava heat in the Ashlands still works as normal (the stronger of the two is used).");
		HeatShimmerStrength = config.Bind<float>("Wildfire Atmosphere", "HeatShimmerStrength", 0.55f, new ConfigDescription("How strong the heat shimmer gets right beside the fire (0-1; Ashlands lava reaches 1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		HeatShimmerDistance = config.Bind<float>("Wildfire Atmosphere", "HeatShimmerDistance", 30f, new ConfigDescription("How far from the nearest flames (metres) the heat shimmer fades out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 200f), Array.Empty<object>()));
		EnableSmokySky = config.Bind<bool>("Wildfire Atmosphere", "EnableSmokySky", true, "Near a wildfire the air fills with smoke: brown-orange haze, thicker fog, and sunlight turning orange. Blends back to normal weather as you get away. Does not change the actual weather (no rain/wetness side effects).");
		SmokySkyStrength = config.Bind<float>("Wildfire Atmosphere", "SmokySkyStrength", 0.7f, new ConfigDescription("How heavy the smoke haze gets at its thickest (0-1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		SmokySkyRadius = config.Bind<float>("Wildfire Atmosphere", "SmokySkyRadius", 250f, new ConfigDescription("Distance from the nearest flames (metres) at which the smoke haze starts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 1000f), Array.Empty<object>()));
		EnableFallingAsh = config.Bind<bool>("Wildfire Atmosphere", "EnableFallingAsh", true, "Grey ash and a few glowing embers drift down around you while you are in the smoke.");
		EnableSmokePlumes = config.Bind<bool>("Wildfire Atmosphere", "EnableSmokePlumes", true, "Tall columns of dark smoke rise from the fire into the sky, visible from far away.");
		MaxSmokePlumes = config.Bind<int>("Wildfire Atmosphere", "MaxSmokePlumes", 5, new ConfigDescription("Most smoke columns at once (they scale with fire size). Each is one cheap particle system.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
		EnableEarthquakes = config.Bind<bool>("Earthquakes", "EnableEarthquakes", true, "Earthquakes: for 4-5 minutes the land tilts (in a different direction each time) while you, creatures, the sea and the sky stay level; the ground shakes and rumbles, you and creatures are pushed downhill, and loose items, fallen logs and carts slide. They happen on their own now and then, and often during the omen of a Flash Surge or Great Flood.");
		EarthquakeMinimumWorldDay = config.Bind<int>("Earthquakes", "EarthquakeMinimumWorldDay", 2, new ConfigDescription("No natural earthquakes before this in-game day.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
		EarthquakeMinimumHours = config.Bind<float>("Earthquakes", "MinimumHoursBetweenEarthquakes", 1f, new ConfigDescription("Shortest gap between natural earthquakes, in real hours of world time (1 hour = 2 in-game days).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 100f), Array.Empty<object>()));
		EarthquakeMaximumHours = config.Bind<float>("Earthquakes", "MaximumHoursBetweenEarthquakes", 2.5f, new ConfigDescription("Longest gap between natural earthquakes, in real hours of world time.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 200f), Array.Empty<object>()));
		EarthquakeMinimumSeconds = config.Bind<float>("Earthquakes", "EarthquakeMinimumSeconds", 240f, new ConfigDescription("Shortest earthquake, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 1800f), Array.Empty<object>()));
		EarthquakeMaximumSeconds = config.Bind<float>("Earthquakes", "EarthquakeMaximumSeconds", 300f, new ConfigDescription("Longest earthquake, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 1800f), Array.Empty<object>()));
		EarthquakeChanceFlashSurge = config.Bind<float>("Earthquakes", "ChanceDuringFlashSurge", 0.65f, new ConfigDescription("Chance that a Flash Surge's omen brings an earthquake.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		EarthquakeChanceGreatFlood = config.Bind<float>("Earthquakes", "ChanceDuringGreatFlood", 0.8f, new ConfigDescription("Chance that a Great Flood's omen brings an earthquake.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		EarthquakeChanceOtherEvents = config.Bind<float>("Earthquakes", "ChanceDuringOtherEvents", 0.25f, new ConfigDescription("Chance that any other event (Storm Tide, drought, wildfire, Strong Winds) begins with an earthquake.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		EarthquakeTilt = config.Bind<bool>("Earthquakes", "EnableTilt", true, "The land visibly tilts and you slide downhill. Turn off for shaking and rumbling only (e.g. if the tilt makes you feel unwell).");
		EarthquakeMaxTiltDegrees = config.Bind<float>("Earthquakes", "MaxTiltDegrees", 12f, new ConfigDescription("How far the land leans for most of an earthquake, in degrees (each quake rolls 70-100% of this, swaying as it goes).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
		EarthquakeHeaveTiltDegrees = config.Bind<float>("Earthquakes", "HeaveTiltDegrees", 34f, new ConfigDescription("2-4 times per earthquake the land heaves much further for several seconds, up to this many degrees. Ground that ends up steeper than 38 degrees (its real slope plus the tilt) makes you slide, so heaves are when you may lose your footing. 0 = no heaves.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 45f), Array.Empty<object>()));
		EarthquakeLevelOcean = config.Bind<bool>("Earthquakes", "KeepOceanLevel", true, "The sea stays level while the land tilts, so water runs up the low side and pulls back from the high side (visual only - the real water level does not change).");
		EarthquakeUprightCreatures = config.Bind<bool>("Earthquakes", "KeepCreaturesUpright", true, "You, other players and creatures stay upright while the land tilts under them.");
		EarthquakeLevelSky = config.Bind<bool>("Earthquakes", "KeepSkyLevel", true, "The sky and horizon stay level while the land tilts.");
		EarthquakeShakeStrength = config.Bind<float>("Earthquakes", "ShakeStrength", 1f, new ConfigDescription("Strength of the camera shake (uses Valheim's own shake; the game's Camera Shake option still switches it off).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
		EarthquakeSteepSliding = config.Bind<bool>("Earthquakes", "SlideOnSteepGround", true, "You and creatures slide only where the tilt makes the ground really steep: its real slope plus the earthquake's tilt past Valheim's own slide angle (38 degrees for players). Gentle tilts never move you.");
		EarthquakeObjectSlide = config.Bind<float>("Earthquakes", "ObjectSlideStrength", 1f, new ConfigDescription("How strongly loose objects (dropped items, fallen logs, carts) feel the tilted gravity and the jolts. 1 = real physics: their own friction decides whether they slide. 0 = they stay put.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
		EarthquakeTreefall = config.Bind<bool>("Earthquakes", "EarthquakeTreefall", true, "Trees come crashing down during earthquakes: one or two at every jolt, a steady toppling in between, and a lot more during heaves. They fall downhill. Trees near your buildings are never felled.");
		EarthquakeTreesPerMinute = config.Bind<float>("Earthquakes", "TreesPerMinute", 6f, new ConfigDescription("Trees toppled per minute between the jolts at the height of a quake (three times as many during a heave).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
		EarthquakeMaxTreefalls = config.Bind<int>("Earthquakes", "MaxTreefallsPerQuake", 30, new ConfigDescription("Most trees one earthquake can bring down.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>()));
		EarthquakeTreefallRadius = config.Bind<float>("Earthquakes", "TreefallRadius", 60f, new ConfigDescription("How far from players (metres) trees can fall. Trees closer than 7 m to a player are spared, so there is always a moment to react.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 120f), Array.Empty<object>()));
		EarthquakeDust = config.Bind<bool>("Earthquakes", "EarthquakeDust", true, "Clouds of dust burst up from the ground around you at every jolt and heave.");
		EarthquakeRumbleVolume = config.Bind<float>("Earthquakes", "RumbleVolume", 0.8f, new ConfigDescription("Volume of the earthquake rumble and cracks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		EarthquakeUseBossSummonSound = config.Bind<bool>("Earthquakes", "UseBossSummonSound", true, "Use the ground-shaking sound Valheim plays when a boss is summoned at its altar as the earthquake rumble.");
		EarthquakeRumbleSound = config.Bind<string>("Earthquakes", "RumbleSound", "sfx_prespawn", "Fallback rumble sound(s), comma-separated game sound names, used when no summoning altar is loaded. The first quake writes a list of suitable game sounds to LogOutput.log ('[quake] game sounds...'). If none is found, slowed-down thunder is used.");
		EarthquakeCrackSounds = config.Bind<string>("Earthquakes", "CrackSounds", "", "Optional game sounds played (pitched down) when the ground jolts, comma-separated (e.g. sfx_rock_destroyed). Empty = the jolts use the earthquake sound file.");
		EarthquakeSoundFile = config.Bind<string>("Earthquakes", "EarthquakeSoundFile", "earthquake.mp3", "The earthquake sound: an MP3 in the mod's Audio folder (beside TheFloods.dll), looped for the rumble and played in bursts at each jolt. If it is missing, the boss-summon sound (UseBossSummonSound) or slowed thunder is used instead.");
		EnableScorchedGround = config.Bind<bool>("Wildfire Atmosphere", "EnableScorchedGround", true, "Ground the fire passes over turns to bare scorched earth with the grass burnt away, then slowly recovers. Visual only and never saved to the world - your paths, farms and terrain edits are untouched, and a game restart clears all burns.");
		ScorchedGroundRecoveryDays = config.Bind<float>("Wildfire Atmosphere", "ScorchedGroundRecoveryDays", 1f, new ConfigDescription("In-game days for burnt ground to fully recover (sleeping skips time, so it recovers overnight).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
		ScorchedGroundStrength = config.Bind<float>("Wildfire Atmosphere", "ScorchedGroundStrength", 0.9f, new ConfigDescription("How completely freshly burnt ground turns to bare earth (0-1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		DebugWildfireVfxLogging = config.Bind<bool>("Wildfire VFX", "DebugWildfireVfxLogging", false, "Writes wildfire VFX asset loading, fallback, and reload diagnostics to BepInEx LogOutput.log.");
		MeadowsFlammability = config.Bind<float>("Wildfire Biomes", "MeadowsFlammability", 1f, "Ignition fuel multiplier for Meadows.");
		BlackForestFlammability = config.Bind<float>("Wildfire Biomes", "BlackForestFlammability", 0.9f, "Ignition fuel multiplier for Black Forest.");
		PlainsFlammability = config.Bind<float>("Wildfire Biomes", "PlainsFlammability", 1f, "Ignition fuel multiplier for Plains.");
		SwampFlammability = config.Bind<float>("Wildfire Biomes", "SwampFlammability", 0.15f, "Ignition fuel multiplier for Swamp.");
		MountainFlammability = config.Bind<float>("Wildfire Biomes", "MountainFlammability", 0.1f, "Ignition fuel multiplier for Mountain.");
		MistlandsFlammability = config.Bind<float>("Wildfire Biomes", "MistlandsFlammability", 0.5f, "Ignition fuel multiplier for Mistlands.");
		AshlandsFlammability = config.Bind<float>("Wildfire Biomes", "AshlandsFlammability", 0f, "Ignition fuel multiplier for Ashlands.");
		DeepNorthFlammability = config.Bind<float>("Wildfire Biomes", "DeepNorthFlammability", 0f, "Ignition fuel multiplier for Deep North.");
		OtherBiomeFlammability = config.Bind<float>("Wildfire Biomes", "OtherBiomeFlammability", 0.3f, "Ignition fuel multiplier for unknown or modded biomes.");
		EnableStrongWinds = config.Bind<bool>("Strong Winds", "Enable Strong Winds", true, "Allows the standalone Strong Winds disaster and Broken Cycle wind escalation.");
		StrongWindsEventChance = config.Bind<float>("Strong Winds", "Event chance", 0.08f, "Chance that a strong-winds schedule window starts the event.");
		StrongWindsCooldownHours = config.Bind<float>("Strong Winds", "Cooldown", 5f, "Minimum active world hours before another natural Strong Winds check.");
		StrongWindsDurationMinutes = config.Bind<float>("Strong Winds", "Duration", 18f, "Approximate real-time duration of a full Strong Winds event.");
		StrongWindsMinimumWorldDay = config.Bind<int>("Strong Winds", "Minimum world progression requirement", 6, "Earliest world day for natural Strong Winds.");
		StrongWindsWindStrengthMultiplier = config.Bind<float>("Strong Winds", "Wind strength multiplier", 1f, "Overall multiplier for strong-wind force and debris.");
		EnableTreefall = config.Bind<bool>("Strong Winds", "Enable treefall", true, "Allows host-controlled native tree damage during severe winds.");
		TreefallChance = config.Bind<float>("Strong Winds", "Treefall chance", 0.35f, "Chance for each eligible severe gust window to topple one nearby valid tree.");
		TreefallDistanceFromPlayer = config.Bind<float>("Strong Winds", "Treefall distance from player", 55f, "Maximum tree candidate scan distance around active players.");
		MaxTreefallsPerPlayer = config.Bind<int>("Strong Winds", "Maximum treefalls per player", 3, "Maximum host-triggered treefalls credited near each player per event.");
		MaxTreefallsGlobally = config.Bind<int>("Strong Winds", "Maximum treefalls globally", 12, "Maximum host-triggered treefalls per Strong Winds or Broken Cycle event.");
		TreefallCooldownPerZoneSeconds = config.Bind<float>("Strong Winds", "Treefall cooldown per zone", 120f, "Cooldown before another tree can be toppled in the same loaded zone.");
		ProtectedBaseRadius = config.Bind<float>("Strong Winds", "Protected base radius", 32f, "Radius around beds, portals, workbenches, wards, and player pieces protected from treefall targeting.");
		FallingTreeDamageToPlayers = config.Bind<float>("Strong Winds", "Falling-tree damage to players", 0.55f, "Reserved multiplier for vanilla fallen-tree player danger. Lower values keep storms survivable.");
		FallingTreeDamageToCreatures = config.Bind<float>("Strong Winds", "Falling-tree damage to creatures", 0.75f, "Reserved multiplier for vanilla fallen-tree creature danger.");
		StrongWindStructureDamage = config.Bind<bool>("Strong Winds", "Structure damage setting", false, "If false, treefall targeting avoids protected player structures where detectable.");
		WindDebrisAmount = config.Bind<float>("Strong Winds", "Debris amount", 1f, "Client-side windblown debris particle density.");
		StrongWindsDebugMode = config.Bind<bool>("Strong Winds", "Debug Mode", false, "Writes Strong Winds diagnostics to the log.");
		StrongWindsDebugIgnoreBaseProtection = config.Bind<bool>("Strong Winds", "Debug ignore base protection", false, "Allows strongwinds testtree to ignore base protection for controlled testing.");
		DebugLogging = config.Bind<bool>("Debug", "DebugLogging", false, "Writes concise event/state diagnostics to BepInEx LogOutput.log.");
		VerboseWaterTileBindingLogs = config.Bind<bool>("Debug", "VerboseWaterTileBindingLogs", false, "Logs every individual terrain-water tile. Leave false for normal play.");
		StartTestEventOnWorldLoad = config.Bind<bool>("Debug", "StartTestEventOnWorldLoad", false, "Starts a custom test flood whenever the host loads a world. Turn off after testing.");
		TestStartPhase = config.Bind<string>("Debug", "TestStartPhase", "Omen", "Custom test phase: Omen, Rising, Peak, or Receding.");
	}

	internal FloodEventProfile GetProfile(FloodEventType type)
	{
		return type switch
		{
			FloodEventType.StormTide => StormTide, 
			FloodEventType.FlashSurge => FlashSurge, 
			FloodEventType.GreatFlood => GreatFlood, 
			FloodEventType.Drought => Drought, 
			FloodEventType.Wildfire => Wildfire, 
			_ => null, 
		};
	}

	internal float GetFlashSurgeDrawdownMinimum()
	{
		return Mathf.Min(FlashSurgeDrawdownMinimumMeters.Value, FlashSurgeDrawdownMaximumMeters.Value);
	}

	internal float GetFlashSurgeDrawdownMaximum()
	{
		return Mathf.Max(FlashSurgeDrawdownMinimumMeters.Value, FlashSurgeDrawdownMaximumMeters.Value);
	}

	private static void RemoveLegacyDailyRollSettings(ConfigFile config)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Expected O, but got Unknown
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		string[] array = new string[4] { "Storm Tide", "Flash Surge", "The Great Flood", "Drought" };
		for (int i = 0; i < array.Length; i++)
		{
			config.Remove(new ConfigDefinition(array[i], "CooldownDays"));
			config.Remove(new ConfigDefinition(array[i], "DailyChance"));
		}
	}
}
internal enum FloodEventType
{
	None,
	Custom,
	StormTide,
	FlashSurge,
	GreatFlood,
	Drought,
	Wildfire,
	StrongWinds
}
internal enum FloodPhase
{
	Dormant,
	Omen,
	Rising,
	Peak,
	Receding
}
internal enum WildfireVfxSourceMode
{
	Auto,
	AssetBundle,
	Textures,
	Procedural
}
[Serializable]
internal sealed class FloodState
{
	internal int FormatVersion = 7;

	internal FloodPhase Phase;

	internal FloodEventType EventType;

	internal double PhaseStartedWorldSeconds;

	internal double PhaseEndsWorldSeconds;

	internal float PeakMeters = 3.5f;

	internal float DrawdownMeters;

	internal long EventNumber;

	internal long EventSeed;

	internal long LastCompletedDay = -99999L;

	internal long LastRollDay = -99999L;

	internal long LastStormTideDay = -99999L;

	internal long LastFlashSurgeDay = -99999L;

	internal long LastGreatFloodDay = -99999L;

	internal long LastDroughtDay = -99999L;

	internal bool ManualEvent;

	internal bool IsSevereDrought;

	internal bool SchedulerInitialized;

	internal double NextOrdinaryEventWorldSeconds;

	internal double NextGreatFloodWorldSeconds;

	internal double GreatFloodForceWorldSeconds;

	internal double GlobalRecoveryEndsWorldSeconds;

	internal long SchedulerCycle;

	internal float DrynessIndex;

	internal float WildfireOriginX;

	internal float WildfireOriginY;

	internal float WildfireOriginZ;

	internal double NextStrikeWorldSeconds;

	internal double LastDrynessUpdateWorldSeconds;

	internal double NextWildfireWorldSeconds;

	internal double WildfireForceWorldSeconds;

	internal double NextStrongWindsWorldSeconds;

	internal double StrongWindsRecoveryEndsWorldSeconds;

	internal long LastStrongWindsDay = -99999L;

	internal double QuakeStartWorldSeconds;

	internal double QuakeEndWorldSeconds;

	internal long QuakeSeed;

	internal double NextQuakeWorldSeconds;

	internal string Serialize()
	{
		string[] array = new string[38];
		array[0] = FormatVersion.ToString(CultureInfo.InvariantCulture);
		int phase = (int)Phase;
		array[1] = phase.ToString(CultureInfo.InvariantCulture);
		array[2] = PhaseStartedWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[3] = PhaseEndsWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[4] = PeakMeters.ToString("R", CultureInfo.InvariantCulture);
		array[5] = DrawdownMeters.ToString("R", CultureInfo.InvariantCulture);
		array[6] = EventNumber.ToString(CultureInfo.InvariantCulture);
		array[7] = EventSeed.ToString(CultureInfo.InvariantCulture);
		array[8] = LastCompletedDay.ToString(CultureInfo.InvariantCulture);
		array[9] = LastRollDay.ToString(CultureInfo.InvariantCulture);
		array[10] = (ManualEvent ? "1" : "0");
		phase = (int)EventType;
		array[11] = phase.ToString(CultureInfo.InvariantCulture);
		array[12] = LastStormTideDay.ToString(CultureInfo.InvariantCulture);
		array[13] = LastFlashSurgeDay.ToString(CultureInfo.InvariantCulture);
		array[14] = LastGreatFloodDay.ToString(CultureInfo.InvariantCulture);
		array[15] = LastDroughtDay.ToString(CultureInfo.InvariantCulture);
		array[16] = (IsSevereDrought ? "1" : "0");
		array[17] = (SchedulerInitialized ? "1" : "0");
		array[18] = NextOrdinaryEventWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[19] = NextGreatFloodWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[20] = GreatFloodForceWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[21] = GlobalRecoveryEndsWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[22] = SchedulerCycle.ToString(CultureInfo.InvariantCulture);
		array[23] = DrynessIndex.ToString("R", CultureInfo.InvariantCulture);
		array[24] = WildfireOriginX.ToString("R", CultureInfo.InvariantCulture);
		array[25] = WildfireOriginY.ToString("R", CultureInfo.InvariantCulture);
		array[26] = WildfireOriginZ.ToString("R", CultureInfo.InvariantCulture);
		array[27] = NextStrikeWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[28] = LastDrynessUpdateWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[29] = NextWildfireWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[30] = WildfireForceWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[31] = NextStrongWindsWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[32] = StrongWindsRecoveryEndsWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[33] = LastStrongWindsDay.ToString(CultureInfo.InvariantCulture);
		array[34] = QuakeStartWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[35] = QuakeEndWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		array[36] = QuakeSeed.ToString(CultureInfo.InvariantCulture);
		array[37] = NextQuakeWorldSeconds.ToString("R", CultureInfo.InvariantCulture);
		return string.Join("|", array);
	}

	internal static bool TryDeserialize(string data, out FloodState state)
	{
		state = null;
		if (string.IsNullOrWhiteSpace(data))
		{
			return false;
		}
		string[] array = data.Split(new char[1] { '|' });
		if (array.Length != 10 && array.Length != 14 && array.Length != 16 && array.Length != 23 && array.Length != 31 && array.Length != 34 && array.Length != 38)
		{
			return false;
		}
		try
		{
			state = new FloodState
			{
				FormatVersion = int.Parse(array[0], CultureInfo.InvariantCulture),
				Phase = (FloodPhase)int.Parse(array[1], CultureInfo.InvariantCulture),
				PhaseStartedWorldSeconds = double.Parse(array[2], CultureInfo.InvariantCulture),
				PhaseEndsWorldSeconds = double.Parse(array[3], CultureInfo.InvariantCulture),
				PeakMeters = float.Parse(array[4], CultureInfo.InvariantCulture)
			};
			if (array.Length == 23 || array.Length == 31 || array.Length == 34 || array.Length == 38)
			{
				state.DrawdownMeters = float.Parse(array[5], CultureInfo.InvariantCulture);
				state.EventNumber = long.Parse(array[6], CultureInfo.InvariantCulture);
				state.EventSeed = long.Parse(array[7], CultureInfo.InvariantCulture);
				state.LastCompletedDay = long.Parse(array[8], CultureInfo.InvariantCulture);
				state.LastRollDay = long.Parse(array[9], CultureInfo.InvariantCulture);
				state.ManualEvent = array[10] == "1";
				state.EventType = (FloodEventType)int.Parse(array[11], CultureInfo.InvariantCulture);
				state.LastStormTideDay = long.Parse(array[12], CultureInfo.InvariantCulture);
				state.LastFlashSurgeDay = long.Parse(array[13], CultureInfo.InvariantCulture);
				state.LastGreatFloodDay = long.Parse(array[14], CultureInfo.InvariantCulture);
				state.LastDroughtDay = long.Parse(array[15], CultureInfo.InvariantCulture);
				state.IsSevereDrought = array[16] == "1";
				state.SchedulerInitialized = array[17] == "1";
				state.NextOrdinaryEventWorldSeconds = double.Parse(array[18], CultureInfo.InvariantCulture);
				state.NextGreatFloodWorldSeconds = double.Parse(array[19], CultureInfo.InvariantCulture);
				state.GreatFloodForceWorldSeconds = double.Parse(array[20], CultureInfo.InvariantCulture);
				state.GlobalRecoveryEndsWorldSeconds = double.Parse(array[21], CultureInfo.InvariantCulture);
				state.SchedulerCycle = long.Parse(array[22], CultureInfo.InvariantCulture);
				if (array.Length == 31 || array.Length == 34 || array.Length == 38)
				{
					state.DrynessIndex = Mathf.Clamp01(float.Parse(array[23], CultureInfo.InvariantCulture));
					state.WildfireOriginX = float.Parse(array[24], CultureInfo.InvariantCulture);
					state.WildfireOriginY = float.Parse(array[25], CultureInfo.InvariantCulture);
					state.WildfireOriginZ = float.Parse(array[26], CultureInfo.InvariantCulture);
					state.NextStrikeWorldSeconds = double.Parse(array[27], CultureInfo.InvariantCulture);
					state.LastDrynessUpdateWorldSeconds = double.Parse(array[28], CultureInfo.InvariantCulture);
					state.NextWildfireWorldSeconds = double.Parse(array[29], CultureInfo.InvariantCulture);
					state.WildfireForceWorldSeconds = double.Parse(array[30], CultureInfo.InvariantCulture);
				}
				if (array.Length >= 34)
				{
					state.NextStrongWindsWorldSeconds = double.Parse(array[31], CultureInfo.InvariantCulture);
					state.StrongWindsRecoveryEndsWorldSeconds = double.Parse(array[32], CultureInfo.InvariantCulture);
					state.LastStrongWindsDay = long.Parse(array[33], CultureInfo.InvariantCulture);
				}
				if (array.Length >= 38)
				{
					state.QuakeStartWorldSeconds = double.Parse(array[34], CultureInfo.InvariantCulture);
					state.QuakeEndWorldSeconds = double.Parse(array[35], CultureInfo.InvariantCulture);
					state.QuakeSeed = long.Parse(array[36], CultureInfo.InvariantCulture);
					state.NextQuakeWorldSeconds = double.Parse(array[37], CultureInfo.InvariantCulture);
				}
				state.FormatVersion = 6;
			}
			else if (array.Length == 16)
			{
				state.DrawdownMeters = float.Parse(array[5], CultureInfo.InvariantCulture);
				state.EventNumber = long.Parse(array[6], CultureInfo.InvariantCulture);
				state.EventSeed = long.Parse(array[7], CultureInfo.InvariantCulture);
				state.LastCompletedDay = long.Parse(array[8], CultureInfo.InvariantCulture);
				state.LastRollDay = long.Parse(array[9], CultureInfo.InvariantCulture);
				state.ManualEvent = array[10] == "1";
				state.EventType = (FloodEventType)int.Parse(array[11], CultureInfo.InvariantCulture);
				state.LastStormTideDay = long.Parse(array[12], CultureInfo.InvariantCulture);
				state.LastFlashSurgeDay = long.Parse(array[13], CultureInfo.InvariantCulture);
				state.LastGreatFloodDay = long.Parse(array[14], CultureInfo.InvariantCulture);
				state.LastDroughtDay = long.Parse(array[15], CultureInfo.InvariantCulture);
				state.FormatVersion = 6;
			}
			else if (array.Length == 14)
			{
				state.EventNumber = long.Parse(array[5], CultureInfo.InvariantCulture);
				state.EventSeed = long.Parse(array[6], CultureInfo.InvariantCulture);
				state.LastCompletedDay = long.Parse(array[7], CultureInfo.InvariantCulture);
				state.LastRollDay = long.Parse(array[8], CultureInfo.InvariantCulture);
				state.ManualEvent = array[9] == "1";
				state.EventType = (FloodEventType)int.Parse(array[10], CultureInfo.InvariantCulture);
				state.LastStormTideDay = long.Parse(array[11], CultureInfo.InvariantCulture);
				state.LastFlashSurgeDay = long.Parse(array[12], CultureInfo.InvariantCulture);
				state.LastGreatFloodDay = long.Parse(array[13], CultureInfo.InvariantCulture);
				state.LastDroughtDay = state.LastCompletedDay;
				state.DrawdownMeters = 0f;
				state.FormatVersion = 6;
			}
			else
			{
				state.EventNumber = long.Parse(array[5], CultureInfo.InvariantCulture);
				state.EventSeed = long.Parse(array[6], CultureInfo.InvariantCulture);
				state.LastCompletedDay = long.Parse(array[7], CultureInfo.InvariantCulture);
				state.LastRollDay = long.Parse(array[8], CultureInfo.InvariantCulture);
				state.ManualEvent = array[9] == "1";
				state.EventType = ((state.Phase != FloodPhase.Dormant) ? FloodEventType.Custom : FloodEventType.None);
				state.LastStormTideDay = state.LastCompletedDay;
				state.LastFlashSurgeDay = state.LastCompletedDay;
				state.LastGreatFloodDay = state.LastCompletedDay;
				state.LastDroughtDay = state.LastCompletedDay;
				state.DrawdownMeters = 0f;
				state.FormatVersion = 6;
			}
			return true;
		}
		catch
		{
			state = null;
			return false;
		}
	}
}
internal sealed class LightningStrikePayload
{
	internal Vector3 Position;

	internal float Damage;

	internal bool IsDirectHit;

	internal long TargetPlayerId;

	internal int Ignite;

	internal long Seed;

	internal string Serialize()
	{
		return string.Join("|", Position.x.ToString("R", CultureInfo.InvariantCulture), Position.y.ToString("R", CultureInfo.InvariantCulture), Position.z.ToString("R", CultureInfo.InvariantCulture), Damage.ToString("R", CultureInfo.InvariantCulture), IsDirectHit ? "1" : "0", TargetPlayerId.ToString(CultureInfo.InvariantCulture), Ignite.ToString(CultureInfo.InvariantCulture), Seed.ToString(CultureInfo.InvariantCulture));
	}

	internal static bool TryDeserialize(string data, out LightningStrikePayload payload)
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		payload = null;
		if (string.IsNullOrWhiteSpace(data))
		{
			return false;
		}
		string[] array = data.Split(new char[1] { '|' });
		if (array.Length != 8)
		{
			return false;
		}
		try
		{
			payload = new LightningStrikePayload
			{
				Position = new Vector3(float.Parse(array[0], CultureInfo.InvariantCulture), float.Parse(array[1], CultureInfo.InvariantCulture), float.Parse(array[2], CultureInfo.InvariantCulture)),
				Damage = float.Parse(array[3], CultureInfo.InvariantCulture),
				IsDirectHit = (array[4] == "1"),
				TargetPlayerId = long.Parse(array[5], CultureInfo.InvariantCulture),
				Ignite = int.Parse(array[6], CultureInfo.InvariantCulture),
				Seed = long.Parse(array[7], CultureInfo.InvariantCulture)
			};
			return true;
		}
		catch
		{
			payload = null;
			return false;
		}
	}
}
internal sealed class FloodStateStore
{
	private readonly ManualLogSource _logger;

	private readonly string _folder;

	internal FloodStateStore(ManualLogSource logger)
	{
		_logger = logger;
		_folder = Path.Combine(Paths.ConfigPath, "marc.thefloods");
	}

	internal FloodState Load(string worldName)
	{
		try
		{
			string path = GetPath(worldName);
			if (!File.Exists(path))
			{
				return new FloodState();
			}
			if (FloodState.TryDeserialize(File.ReadAllText(path), out var state))
			{
				return state;
			}
			_logger.LogWarning((object)"The Floods: state file could not be read, beginning a fresh inactive state.");
		}
		catch (Exception ex)
		{
			_logger.LogError((object)("The Floods: could not load state: " + ex.Message));
		}
		return new FloodState();
	}

	internal void Save(string worldName, FloodState state)
	{
		try
		{
			Directory.CreateDirectory(_folder);
			string path = GetPath(worldName);
			string text = path + ".tmp";
			File.WriteAllText(text, state.Serialize());
			File.Copy(text, path, overwrite: true);
			File.Delete(text);
		}
		catch (Exception ex)
		{
			_logger.LogError((object)("The Floods: could not save state: " + ex.Message));
		}
	}

	private string GetPath(string worldName)
	{
		string text = (string.IsNullOrWhiteSpace(worldName) ? "UnknownWorld" : worldName);
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char oldChar in invalidFileNameChars)
		{
			text = text.Replace(oldChar, '_');
		}
		return Path.Combine(_folder, text + ".floods-state.txt");
	}
}
internal sealed class StormApproachVisual
{
	internal static readonly StormApproachVisual Inactive = new StormApproachVisual();

	internal bool Active;

	internal Vector3 FrontDirection;

	internal float FrontProgress;

	internal float OverheadStrength;

	internal float WallStrength;

	internal float WindStrength;

	internal float SurgeMeters;

	internal long Seed;
}
internal sealed class FloodDirector
{
	internal const string RpcState = "TheFloods_State_01";

	internal const string RpcMessage = "TheFloods_Message_01";

	internal const string RpcStrike = "TheFloods_Strike_01";

	private readonly ManualLogSource _logger;

	private readonly FloodConfig _config;

	private readonly FloodWaterAdapter _water;

	private readonly FloodEnvironmentAdapter _environment;

	private readonly FloodVisuals _visuals;

	private readonly FloodStateStore _store;

	private readonly FloodLightningSystem _lightning;

	private readonly WildfireSystem _wildfires;

	private readonly StrongWindsSystem _strongWinds;

	private readonly StormAudioManager _audio;

	private readonly EarthquakeSystem _quake;

	private bool _quakeAnnounced;

	private int _quakeJoltsSeen;

	private float _nextQuakeTreeTickAt;

	private FloodState _state = new FloodState();

	private StormApproachVisual _stormApproach = StormApproachVisual.Inactive;

	private bool _loaded;

	private string _worldName;

	private float _lastSyncAt;

	private float _lastVerboseAt;

	private bool _autoTestStarted;

	private MethodInfo _currentEnvironmentMethod;

	private FieldInfo _environmentNameField;

	private bool _environmentReflectionSearched;

	private MethodInfo _envManIsWetMethod;

	private bool _envWetReflectionSearched;

	private readonly Dictionary<string, bool> _rainEnvironmentCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

	private readonly PerfProbe _perf = new PerfProbe();

	private long _sequenceEventNumber = -1L;

	private int _sequenceIndex = -1;

	internal FloodState CurrentState => _state;

	internal bool IsWorldReady => _loaded;

	internal bool IsAuthoritative
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}
	}

	internal float CurrentStormStrength
	{
		get
		{
			if (!TryGetWorldSeconds(out var seconds))
			{
				return 0f;
			}
			return GetStormStrength(seconds);
		}
	}

	internal float CurrentWildfireIntensity
	{
		get
		{
			if (!TryGetWorldSeconds(out var seconds))
			{
				return 0f;
			}
			return GetWildfireIntensity(seconds);
		}
	}

	internal StormApproachVisual CurrentStormApproach => _stormApproach ?? StormApproachVisual.Inactive;

	internal FloodDirector(ManualLogSource logger, FloodConfig config, FloodWaterAdapter water, FloodEnvironmentAdapter environment, FloodVisuals visuals)
	{
		_logger = logger;
		_config = config;
		_water = water;
		_environment = environment;
		_visuals = visuals;
		_store = new FloodStateStore(logger);
		_lightning = new FloodLightningSystem(logger, config, visuals);
		_wildfires = new WildfireSystem(logger, config);
		_strongWinds = new StrongWindsSystem(logger, config);
		_audio = new StormAudioManager(logger, config);
		_quake = new EarthquakeSystem(logger, config);
		EarthquakeSystem.Current = _quake;
	}

	internal void LateTick()
	{
		if (_config.Enabled.Value && !((Object)(object)ZNet.instance == (Object)null))
		{
			_wildfires.LateUpdateAtmosphere(CurrentWildfireIntensity);
		}
	}

	internal void Tick()
	{
		if (!_config.Enabled.Value || (Object)(object)ZNet.instance == (Object)null)
		{
			_water.RestoreOriginalWaterLevel();
			_environment.ReleaseForcedEnvironment();
		}
		else
		{
			if (!TryGetWorldSeconds(out var seconds))
			{
				return;
			}
			EnsureLoaded(seconds);
			ApplyLocalEffects(seconds);
			if (IsAuthoritative)
			{
				RunAuthoritativeState(seconds);
				UpdateWildfireSequence(seconds);
				UpdateEarthquakeSchedule(seconds);
				if (Time.unscaledTime - _lastSyncAt > 4f)
				{
					BroadcastState();
				}
				if (_config.DebugLogging.Value && Time.unscaledTime - _lastVerboseAt > 30f)
				{
					_lastVerboseAt = Time.unscaledTime;
					_logger.LogInfo((object)("The Floods: " + DescribeState(seconds)));
				}
			}
		}
	}

	internal void ReceiveState(string payload)
	{
		if (!FloodState.TryDeserialize(payload, out var state))
		{
			_logger.LogWarning((object)"The Floods: ignored malformed state sync.");
		}
		else if (!IsAuthoritative)
		{
			_state = state;
			_loaded = true;
		}
	}

	internal void ReceiveStrike(string payload)
	{
		if (!LightningStrikePayload.TryDeserialize(payload, out var payload2))
		{
			_logger.LogWarning((object)"The Floods: ignored malformed lightning strike sync.");
			return;
		}
		_lightning.RenderStrike(payload2);
		ApplyLocalLightningDamage(payload2);
	}

	internal void Dispose()
	{
		_lightning.Dispose();
		_wildfires.Dispose();
		_strongWinds.Dispose();
		_audio.Dispose();
		_quake.Dispose();
		EarthquakeSystem.Current = null;
	}

	internal string HandleCommand(string[] args)
	{
		//IL_0372: Unknown result type (might be due to invalid IL or missing references)
		//IL_0361: Unknown result type (might be due to invalid IL or missing references)
		//IL_0377: Unknown result type (might be due to invalid IL or missing references)
		//IL_037a: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
		if (!TryGetWorldSeconds(out var seconds))
		{
			return "The Floods: world clock is not ready yet.";
		}
		EnsureLoaded(seconds);
		if (args == null || args.Length == 0 || args[0].Equals("help", StringComparison.OrdinalIgnoreCase))
		{
			return "The Floods: floods status | floods schedule | floods event stormtide|flashsurge|greatflood|drought|wildfire|strongwinds|earthquake [omen|rise|spread|peak|recede|burnout] | floods quake [seconds]|stop|status | floods strike | floods strikeme | floods dryness <0..1> | floods ignite [x z] | floods fireout | floods wildfirevfx status|reload|procedural|assetbundle|textures | floods start omen|rise|peak|recede | floods set <metres> | floods fastforward | floods stop | floods probe | strongwinds start|stop|peak|status|testtree.";
		}
		switch (args[0].ToLowerInvariant())
		{
		case "status":
			return DescribeState(seconds);
		case "schedule":
			return DescribeSchedule(seconds);
		case "event":
		{
			if (args.Length < 2)
			{
				return "Usage: floods event stormtide|flashsurge|greatflood|drought|wildfire|strongwinds|earthquake [omen|rise|spread|peak|recede|burnout]";
			}
			if (args[1].Equals("earthquake", StringComparison.OrdinalIgnoreCase) || args[1].Equals("quake", StringComparison.OrdinalIgnoreCase))
			{
				return HandleQuakeCommand(new string[1] { "quake" }, seconds);
			}
			if (!TryParseEventType(args[1], out var type) || type == FloodEventType.Custom)
			{
				return "The Floods: choose stormtide, flashsurge, greatflood, drought, wildfire, or strongwinds.";
			}
			FloodPhase phase = ((args.Length <= 2) ? FloodPhase.Omen : ParsePhase(args[2]));
			if (type == FloodEventType.StrongWinds)
			{
				BeginPhase(FloodEventType.StrongWinds, phase, seconds, manual: true, 0f, rollProfileHeight: false);
				return "The Floods: Strong Winds started in " + phase.ToString() + ".";
			}
			FloodEventProfile profile = _config.GetProfile(type);
			if (profile == null)
			{
				return "The Floods: profile was not found.";
			}
			if (type == FloodEventType.Wildfire)
			{
				Vector3 val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : FindIgnitionPointNearPlayer(seconds, allowFallback: true));
				List<Vector3> list = BeginWildfireAt(val, phase, seconds, manual: true, "Smoke rises beyond the trees. Fire and flood are now both in the weather.");
				return "The Floods: Wildfire started in " + phase.ToString() + ". " + (list?.Count ?? 0) + " fires from you: " + DescribeFireStarts(val, list) + ". [" + _wildfires.LastSeedReport + "; sea level " + (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f).ToString("0.0", CultureInfo.InvariantCulture) + " m, tide " + GetTideMeters(seconds).ToString("+0.00;-0.00", CultureInfo.InvariantCulture) + " m]";
			}
			float num = StartManualEvent(type, phase, seconds);
			string text = ((type == FloodEventType.Drought) ? ("Rolled drawdown: -" + Mathf.Abs(num).ToString("0.0", CultureInfo.InvariantCulture)) : ("Rolled height: +" + num.ToString("0.0", CultureInfo.InvariantCulture)));
			string text2 = ((type == FloodEventType.Drought) ? ("drawdown range " + profile.GetMinimumHeight().ToString("0.0", CultureInfo.InvariantCulture) + "–" + profile.GetMaximumHeight().ToString("0.0", CultureInfo.InvariantCulture) + "m") : ("range " + profile.GetMinimumHeight().ToString("0.0", CultureInfo.InvariantCulture) + "–" + profile.GetMaximumHeight().ToString("0.0", CultureInfo.InvariantCulture) + "m"));
			return "The Floods: " + profile.DisplayName + " started in " + phase.ToString() + ". " + text + " (" + text2 + ").";
		}
		case "start":
		{
			FloodPhase phase2 = ((args.Length <= 1) ? FloodPhase.Omen : ParsePhase(args[1]));
			StartManualEvent(FloodEventType.Custom, phase2, seconds, _config.MaximumSurgeMeters.Value);
			return "The Floods: custom event started in " + phase2.ToString() + ".";
		}
		case "set":
		{
			if (args.Length < 2)
			{
				return "Usage: floods set <metres>";
			}
			if (!float.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return "The Floods: use a number such as 2.5";
			}
			float num2 = Mathf.Max(1f, _config.DebugMaximumSurgeMeters.Value);
			result2 = Mathf.Clamp(result2, 0f - num2, num2);
			StartManualEvent(FloodEventType.Custom, FloodPhase.Peak, seconds, result2);
			return "The Floods: forced custom water offset set to " + FormatSignedMeters(result2) + ".";
		}
		case "fastforward":
			if (_state.Phase == FloodPhase.Dormant)
			{
				return "The Floods: no active event to advance.";
			}
			AdvancePhase(seconds);
			return "The Floods: advanced to " + _state.Phase.ToString() + ".";
		case "stop":
			StopEvent(seconds, manualStop: true);
			return "The Floods: stopped and runtime water restored.";
		case "probe":
			LogWaterProbe();
			return "The Floods: water/environment probe written to BepInEx LogOutput.log.";
		case "quake":
		case "earthquake":
			return HandleQuakeCommand(args, seconds);
		case "strike":
			return TriggerManualStrike(seconds, direct: false);
		case "strikeme":
			return TriggerManualStrike(seconds, direct: true);
		case "dryness":
		{
			if (args.Length < 2)
			{
				return "Usage: floods dryness <0..1>";
			}
			if (!float.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return "The Floods: use a dryness value such as 0.75";
			}
			_state.DrynessIndex = Mathf.Clamp01(result);
			_state.LastDrynessUpdateWorldSeconds = seconds;
			SaveAndBroadcast();
			return "The Floods: dryness set to " + _state.DrynessIndex.ToString("0.00", CultureInfo.InvariantCulture) + ".";
		}
		case "ignite":
			return TriggerManualIgnition(args, seconds);
		case "fireout":
			ExtinguishWildfire(seconds, manual: true);
			return "The Floods: wildfire nodes extinguished.";
		case "wildfirevfx":
			return _wildfires.HandleVfxCommand((args.Length > 1) ? args[1] : "status");
		default:
			return "The Floods: unknown command. Type floods help";
		}
	}

	internal string HandleStrongWindsCommand(string[] args)
	{
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		if (!TryGetWorldSeconds(out var seconds))
		{
			return "Strong Winds: world clock is not ready yet.";
		}
		EnsureLoaded(seconds);
		if (args == null || args.Length == 0 || args[0].Equals("help", StringComparison.OrdinalIgnoreCase))
		{
			return "Strong Winds: strongwinds start | strongwinds stop | strongwinds peak | strongwinds status | strongwinds testtree";
		}
		switch (args[0].ToLowerInvariant())
		{
		case "start":
			BeginPhase(FloodEventType.StrongWinds, FloodPhase.Omen, seconds, manual: true, 0f, rollProfileHeight: false);
			return "Strong Winds: event started.";
		case "peak":
			BeginPhase(FloodEventType.StrongWinds, FloodPhase.Peak, seconds, manual: true, 0f, rollProfileHeight: false);
			return "Strong Winds: event forced to peak.";
		case "stop":
			if (_state.EventType == FloodEventType.StrongWinds)
			{
				StopEvent(seconds, manualStop: true);
				return "Strong Winds: event stopped.";
			}
			_strongWinds.ResetRuntime();
			return "Strong Winds: no active Strong Winds event; runtime wind state cleared.";
		case "status":
			return "Strong Winds: intensity=" + GetStrongWindIntensity(seconds, GetStormStrength(seconds)).ToString("0.00", CultureInfo.InvariantCulture) + " treefalls=" + _strongWinds.TreefallStatus;
		case "testtree":
			return _strongWinds.TryDebugTreefall(GetLoadedPlayers(), GetEffectiveWindDirection(GetStormFrontDirection()), _config.StrongWindsDebugIgnoreBaseProtection.Value);
		default:
			return "Strong Winds: unknown command. Type strongwinds help";
		}
	}

	private void EnsureLoaded(double now)
	{
		if (_loaded)
		{
			return;
		}
		_worldName = ZNet.instance.GetWorldName();
		if (!string.IsNullOrWhiteSpace(_worldName))
		{
			if (IsAuthoritative)
			{
				_state = _store.Load(_worldName);
				_logger.LogInfo((object)("The Floods: loaded state for world '" + _worldName + "'. " + DescribeState(now)));
			}
			_loaded = true;
		}
	}

	private void RunAuthoritativeState(double now)
	{
		if (!_autoTestStarted && _config.StartTestEventOnWorldLoad.Value)
		{
			_autoTestStarted = true;
			StartManualEvent(FloodEventType.Custom, ParsePhase(_config.TestStartPhase.Value), now, _config.MaximumSurgeMeters.Value);
			return;
		}
		UpdateDryness(now);
		if (_state.Phase != FloodPhase.Dormant && now >= _state.PhaseEndsWorldSeconds)
		{
			AdvancePhase(now);
		}
		RunLightningScheduler(now, GetStormStrength(now));
		if (_state.Phase == FloodPhase.Dormant)
		{
			TryRunScheduledNaturalEvent(now);
		}
	}

	private void TryRunScheduledNaturalEvent(double now)
	{
		EnsureWaterCycleSchedule(now);
		if (GetWorldDay(now) < _config.MinimumWorldDay.Value || now < _state.GlobalRecoveryEndsWorldSeconds)
		{
			return;
		}
		if (CanStartGreatFlood(now))
		{
			BeginPhase(FloodEventType.GreatFlood, FloodPhase.Omen, now, manual: false, 0f, rollProfileHeight: true);
		}
		else
		{
			if (TryRunStrongWindsSchedule(now))
			{
				return;
			}
			if (now < _state.NextOrdinaryEventWorldSeconds)
			{
				TryRunWildfireSchedule(now);
				return;
			}
			FloodEventType floodEventType = SelectOrdinaryEventType();
			if (floodEventType == FloodEventType.None)
			{
				_state.NextOrdinaryEventWorl