Decompiled source of Seamanship v0.5.0

Seamanship.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Seamanship.Core;
using Seamanship.Ships;
using Seamanship.Skill;
using Seamanship.UI;
using Seamanship.Wind;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Seamanship
{
	internal static class Diagnostics
	{
		private static string s_lastEvent = "nothing yet";

		private static float s_lastEventTime;

		private static bool s_patchesReported;

		private static float s_nextSeaState;

		public static void Note(string what)
		{
			s_lastEvent = what;
			s_lastEventTime = Time.realtimeSinceStartup;
		}

		public static void Update()
		{
			LogSeaState();
			float value = ModConfig.LogStutterSeconds.Value;
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (!(value <= 0f) && !(unscaledDeltaTime < value) && ModConfig.Enabled.Value)
			{
				Ship localShip = Ship.GetLocalShip();
				if (!((Object)(object)localShip == (Object)null))
				{
					float num = Time.realtimeSinceStartup - s_lastEventTime;
					string text = ((num <= unscaledDeltaTime + 0.1f) ? "during it" : "not during it");
					Plugin.Log.LogInfo((object)string.Format(CultureInfo.InvariantCulture, "Frame took {0:0.0} s aboard {1}. Wind {2}, hull stress {3:0.00}. Last mod event: {4}, {5:0.0} s ago ({6}).", unscaledDeltaTime, ((Object)localShip).name, WindSystem.Local.Level, ShipState.ReadStress(localShip), s_lastEvent, num, text));
				}
			}
		}

		private static void LogSeaState()
		{
			float value = ModConfig.LogSeaStateSeconds.Value;
			if (!(value <= 0f) && ModConfig.Enabled.Value && !(Time.unscaledTime < s_nextSeaState) && !((Object)(object)EnvMan.instance == (Object)null) && !((Object)(object)Ship.GetLocalShip() == (Object)null))
			{
				s_nextSeaState = Time.unscaledTime + Mathf.Max(5f, value);
				Plugin.Log.LogInfo((object)WindSystem.DescribeSea(EnvMan.instance));
			}
		}

		public static void ReportSharedPatches(Harmony own)
		{
			if (s_patchesReported)
			{
				return;
			}
			s_patchesReported = true;
			foreach (MethodBase patchedMethod in own.GetPatchedMethods())
			{
				Patches patchInfo = Harmony.GetPatchInfo(patchedMethod);
				if (patchInfo != null)
				{
					IEnumerable<string> values = (from patch in patchInfo.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Transpilers).Concat(patchInfo.Finalizers)
						select patch.owner into owner
						where owner != own.Id
						select owner).Distinct();
					string text = string.Join(", ", values);
					if (text.Length > 0)
					{
						Plugin.Log.LogInfo((object)(patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name + " is also patched by: " + text));
					}
				}
			}
		}
	}
	public static class ModConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> DumpEnvironments;

		public static ConfigEntry<float> ExposureOcean;

		public static ConfigEntry<float> ExposureDeepNorth;

		public static ConfigEntry<float> ExposurePlains;

		public static ConfigEntry<float> ExposureAshlands;

		public static ConfigEntry<float> ExposureMountain;

		public static ConfigEntry<float> ExposureMistlands;

		public static ConfigEntry<float> ExposureSheltered;

		public static ConfigEntry<string> StormEnvironments;

		public static ConfigEntry<string> RoughEnvironments;

		public static ConfigEntry<float> LevelHysteresis;

		public static ConfigEntry<bool> ShiftsEnabled;

		public static ConfigEntry<int> WindPeriodSeconds;

		public static ConfigEntry<string> NormalShiftSigma;

		public static ConfigEntry<string> NormalShiftTransition;

		public static ConfigEntry<string> ShiftEventChance;

		public static ConfigEntry<string> ShiftEventTransition;

		public static ConfigEntry<float> ShiftEventMinDeg;

		public static ConfigEntry<float> ShiftEventMaxDeg;

		public static ConfigEntry<bool> FrontShiftsEnabled;

		public static ConfigEntry<bool> GustsEnabled;

		public static ConfigEntry<string> GustChance;

		public static ConfigEntry<float> GustMinStrength;

		public static ConfigEntry<float> GustMaxStrength;

		public static ConfigEntry<bool> GustsAffectSailForce;

		public static ConfigEntry<bool> UpwindEnabled;

		public static ConfigEntry<float> UpwindFloor;

		public static ConfigEntry<float> NoGoAngle;

		public static ConfigEntry<float> NoGoFade;

		public static ConfigEntry<float> CloseHauledBonus;

		public static ConfigEntry<float> CloseHauledBand;

		public static ConfigEntry<bool> SkillEnabled;

		public static ConfigEntry<float> SkillSailForce;

		public static ConfigEntry<float> SkillRudder;

		public static ConfigEntry<float> SkillUpwindFloor;

		public static ConfigEntry<float> SkillNoGoAngle;

		public static ConfigEntry<float> SkillWindage;

		public static ConfigEntry<float> SkillStress;

		public static ConfigEntry<string> XpPerSecond;

		public static ConfigEntry<float> XpCloseHauledPerSecond;

		public static ConfigEntry<float> XpTack;

		public static ConfigEntry<float> XpRideOut;

		public static ConfigEntry<float> XpMinSpeed;

		public static ConfigEntry<float> XpGraceSeconds;

		public static ConfigEntry<float> XpMultiplier;

		public static ConfigEntry<bool> WindageEnabled;

		public static ConfigEntry<float> WindageLeeway;

		public static ConfigEntry<float> WindageBowFallOff;

		public static ConfigEntry<float> WindageHeadWind;

		public static ConfigEntry<float> WindageHeadWindMinSpeed;

		public static ConfigEntry<float> WindageHalfSail;

		public static ConfigEntry<float> WindageBarePoles;

		public static ConfigEntry<bool> StressEnabled;

		public static ConfigEntry<float> StressFreshFullSeconds;

		public static ConfigEntry<float> StressStrongFullSeconds;

		public static ConfigEntry<float> StressGaleFullSeconds;

		public static ConfigEntry<float> StressGaleHalfSeconds;

		public static ConfigEntry<float> StressRecoverSeconds;

		public static ConfigEntry<float> StressWarnAt;

		public static ConfigEntry<float> StressDangerAt;

		public static ConfigEntry<float> StressDamagePercent;

		public static ConfigEntry<float> StressDamageInterval;

		public static ConfigEntry<float> StressMinHullPercent;

		public static ConfigEntry<string> StressWarnSound;

		public static ConfigEntry<string> StressDamageSound;

		public static ConfigEntry<float> HandlingSailForce;

		public static ConfigEntry<float> HandlingPaddleForce;

		public static ConfigEntry<float> HandlingTurnSail;

		public static ConfigEntry<float> HandlingTurnPaddle;

		public static ConfigEntry<float> HandlingRudderSpeed;

		public static ConfigEntry<bool> ShowInstruments;

		public static ConfigEntry<float> CourseArrowOpacity;

		public static ConfigEntry<float> CourseArrowKnots;

		public static ConfigEntry<bool> CameraShake;

		public static ConfigEntry<float> LogStutterSeconds;

		public static ConfigEntry<float> LogSeaStateSeconds;

		private static HashSet<string> s_storm = new HashSet<string>();

		private static HashSet<string> s_rough = new HashSet<string>();

		private static int s_weatherRevision = -1;

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

		private static StressParams s_stressParams;

		private static int s_stressRevision = -1;

		private static float[] s_xpPerSecond;

		private static int s_xpRevision = -1;

		public static int Revision { get; private set; }

		public static void Bind(ConfigFile config)
		{
			//IL_08f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_08ff: Expected O, but got Unknown
			//IL_092d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0937: Expected O, but got Unknown
			//IL_0980: Unknown result type (might be due to invalid IL or missing references)
			//IL_098a: Expected O, but got Unknown
			//IL_09b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c2: Expected O, but got Unknown
			config.SaveOnConfigSet = false;
			Enabled = Synced(config, "1 - General", "Enabled", value: true, "Master switch for the whole mod. Off is exactly vanilla: wind, handling, windage, hull stress, experience and the HUD readouts all stop. The skill stays in the list and keeps its level. The mod must still be installed on the server and every client.");
			DumpEnvironments = config.Bind<bool>("1 - General", "Log environments", false, "Log the game's weather list and wind ranges once at start-up, to check them after a game update.");
			ExposureOcean = Synced(config, "2 - Wind levels", "Exposure Ocean", 1.3f, "Wind multiplier over open ocean.");
			ExposureDeepNorth = Synced(config, "2 - Wind levels", "Exposure Deep North", 1.2f, "");
			ExposurePlains = Synced(config, "2 - Wind levels", "Exposure Plains", 1f, "");
			ExposureAshlands = Synced(config, "2 - Wind levels", "Exposure Ashlands", 1f, "");
			ExposureMountain = Synced(config, "2 - Wind levels", "Exposure Mountain", 0.95f, "");
			ExposureMistlands = Synced(config, "2 - Wind levels", "Exposure Mistlands", 0.9f, "");
			ExposureSheltered = Synced(config, "2 - Wind levels", "Exposure sheltered", 0.85f, "Black Forest, Swamp and Meadows coasts.");
			StormEnvironments = Synced(config, "2 - Wind levels", "Storm weather", "ThunderStorm,SnowStorm,Twilight_SnowStorm", "Weather names that never read below Strong.");
			RoughEnvironments = Synced(config, "2 - Wind levels", "Rough weather", "Rain", "Weather names that never read below Fresh.");
			LevelHysteresis = Synced(config, "2 - Wind levels", "Level hysteresis", 0.03f, "How far the wind must clear a boundary before the level changes.");
			ShiftsEnabled = Synced(config, "3 - Wind shifts", "Enabled", value: true, "Replace vanilla's snap to a new random direction with slow veers and occasional sudden shifts.");
			WindPeriodSeconds = Synced(config, "3 - Wind shifts", "Period seconds", 125, "How often a new veer or back is drawn.");
			NormalShiftSigma = Synced(config, "3 - Wind shifts", "Normal shift degrees", "8,10,12,15,20", "Standard deviation of an ordinary shift, Light to Gale.");
			NormalShiftTransition = Synced(config, "3 - Wind shifts", "Normal shift seconds", "60,60,45,40,30", "Time an ordinary shift takes to ease in, Light to Gale.");
			ShiftEventChance = Synced(config, "3 - Wind shifts", "Sudden shift chance", "0.02,0.03,0.05,0.10,0.20", "Chance per period of a sudden shift, Light to Gale.");
			ShiftEventTransition = Synced(config, "3 - Wind shifts", "Sudden shift seconds", "20,20,15,10,6", "Time a sudden shift takes to arrive, Light to Gale.");
			ShiftEventMinDeg = Synced(config, "3 - Wind shifts", "Sudden shift min degrees", 40f, "");
			ShiftEventMaxDeg = Synced(config, "3 - Wind shifts", "Sudden shift max degrees", 110f, "");
			FrontShiftsEnabled = Synced(config, "3 - Wind shifts", "Weather fronts", value: true, "A change of weather arrives with a wind shift.");
			GustsEnabled = Synced(config, "4 - Gusts", "Enabled", value: true, "Short gusts from Fresh upward.");
			GustChance = Synced(config, "4 - Gusts", "Gust chance", "0,0,0.25,0.40,0.55", "Chance of a gust in each 20 second window, Light to Gale.");
			GustMinStrength = Synced(config, "4 - Gusts", "Min strength", 0.05f, "");
			GustMaxStrength = Synced(config, "4 - Gusts", "Max strength", 0.15f, "");
			GustsAffectSailForce = Synced(config, "4 - Gusts", "Gusts push the boat", value: true, "Add the gust to vanilla wind intensity (never above 1.0). Off keeps gusts to stress, windage and the instruments.");
			UpwindEnabled = Synced(config, "5 - Upwind", "Enabled", value: true, "Keep some drive close to the wind.");
			UpwindFloor = Synced(config, "5 - Upwind", "Floor", 0.15f, "Minimum wind-angle factor outside the no-go zone at skill 0.");
			NoGoAngle = Synced(config, "5 - Upwind", "No-go angle", 40f, "Half-width of the no-go zone at skill 0, degrees off the wind.");
			NoGoFade = Synced(config, "5 - Upwind", "No-go fade", 10f, "Degrees inside the no-go edge over which drive fades to nothing.");
			CloseHauledBonus = Synced(config, "5 - Upwind", "Close-hauled bonus", 0.2f, "Extra drive at the no-go edge. 0.20 is roughly 10% better progress to windward.");
			CloseHauledBand = Synced(config, "5 - Upwind", "Close-hauled band", 30f, "Degrees beyond the no-go edge over which the bonus tapers away.");
			SkillEnabled = Synced(config, "6 - Seamanship skill", "Enabled", value: true, "Skill effects and experience. The skill itself is always registered.");
			SkillSailForce = Synced(config, "6 - Seamanship skill", "Sail force at 100", 0.05f, "Fractional gain in sail force.");
			SkillRudder = Synced(config, "6 - Seamanship skill", "Rudder response at 100", 0.1f, "Fractional gain in steering force and rudder speed.");
			SkillUpwindFloor = Synced(config, "6 - Seamanship skill", "Upwind floor at 100", 0.25f, "");
			SkillNoGoAngle = Synced(config, "6 - Seamanship skill", "No-go angle at 100", 34f, "");
			SkillWindage = Synced(config, "6 - Seamanship skill", "Windage at 100", 0.7f, "Share of windage left at full skill.");
			SkillStress = Synced(config, "6 - Seamanship skill", "Stress build-up at 100", 0.8f, "Share of stress build-up left at full skill.");
			XpPerSecond = Synced(config, "6 - Seamanship skill", "XP per second", "0.05,0.08,0.11,0.14,0.16", "Experience per second at the helm under sail, Light to Gale.");
			XpCloseHauledPerSecond = Synced(config, "6 - Seamanship skill", "XP close-hauled", 0.05f, "Extra per second within 15 degrees of the no-go edge.");
			XpTack = Synced(config, "6 - Seamanship skill", "XP per tack", 2f, "");
			XpRideOut = Synced(config, "6 - Seamanship skill", "XP ride-out", 1.5f, "For holding full sail through a gust or shift without damage.");
			XpMinSpeed = Synced(config, "6 - Seamanship skill", "XP min speed", 1f, "Boat speed below which no experience is earned.");
			XpMultiplier = Synced(config, "6 - Seamanship skill", "XP multiplier", 1f, "Scales every source of Seamanship experience. 2 levels twice as fast.", 0f, 10f);
			XpGraceSeconds = Synced(config, "6 - Seamanship skill", "XP grace seconds", 10f, "No experience this soon after taking the helm or teleporting.");
			WindageEnabled = Synced(config, "7 - Windage", "Enabled", value: true, "Wind pushes the hull to leeward, turns the bow off the wind and brakes her head to wind. Only while someone is at the helm.");
			WindageLeeway = Synced(config, "7 - Windage", "Leeway", 0.15f, "Sideways push as a share of the boat's rowing thrust, at baseline wind.");
			WindageBowFallOff = Synced(config, "7 - Windage", "Bow fall-off", 0.15f, "Turning moment as a share of rowing rudder force, at baseline wind. Turns the hull without pushing it.");
			WindageHeadWind = Synced(config, "7 - Windage", "Head wind", 0.25f, "Braking with the bow in the no-go zone, as a share of the boat's rowing thrust, at baseline wind and full sail. 0 is vanilla.");
			WindageHeadWindMinSpeed = Synced(config, "7 - Windage", "Head wind minimum speed", 0.5f, "Head wind never slows the boat below this (m/s), so she keeps enough way on to finish a tack.");
			WindageHalfSail = Synced(config, "7 - Windage", "Half sail factor", 0.75f, "Share of full-sail windage with the sail half up.");
			WindageBarePoles = Synced(config, "7 - Windage", "Bare poles factor", 0.5f, "Share of full-sail windage with no sail up, paddling or stopped.");
			StressEnabled = Synced(config, "8 - Overpressing", "Enabled", value: true, "Too much sail in strong wind stresses the hull.");
			StressFreshFullSeconds = Synced(config, "8 - Overpressing", "Fresh full sail seconds", 180f, "Time to damage on a close reach at Full sail in Fresh wind.");
			StressStrongFullSeconds = Synced(config, "8 - Overpressing", "Strong full sail seconds", 60f, "");
			StressGaleFullSeconds = Synced(config, "8 - Overpressing", "Gale full sail seconds", 25f, "");
			StressGaleHalfSeconds = Synced(config, "8 - Overpressing", "Gale half sail seconds", 120f, "");
			StressRecoverSeconds = Synced(config, "8 - Overpressing", "Recover seconds", 20f, "Time for full stress to drain once the load is off.");
			StressWarnAt = Synced(config, "8 - Overpressing", "Warn at", 0.5f, "");
			StressDangerAt = Synced(config, "8 - Overpressing", "Danger at", 0.8f, "");
			StressDamagePercent = Synced(config, "8 - Overpressing", "Damage percent", 1.5f, "Hull health lost per tick at full stress, percent of maximum.");
			StressDamageInterval = Synced(config, "8 - Overpressing", "Damage interval", 3f, "Seconds between damage ticks.");
			StressMinHullPercent = Synced(config, "8 - Overpressing", "Min hull percent", 15f, "Overpressing never takes the hull below this.");
			StressWarnSound = Synced(config, "8 - Overpressing", "Warn sound", "sfx_wood_hit", "Effect prefab played as stress builds. Empty for none.");
			StressDamageSound = Synced(config, "8 - Overpressing", "Damage sound", "sfx_wood_break", "Effect prefab played on a damage tick. Empty for none.");
			HandlingSailForce = Synced(config, "10 - Handling", "Sail force", 1f, "Multiplies every boat's sail drive. Speed follows the square root: 1.2 is about 10% faster.", 0.25f, 3f);
			HandlingPaddleForce = Synced(config, "10 - Handling", "Paddle force", 1f, "Multiplies paddling thrust, forward and back. Speed follows the square root.", 0.25f, 3f);
			HandlingTurnSail = Synced(config, "10 - Handling", "Turning under sail", 1f, "Multiplies rudder force while sailing. Turn rate scales directly: 0.8 turns 20% slower.", 0.25f, 3f);
			HandlingTurnPaddle = Synced(config, "10 - Handling", "Turning under paddle", 1f, "Multiplies rudder force while paddling.", 0.25f, 3f);
			HandlingRudderSpeed = Synced(config, "10 - Handling", "Rudder speed", 1f, "Multiplies how fast the rudder swings from side to side.", 0.25f, 3f);
			ShowInstruments = config.Bind<bool>("9 - Instruments", "Show on ship HUD", true, "Add speed, course, wind level, wind shift and hull stress to the vanilla ship HUD.");
			CourseArrowOpacity = config.Bind<float>("9 - Instruments", "Course arrow opacity", 0.55f, new ConfigDescription("How solid the course arrow on the boat icon is drawn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			CourseArrowKnots = config.Bind<float>("9 - Instruments", "Course arrow knots per head", 4f, new ConfigDescription("The course arrow gains an arrowhead for every this many knots, up to five.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			CameraShake = config.Bind<bool>("9 - Instruments", "Camera shake", true, "Shake the camera as hull stress builds.");
			LogStutterSeconds = config.Bind<float>("9 - Instruments", "Log stutters over seconds", 2f, new ConfigDescription("Log any frame longer than this while aboard a ship, with the last thing the mod did. 0 turns it off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			LogSeaStateSeconds = config.Bind<float>("9 - Instruments", "Log sea state every seconds", 0f, new ConfigDescription("While aboard a ship, log the weather, the game's wind strength and the strength the waves are using. 0 turns it off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			config.SettingChanged += delegate
			{
				Revision++;
			};
			config.ConfigReloaded += delegate
			{
				Revision++;
			};
			config.Save();
		}

		private static ConfigEntry<T> Synced<T>(ConfigFile config, string section, string key, T value, string description)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			return config.Bind<T>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { val }));
		}

		private static ConfigEntry<float> Synced(ConfigFile config, string section, string key, float value, string description, float min, float max)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			return config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { val }));
		}

		public static float[] Levels(ConfigEntry<string> entry, float[] fallback)
		{
			float[] array = (float[])fallback.Clone();
			string[] array2 = (entry.Value ?? "").Split(',');
			for (int i = 0; i < array.Length && i < array2.Length; i++)
			{
				if (float.TryParse(array2[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					array[i] = result;
				}
				else
				{
					WarnOnce(entry, "\"" + array2[i].Trim() + "\" is not a number; using " + array[i].ToString(CultureInfo.InvariantCulture));
				}
			}
			if (array2.Length != array.Length)
			{
				WarnOnce(entry, $"expected {array.Length} comma-separated values, Light to Gale, found {array2.Length}; defaults fill the gaps");
			}
			return array;
		}

		private static void WarnOnce(ConfigEntry<string> entry, string problem)
		{
			string item = ((ConfigEntryBase)entry).Definition.Key + "=" + entry.Value;
			if (s_warned.Add(item))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Setting \"" + ((ConfigEntryBase)entry).Definition.Key + "\" (" + entry.Value + "): " + problem + "."));
				}
			}
		}

		public static WeatherClass Classify(string environmentName)
		{
			if (s_weatherRevision != Revision)
			{
				s_storm = ParseNames(StormEnvironments.Value);
				s_rough = ParseNames(RoughEnvironments.Value);
				s_weatherRevision = Revision;
			}
			if (string.IsNullOrEmpty(environmentName))
			{
				return WeatherClass.Fair;
			}
			if (s_storm.Contains(environmentName))
			{
				return WeatherClass.Storm;
			}
			if (s_rough.Contains(environmentName))
			{
				return WeatherClass.Rough;
			}
			return WeatherClass.Fair;
		}

		private static HashSet<string> ParseNames(string list)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = (list ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		public static float Exposure(Biome biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if ((int)biome <= 16)
			{
				if ((int)biome <= 4)
				{
					if (biome - 1 <= 1)
					{
						goto IL_0085;
					}
					if ((int)biome == 4)
					{
						return ExposureMountain.Value;
					}
				}
				else
				{
					if ((int)biome == 8)
					{
						goto IL_0085;
					}
					if ((int)biome == 16)
					{
						return ExposurePlains.Value;
					}
				}
			}
			else if ((int)biome <= 64)
			{
				if ((int)biome == 32)
				{
					return ExposureAshlands.Value;
				}
				if ((int)biome == 64)
				{
					return ExposureDeepNorth.Value;
				}
			}
			else
			{
				if ((int)biome == 256)
				{
					return ExposureOcean.Value;
				}
				if ((int)biome == 512)
				{
					return ExposureMistlands.Value;
				}
			}
			return 1f;
			IL_0085:
			return ExposureSheltered.Value;
		}

		public static WindShiftParams ShiftParams()
		{
			WindShiftParams windShiftParams = new WindShiftParams();
			return new WindShiftParams
			{
				NormalSigmaDeg = Levels(NormalShiftSigma, windShiftParams.NormalSigmaDeg),
				NormalTransitionSec = Levels(NormalShiftTransition, windShiftParams.NormalTransitionSec),
				EventChance = Levels(ShiftEventChance, windShiftParams.EventChance),
				EventTransitionSec = Levels(ShiftEventTransition, windShiftParams.EventTransitionSec),
				EventMinDeg = ShiftEventMinDeg.Value,
				EventMaxDeg = ShiftEventMaxDeg.Value,
				PeriodSeconds = Math.Max(10, WindPeriodSeconds.Value)
			};
		}

		public static GustParams GustParams()
		{
			GustParams gustParams = new GustParams();
			return new GustParams
			{
				ChancePerSlot = Levels(GustChance, gustParams.ChancePerSlot),
				MinStrength = GustMinStrength.Value,
				MaxStrength = GustMaxStrength.Value
			};
		}

		public static StressParams StressParams()
		{
			if (s_stressParams != null && s_stressRevision == Revision)
			{
				return s_stressParams;
			}
			s_stressRevision = Revision;
			StressParams result = new StressParams
			{
				FreshFullSeconds = StressFreshFullSeconds.Value,
				StrongFullSeconds = StressStrongFullSeconds.Value,
				GaleFullSeconds = StressGaleFullSeconds.Value,
				GaleHalfSeconds = StressGaleHalfSeconds.Value,
				RecoverSeconds = StressRecoverSeconds.Value
			};
			s_stressParams = result;
			return result;
		}

		public static float[] XpPerSecondByLevel()
		{
			if (s_xpPerSecond != null && s_xpRevision == Revision)
			{
				return s_xpPerSecond;
			}
			s_xpRevision = Revision;
			return s_xpPerSecond = Levels(XpPerSecond, new float[5] { 0.05f, 0.08f, 0.11f, 0.14f, 0.16f });
		}
	}
	[BepInPlugin("com.stuco.seamanship", "Seamanship", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.stuco.seamanship";

		public const string Name = "Seamanship";

		public const string Version = "0.5.0";

		internal static ManualLogSource Log;

		private const float ConfigDebounce = 1f;

		private FileSystemWatcher m_configWatcher;

		private volatile bool m_configTouched;

		private float m_reloadAt = -1f;

		private int m_reloadTries;

		private float m_saveAt = -1f;

		private float m_ownWriteUntil = -1f;

		internal static Harmony Harmony { get; private set; }

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				m_saveAt = Time.unscaledTime + 1f;
			};
			SeamanshipSkill.Register();
			Harmony = new Harmony("com.stuco.seamanship");
			Harmony.PatchAll(typeof(Plugin).Assembly);
			((Component)this).gameObject.AddComponent<StressFeedback>();
			WatchConfigFile();
		}

		private void WatchConfigFile()
		{
			try
			{
				m_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime)
				};
				m_configWatcher.Changed += delegate
				{
					m_configTouched = true;
				};
				m_configWatcher.Created += delegate
				{
					m_configTouched = true;
				};
				m_configWatcher.Renamed += delegate
				{
					m_configTouched = true;
				};
				m_configWatcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Not watching the config file for edits: " + ex.Message));
			}
		}

		private void Update()
		{
			HelmTracker.Update(Time.deltaTime);
			SaveConfigIfChanged();
			ReloadConfigIfEdited();
			Diagnostics.Update();
		}

		private void SaveConfigIfChanged()
		{
			if (!(m_saveAt < 0f) && !(Time.unscaledTime < m_saveAt))
			{
				m_saveAt = -1f;
				SaveConfig();
			}
		}

		private void SaveConfig()
		{
			try
			{
				m_ownWriteUntil = Time.unscaledTime + 2f;
				((BaseUnityPlugin)this).Config.Save();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not save the config file: " + ex.Message));
			}
		}

		private void ReloadConfigIfEdited()
		{
			if (m_configTouched)
			{
				m_configTouched = false;
				if (Time.unscaledTime >= m_ownWriteUntil)
				{
					m_reloadAt = Time.unscaledTime + 1f;
					m_reloadTries = 0;
				}
			}
			if (m_reloadAt < 0f || Time.unscaledTime < m_reloadAt)
			{
				return;
			}
			m_reloadAt = -1f;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Diagnostics.Note("reloaded the config file");
				Log.LogInfo((object)"Config file changed on disk; reloaded.");
			}
			catch (Exception ex)
			{
				if (++m_reloadTries < 5)
				{
					m_reloadAt = Time.unscaledTime + 1f;
				}
				else
				{
					Log.LogWarning((object)("Could not reload the config file: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
			if (m_saveAt >= 0f)
			{
				SaveConfig();
			}
			m_configWatcher?.Dispose();
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace Seamanship.Wind
{
	public sealed class WindProbe
	{
		private float m_exposure = 1f;

		private float m_nextBiomeCheck;

		public float W { get; private set; }

		public float WGust { get; private set; }

		public WindLevel Level { get; private set; } = WindLevel.Moderate;

		public GustState Gust { get; private set; }

		public void Update(Vector3 position)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time >= m_nextBiomeCheck)
			{
				m_nextBiomeCheck = Time.time + 2f;
				m_exposure = ((WorldGenerator.instance != null) ? ModConfig.Exposure(WorldGenerator.instance.GetBiome(position)) : 1f);
			}
			WeatherClass weather = WindSystem.Weather;
			W = WindLevels.EffectiveWind(WindSystem.BaseIntensity, m_exposure, weather);
			Level = WindLevels.Resolve(W, Level, weather, ModConfig.LevelHysteresis.Value);
			Gust = (ModConfig.GustsEnabled.Value ? Gusts.Evaluate(WindSystem.Now, WindSystem.Seed, Level, WindSystem.GustParams) : default(GustState));
			WGust = Mathf.Min(1f, W + Gust.Strength);
		}
	}
	public static class WindSystem
	{
		private const float MaxSwingDegPerSec = 45f;

		private const float WaveRetargetDeg = 2f;

		private const float WaveRetargetIntensity = 0.02f;

		private static WindDirectionModel s_model;

		private static int s_modelSeed;

		private static int s_modelRevision = -1;

		private static bool s_everOwned;

		private static bool s_owned;

		private static float s_angle;

		private static float s_intensity;

		private static Vector4 s_wind;

		private static bool s_publish;

		private static string s_standDown;

		private static float s_gameIntensity;

		private static string s_envName;

		private static float s_frontFrom;

		private static float s_frontTo;

		private static double s_frontStart;

		private static float s_frontDuration = 1f;

		public static readonly WindProbe Local = new WindProbe();

		public static double Now { get; private set; }

		public static int Seed { get; private set; }

		public static GustParams GustParams { get; private set; } = new GustParams();

		public static float BaseIntensity { get; private set; } = 0.3f;

		public static WeatherClass Weather { get; private set; }

		public static string EnvironmentName => s_envName ?? "";

		public static bool Owned => s_owned;

		public static WindShiftState Shift { get; private set; }

		public static bool FrontArriving { get; private set; }

		public static float FrontStepDeg => Angles.Delta(s_frontTo - s_frontFrom);

		public static bool ShiftEventActive
		{
			get
			{
				if (s_owned)
				{
					if (!Shift.IsEvent || !Shift.Transitioning)
					{
						if (FrontArriving)
						{
							return Mathf.Abs(FrontStepDeg) >= 30f;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		public static float CurrentStepDeg
		{
			get
			{
				if (!s_owned)
				{
					return 0f;
				}
				if (FrontArriving)
				{
					return FrontStepDeg;
				}
				if (!Shift.Transitioning)
				{
					return 0f;
				}
				return Shift.StepDeg;
			}
		}

		public static void Reset()
		{
			s_model = null;
			s_everOwned = false;
			s_owned = false;
			s_publish = false;
			s_standDown = null;
			s_envName = null;
			s_frontFrom = (s_frontTo = 0f);
			FrontArriving = false;
		}

		public static bool Apply(EnvMan env, float targetIntensity)
		{
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			float fixedDeltaTime = Time.fixedDeltaTime;
			s_publish = false;
			Now = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0);
			Seed = ((WorldGenerator.instance != null) ? WorldGenerator.instance.GetSeed() : 0);
			if (!ModConfig.Enabled.Value || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()))
			{
				s_owned = false;
				return true;
			}
			RefreshModel();
			UpdateWeather(env);
			string text = StandDown(env);
			if (text != s_standDown)
			{
				s_standDown = text;
				Plugin.Log.LogInfo((object)((text != null) ? ("Wind handed back to the game: " + text + ".") : "Wind shifts resumed."));
			}
			if (!ModConfig.ShiftsEnabled.Value || text != null)
			{
				s_owned = false;
				BaseIntensity = (s_intensity = env.GetWindIntensity());
				UpdateLocalProbe();
				return true;
			}
			targetIntensity = Mathf.Clamp(targetIntensity, 0.05f, 1f);
			s_gameIntensity = targetIntensity;
			if (!s_owned && !s_everOwned)
			{
				s_intensity = targetIntensity;
			}
			s_intensity = Mathf.MoveTowards(s_intensity, targetIntensity, fixedDeltaTime * 0.2f);
			BaseIntensity = s_intensity;
			UpdateLocalProbe();
			Shift = s_model.Evaluate(Now);
			float num = Shift.AngleDeg + FrontOffset();
			bool snap = !s_everOwned;
			if (!s_owned)
			{
				s_angle = (s_everOwned ? Yaw(env.GetWindDir()) : num);
				s_owned = true;
				s_everOwned = true;
			}
			s_angle = Mathf.MoveTowardsAngle(s_angle, num, 45f * fixedDeltaTime);
			float num2 = s_intensity;
			if (ModConfig.GustsAffectSailForce.Value)
			{
				num2 = Mathf.Min(1f, num2 + Local.Gust.Strength);
			}
			s_wind = Direction(s_angle, num2);
			s_publish = true;
			float angleDeg = Shift.AngleDeg + Shift.StepDeg * (1f - Angles.SmoothStep01(Shift.Progress)) + s_frontTo;
			LayOutSwell(env, Direction(angleDeg, targetIntensity), snap);
			return false;
		}

		public static void PublishWind(EnvMan env)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (s_publish)
			{
				s_publish = false;
				env.m_wind = s_wind;
				if ((Object)(object)env.m_clothWindZone != (Object)null)
				{
					env.m_clothWindZone.SetWindDirection(env.GetWindDir(), false);
					env.m_clothWindZone.main = Mathf.Pow(env.GetWindIntensity(), 2f) * 100f;
				}
				Shader.SetGlobalVector(EnvMan.s_globalWindForce, Vector4.op_Implicit(env.GetWindForce()));
			}
		}

		private static void LayOutSwell(EnvMan env, Vector4 target, bool snap)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (snap)
			{
				env.m_windDir1 = target;
				env.m_windDir2 = target;
				env.m_windTransitionTimer = -1f;
			}
			else if (!(env.m_windTransitionTimer >= 0f))
			{
				Vector4 windDir = env.m_windDir1;
				if (!(Vector3.Angle(new Vector3(windDir.x, 0f, windDir.z), new Vector3(target.x, 0f, target.z)) < 2f) || !(Mathf.Abs(windDir.w - target.w) < 0.02f))
				{
					env.m_windDir2 = target;
					env.m_windTransitionTimer = 0f;
					Diagnostics.Note("started a swell cross-fade");
				}
			}
		}

		public static string DescribeSea(EnvMan env)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Vector4 val = default(Vector4);
			Vector4 val2 = default(Vector4);
			float num = default(float);
			env.GetWindData(ref val, ref val2, ref num);
			string text = ((num > 0f) ? string.Format(CultureInfo.InvariantCulture, "{0:0.00} fading to {1:0.00} ({2:0}%)", val.w, val2.w, num * 100f) : val.w.ToString("0.00", CultureInfo.InvariantCulture));
			return string.Format(CultureInfo.InvariantCulture, "Sea state: weather {0}, game wind {1:0.00}, swell {2}, sailing wind {3:0.00}{4}, level {5}, wind driven by {6}.", EnvironmentName, s_owned ? s_gameIntensity : env.GetWindIntensity(), text, env.GetWindIntensity(), Local.Gust.Active ? " (gusting)" : "", Local.Level, s_owned ? "the mod" : "the game");
		}

		private static Vector4 Direction(float angleDeg, float intensity)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			float num = angleDeg * (MathF.PI / 180f);
			return new Vector4(Mathf.Sin(num), 0f, Mathf.Cos(num), intensity);
		}

		private static void UpdateLocalProbe()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Ship localShip = Ship.GetLocalShip();
			if ((Object)(object)localShip != (Object)null)
			{
				Local.Update(((Component)localShip).transform.position);
			}
			else if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Local.Update(((Component)Player.m_localPlayer).transform.position);
			}
		}

		private static string StandDown(EnvMan env)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (env.m_debugWind)
			{
				return "debug wind is set";
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).InInterior())
			{
				return null;
			}
			if (Utils.LengthXZ(((Component)localPlayer).transform.position) > 10500f - env.m_edgeOfWorldWidth)
			{
				return "edge of the world";
			}
			Ship localShip = Ship.GetLocalShip();
			if (!((Object)(object)localShip != (Object)null) || !localShip.IsWindControllActive())
			{
				return null;
			}
			return "Moder's power is active";
		}

		private static void RefreshModel()
		{
			if (s_model == null || s_modelSeed != Seed || s_modelRevision != ModConfig.Revision)
			{
				s_model = new WindDirectionModel(Seed, ModConfig.ShiftParams(), GlobalLevelAtPeriod);
				GustParams = ModConfig.GustParams();
				s_modelSeed = Seed;
				s_modelRevision = ModConfig.Revision;
				if (s_envName != null)
				{
					s_frontFrom = (s_frontTo = FrontTarget(s_envName));
					FrontArriving = false;
				}
			}
		}

		private static int GlobalLevelAtPeriod(long periodIndex)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 2;
			}
			long num = periodIndex * Math.Max(1, (s_model != null) ? s_model.Params.PeriodSeconds : ModConfig.WindPeriodSeconds.Value);
			State state = Random.state;
			float num2 = 0.5f;
			for (int num3 = 1; num3 <= 8; num3 *= 2)
			{
				long num4 = Math.Max(1L, instance.m_windPeriodDuration / num3);
				Random.InitState((int)(num / num4));
				_ = Random.value;
				num2 += -0.5f / (float)num3 + Random.value / (float)num3;
			}
			Random.state = state;
			return (int)WindLevels.Bucket(Mathf.Clamp01(num2));
		}

		private static void UpdateWeather(EnvMan env)
		{
			EnvSetup currentEnvironment = env.GetCurrentEnvironment();
			string text = ((currentEnvironment != null) ? currentEnvironment.m_name : "");
			Weather = ModConfig.Classify(text);
			if (!(text == s_envName))
			{
				float num = FrontTarget(text);
				if (s_envName == null)
				{
					s_frontFrom = (s_frontTo = num);
					FrontArriving = false;
				}
				else
				{
					s_frontFrom = FrontOffset();
					s_frontTo = num;
					s_frontStart = Now;
					int num2 = (int)((Weather == WeatherClass.Storm) ? WindLevel.Strong : (Local.Level - 1));
					s_frontDuration = Mathf.Max(1f, s_model.Params.EventTransitionSec[Mathf.Clamp(num2, 0, 4)]);
					FrontArriving = true;
				}
				s_envName = text;
			}
		}

		private static float FrontTarget(string environmentName)
		{
			if (!ModConfig.FrontShiftsEnabled.Value || s_model == null)
			{
				return 0f;
			}
			return s_model.FrontOffsetDeg(environmentName);
		}

		private static float FrontOffset()
		{
			if (!FrontArriving)
			{
				return s_frontTo;
			}
			float num = (float)((Now - s_frontStart) / (double)s_frontDuration);
			if (num >= 1f || num < 0f)
			{
				FrontArriving = false;
				return s_frontTo;
			}
			return s_frontFrom + Angles.Delta(s_frontTo - s_frontFrom) * Angles.SmoothStep01(num);
		}

		private static float Yaw(Vector3 dir)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Atan2(dir.x, dir.z) * 57.29578f;
		}

		public static float SignedWindAngle(Transform hull)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)EnvMan.instance == (Object)null)
			{
				return 0f;
			}
			Vector3 val = hull.InverseTransformDirection(-EnvMan.instance.GetWindDir());
			return Mathf.Atan2(val.x, val.z) * 57.29578f;
		}
	}
	[HarmonyPatch(typeof(EnvMan))]
	internal static class EnvManPatches
	{
		[HarmonyPatch("SetTargetWind")]
		[HarmonyPrefix]
		private static bool SetTargetWind(EnvMan __instance, float intensity)
		{
			return WindSystem.Apply(__instance, intensity);
		}

		[HarmonyPatch("UpdateWindTransition")]
		[HarmonyPostfix]
		private static void UpdateWindTransition(EnvMan __instance)
		{
			WindSystem.PublishWind(__instance);
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake(EnvMan __instance)
		{
			WindSystem.Reset();
			Diagnostics.ReportSharedPatches(Plugin.Harmony);
			if (!ModConfig.DumpEnvironments.Value)
			{
				return;
			}
			Plugin.Log.LogInfo((object)$"Vanilla wind period {__instance.m_windPeriodDuration}s, transition {__instance.m_windTransitionDuration}s");
			foreach (EnvSetup environment in __instance.m_environments)
			{
				Plugin.Log.LogInfo((object)$"Environment {environment.m_name}: wind {environment.m_windMin:0.00}-{environment.m_windMax:0.00} ({ModConfig.Classify(environment.m_name)})");
			}
		}
	}
}
namespace Seamanship.UI
{
	[HarmonyPatch(typeof(Hud), "UpdateShipHud")]
	internal static class ShipHudOverlay
	{
		private const float MetresPerSecondToKnots = 1.94f;

		private static readonly Color[] s_levelColors = (Color[])(object)new Color[5]
		{
			new Color(0.62f, 0.83f, 1f),
			new Color(0.72f, 0.91f, 0.53f),
			new Color(1f, 0.89f, 0.48f),
			new Color(1f, 0.66f, 0.3f),
			new Color(1f, 0.36f, 0.36f)
		};

		private static readonly Color s_faster = new Color(0.45f, 0.9f, 0.4f);

		private static readonly Color s_slower = new Color(1f, 0.36f, 0.36f);

		private static readonly Color s_stressed = new Color(1f, 0.22f, 0.18f);

		private static readonly Color s_text = new Color(0.96f, 0.92f, 0.82f);

		private static readonly Color s_plate = new Color(0.07f, 0.09f, 0.13f, 0.78f);

		private static readonly Color32 s_arrowFill = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);

		private static readonly Color32 s_arrowEdge = new Color32((byte)38, (byte)66, (byte)104, byte.MaxValue);

		private const int MaxHeads = 5;

		private const float ArrowTip = 52f;

		private const float ArrowBase = 2f;

		private const float HeadHeight = 10f;

		private const float HeadWidth = 17f;

		private const float ShaftWidth = 7f;

		private const float ArrowEdge = 1.5f;

		private const float PlateHeight = 19f;

		private const float PlatePadding = 7f;

		private const float TrendSize = 8f;

		private const float TrendGap = 3f;

		private static RectTransform s_root;

		private static RectTransform s_windIcon;

		private static Vector2 s_windIconLocal;

		private static bool s_overlay;

		private static Image s_boat;

		private static Color s_boatColor;

		private static RectTransform s_coursePivot;

		private static Image s_courseShaft;

		private static readonly Image[] s_courseHeads = (Image[])(object)new Image[5];

		private static RectTransform s_speedPlate;

		private static TMP_Text s_speed;

		private static Image s_trend;

		private static RectTransform s_windPlate;

		private static TMP_Text s_windName;

		private static Image s_shift;

		private static Sprite s_triangle;

		private static Sprite s_pill;

		private static Sprite s_arrowHead;

		private static Sprite s_arrowShaft;

		private static int s_shownSpeed = -1;

		private static int s_shownWind = -1;

		private static float s_lastSpeed;

		private static float s_trendValue;

		private static Ship s_lastShip;

		[HarmonyPostfix]
		private static void Postfix(Hud __instance, Player player, float dt)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			Ship val = (((Object)(object)player != (Object)null) ? player.GetControlledShip() : null);
			if ((Object)(object)val == (Object)null || !__instance.m_shipHudRoot.activeSelf || !Ensure(__instance))
			{
				return;
			}
			bool flag = ModConfig.Enabled.Value && ModConfig.ShowInstruments.Value;
			((Component)s_coursePivot).gameObject.SetActive(flag);
			((Component)s_speedPlate).gameObject.SetActive(flag);
			((Component)s_windPlate).gameObject.SetActive(flag);
			((Component)s_shift).gameObject.SetActive(flag);
			if (!flag)
			{
				if ((Object)(object)s_boat != (Object)null)
				{
					((Graphic)s_boat).color = s_boatColor;
				}
				return;
			}
			float x = ((Transform)s_root).lossyScale.x;
			bool blink = Mathf.Repeat(Time.unscaledTime * 3f, 1f) < 0.6f;
			Vector3 val2 = (((Object)(object)val.m_body != (Object)null) ? val.m_body.linearVelocity : Vector3.zero);
			float speed = Utils.LengthXZ(val2);
			UpdateSpeed(val, speed, x, dt);
			UpdateCourse(val, val2, speed);
			UpdateWind(x, blink);
			UpdateHull(val, blink);
		}

		private static void UpdateSpeed(Ship ship, float speed, float scale, float dt)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			((Transform)s_speedPlate).rotation = Quaternion.identity;
			((Transform)s_speedPlate).position = ((Transform)s_root).position + Vector3.down * (40f * scale);
			int num = Mathf.RoundToInt(speed * 1.94f * 10f);
			if (num != s_shownSpeed)
			{
				s_shownSpeed = num;
				s_speed.text = ((float)num / 10f).ToString("0.0", CultureInfo.InvariantCulture) + " kt";
				float preferredWidth = s_speed.preferredWidth;
				float num2 = preferredWidth + 3f + 8f;
				s_speedPlate.sizeDelta = new Vector2(num2 + 14f, 19f);
				s_speed.rectTransform.anchoredPosition = new Vector2((preferredWidth - num2) * 0.5f, 0f);
				((Graphic)s_trend).rectTransform.anchoredPosition = new Vector2((num2 - 8f) * 0.5f, 0f);
			}
			if ((Object)(object)ship != (Object)(object)s_lastShip)
			{
				s_lastShip = ship;
				s_lastSpeed = speed;
				s_trendValue = 0f;
			}
			if (dt > 0f)
			{
				s_trendValue = Mathf.Lerp(s_trendValue, (speed - s_lastSpeed) / dt, Mathf.Clamp01(dt * 2f));
				s_lastSpeed = speed;
			}
			bool flag = Mathf.Abs(s_trendValue) > 0.04f;
			((Component)s_trend).gameObject.SetActive(flag);
			if (flag)
			{
				bool flag2 = s_trendValue > 0f;
				((Graphic)s_trend).color = (flag2 ? s_faster : s_slower);
				((Transform)((Graphic)s_trend).rectTransform).localRotation = Quaternion.Euler(0f, 0f, flag2 ? 0f : 180f);
			}
		}

		private static void UpdateCourse(Ship ship, Vector3 velocity, float speed)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			bool flag = speed > 0.5f;
			((Component)s_courseShaft).gameObject.SetActive(flag);
			if (!flag)
			{
				for (int i = 0; i < 5; i++)
				{
					((Component)s_courseHeads[i]).gameObject.SetActive(false);
				}
				return;
			}
			float num = Utils.YawFromDirection(((Component)ship).transform.InverseTransformDirection(velocity));
			((Transform)s_coursePivot).localRotation = Quaternion.Euler(0f, 0f, 0f - num);
			float num2 = Mathf.Max(0.5f, ModConfig.CourseArrowKnots.Value);
			int num3 = Mathf.Clamp(1 + Mathf.FloorToInt(speed * 1.94f / num2), 1, 5);
			Color color = default(Color);
			((Color)(ref color))..ctor(1f, 1f, 1f, Mathf.Clamp01(ModConfig.CourseArrowOpacity.Value));
			for (int j = 0; j < 5; j++)
			{
				((Component)s_courseHeads[j]).gameObject.SetActive(j < num3);
				((Graphic)s_courseHeads[j]).color = color;
			}
			float num4 = 52f - 10f * (float)num3 - 2f;
			((Component)s_courseShaft).gameObject.SetActive(num4 > 0.5f);
			((Graphic)s_courseShaft).color = color;
			((Graphic)s_courseShaft).rectTransform.sizeDelta = new Vector2(7f, Mathf.Max(0f, num4));
			((Graphic)s_courseShaft).rectTransform.anchoredPosition = new Vector2(0f, 2f + num4 * 0.5f);
		}

		private static void UpdateWind(float scale, bool blink)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			WindProbe local = WindSystem.Local;
			int level = (int)local.Level;
			if (level != s_shownWind)
			{
				s_shownWind = level;
				s_windName.text = WindLevels.Name(local.Level);
				s_windPlate.sizeDelta = new Vector2(s_windName.preferredWidth + 14f, 19f);
			}
			((Graphic)s_windName).color = ((local.Gust.Active && blink) ? Color.white : s_levelColors[level - 1]);
			Vector2 val = Vector2.op_Implicit(((Transform)s_windIcon).position - ((Transform)s_root).position);
			Vector2 normalized = ((Vector2)(ref val)).normalized;
			float num = 26f + Mathf.Abs(normalized.x) * s_windPlate.sizeDelta.x * 0.5f;
			Vector3 val2 = ((Transform)s_windIcon).position + Vector2.op_Implicit(normalized * (num * scale));
			if (s_overlay)
			{
				float num2 = s_windPlate.sizeDelta.x * 0.5f * scale;
				float num3 = 4f * scale;
				if (val2.x + num2 > (float)Screen.width - num3 || val2.x - num2 < num3)
				{
					val2 = ((Transform)s_windIcon).position + Vector3.down * (30f * scale);
				}
				val2.x = Mathf.Clamp(val2.x, num2 + num3, (float)Screen.width - num2 - num3);
			}
			((Transform)s_windPlate).rotation = Quaternion.identity;
			((Transform)s_windPlate).position = val2;
			float currentStepDeg = WindSystem.CurrentStepDeg;
			bool flag = Mathf.Abs(currentStepDeg) >= 3f;
			bool shiftEventActive = WindSystem.ShiftEventActive;
			((Component)s_shift).gameObject.SetActive(flag && (!shiftEventActive || blink));
			if (flag)
			{
				Vector2 normalized2 = ((Vector2)(ref s_windIconLocal)).normalized;
				Vector2 val3 = new Vector2(normalized2.y, 0f - normalized2.x) * ((currentStepDeg > 0f) ? 1f : (-1f));
				RectTransform rectTransform = ((Graphic)s_shift).rectTransform;
				rectTransform.anchoredPosition = s_windIconLocal + val3 * 30f;
				((Transform)rectTransform).localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(val3.y, val3.x) * 57.29578f - 90f);
				((Graphic)s_shift).color = (shiftEventActive ? s_levelColors[4] : s_text);
			}
		}

		private static void UpdateHull(Ship ship, bool blink)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_boat == (Object)null)
			{
				return;
			}
			if (!ModConfig.StressEnabled.Value)
			{
				((Graphic)s_boat).color = s_boatColor;
				return;
			}
			float num = ShipState.ReadStress(ship);
			float num2 = Mathf.InverseLerp(ModConfig.StressWarnAt.Value * 0.5f, 1f, num);
			Color val = Color.Lerp(s_boatColor, s_stressed, num2);
			if (num >= ModConfig.StressDangerAt.Value && !blink)
			{
				val = Color.Lerp(val, Color.black, 0.45f);
			}
			((Graphic)s_boat).color = val;
		}

		private static bool Ensure(Hud hud)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Invalid comparison between Unknown and I4
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_root != (Object)null && (Object)(object)s_root == (Object)(object)hud.m_shipWindIndicatorRoot && (Object)(object)s_speed != (Object)null)
			{
				return true;
			}
			RectTransform shipWindIndicatorRoot = hud.m_shipWindIndicatorRoot;
			RectTransform shipWindIconRoot = hud.m_shipWindIconRoot;
			if ((Object)(object)shipWindIndicatorRoot == (Object)null || (Object)(object)shipWindIconRoot == (Object)null || (Object)(object)hud.m_shipWindIcon == (Object)null)
			{
				return false;
			}
			s_root = shipWindIndicatorRoot;
			s_windIcon = ((Graphic)hud.m_shipWindIcon).rectTransform;
			s_windIconLocal = Vector2.op_Implicit(((Transform)shipWindIconRoot).InverseTransformPoint(((Transform)s_windIcon).position));
			s_shownSpeed = (s_shownWind = -1);
			Canvas componentInParent = ((Component)shipWindIndicatorRoot).GetComponentInParent<Canvas>();
			s_overlay = (Object)(object)componentInParent != (Object)null && (int)componentInParent.rootCanvas.renderMode == 0;
			Transform val = ((Transform)shipWindIndicatorRoot).Find("Ship");
			s_boat = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null);
			if ((Object)(object)s_boat != (Object)null)
			{
				s_boatColor = ((Graphic)s_boat).color;
			}
			else
			{
				Plugin.Log.LogWarning((object)"Ship HUD: boat icon not found, hull stress will not tint it.");
			}
			if ((Object)(object)s_triangle == (Object)null)
			{
				s_triangle = MakeTriangle();
			}
			if ((Object)(object)s_pill == (Object)null)
			{
				s_pill = MakePill();
			}
			if ((Object)(object)s_arrowHead == (Object)null)
			{
				s_arrowHead = MakeArrowHead();
			}
			if ((Object)(object)s_arrowShaft == (Object)null)
			{
				s_arrowShaft = MakeArrowShaft();
			}
			TMP_Text healthText = hud.m_healthText;
			s_coursePivot = NewRect("Seamanship_Course", (Transform)(object)shipWindIndicatorRoot, Vector2.zero);
			s_courseShaft = NewImage("Shaft", (Transform)(object)s_coursePivot, new Vector2(7f, 1f), s_arrowShaft);
			for (int i = 0; i < 5; i++)
			{
				s_courseHeads[i] = NewImage("Head" + i, (Transform)(object)s_coursePivot, new Vector2(17f, 10f), s_arrowHead);
				((Graphic)s_courseHeads[i]).rectTransform.anchoredPosition = new Vector2(0f, 52f - 10f * ((float)i + 0.5f));
			}
			s_shift = NewImage("Seamanship_WindShift", (Transform)(object)shipWindIconRoot, new Vector2(11f, 11f), s_triangle);
			s_speedPlate = NewPlate("Seamanship_Speed", (Transform)(object)shipWindIndicatorRoot);
			s_speed = NewText("Value", (Transform)(object)s_speedPlate, healthText, 15f);
			((Graphic)s_speed).color = s_text;
			s_trend = NewImage("Trend", (Transform)(object)s_speedPlate, new Vector2(8f, 8f), s_triangle);
			s_windPlate = NewPlate("Seamanship_WindSpeed", (Transform)(object)shipWindIndicatorRoot);
			s_windName = NewText("Value", (Transform)(object)s_windPlate, healthText, 14f);
			return true;
		}

		private static RectTransform NewRect(string name, Transform parent, Vector2 size)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)parent).gameObject.layer
			}.transform;
			((Transform)val).SetParent(parent, false);
			val.sizeDelta = size;
			return val;
		}

		private static Image NewImage(string name, Transform parent, Vector2 size, Sprite sprite)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)NewRect(name, parent, size)).gameObject.AddComponent<Image>();
			obj.sprite = sprite;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static RectTransform NewPlate(string name, Transform parent)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Image obj = NewImage(name, parent, new Vector2(40f, 19f), s_pill);
			obj.type = (Type)1;
			((Graphic)obj).color = s_plate;
			return ((Graphic)obj).rectTransform;
		}

		private static TMP_Text NewText(string name, Transform parent, TMP_Text template, float size)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)NewRect(name, parent, new Vector2(120f, 24f))).gameObject;
			gameObject.SetActive(false);
			TextMeshProUGUI val = gameObject.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)template != (Object)null)
			{
				((TMP_Text)val).font = template.font;
				((TMP_Text)val).fontSharedMaterial = template.fontSharedMaterial;
			}
			gameObject.SetActive(true);
			((TMP_Text)val).fontSize = size;
			((TMP_Text)val).fontStyle = (FontStyles)1;
			((TMP_Text)val).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
			((Graphic)val).raycastTarget = false;
			((TMP_Text)val).text = "";
			return (TMP_Text)(object)val;
		}

		private static Sprite ToSprite(Color32[] pixels, int width, int height, Vector4 border)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			Texture2D val = new Texture2D(width, height, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			val.SetPixels32(pixels);
			val.Apply(false, true);
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)width, (float)height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, border);
			((Object)obj).hideFlags = (HideFlags)61;
			return obj;
		}

		private static Sprite MakeTriangle()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = (Color32[])(object)new Color32[1024];
			for (int i = 0; i < 32; i++)
			{
				float num = 15f * (1f - (float)i / 31f);
				for (int j = 0; j < 32; j++)
				{
					byte b = (byte)(Mathf.Clamp01(num - Mathf.Abs((float)j + 0.5f - 16f) + 0.5f) * 255f);
					array[i * 32 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			return ToSprite(array, 32, 32, Vector4.zero);
		}

		private static Sprite MakePill()
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = (Color32[])(object)new Color32[576];
			for (int i = 0; i < 24; i++)
			{
				for (int j = 0; j < 24; j++)
				{
					float num = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(12f, 12f));
					byte b = (byte)(Mathf.Clamp01(12f - num) * 255f);
					array[i * 24 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			float num2 = 11f;
			return ToSprite(array, 24, 24, new Vector4(num2, num2, num2, num2));
		}

		private static Sprite MakeArrowHead()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.RoundToInt(68f);
			int num2 = Mathf.RoundToInt(40f);
			float edge = 6f;
			float num3 = Mathf.Sqrt((float)(num2 * num2) + (float)(num * num) / 4f);
			Color32[] array = (Color32[])(object)new Color32[num * num2];
			for (int i = 0; i < num2; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float num4 = Mathf.Abs((float)j + 0.5f - (float)num / 2f);
					float num5 = (float)i + 0.5f;
					float num6 = ((float)num2 * ((float)num / 2f - num4) - (float)num / 2f * num5) / num3;
					float inside = Mathf.Min(num5, num6);
					array[i * num + j] = EdgedPixel(inside, edge);
				}
			}
			return ToSprite(array, num, num2, Vector4.zero);
		}

		private static Sprite MakeArrowShaft()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.RoundToInt(28f);
			float edge = 6f;
			Color32[] array = (Color32[])(object)new Color32[num * 4];
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float inside = (float)num / 2f - Mathf.Abs((float)j + 0.5f - (float)num / 2f);
					array[i * num + j] = EdgedPixel(inside, edge);
				}
			}
			return ToSprite(array, num, 4, Vector4.zero);
		}

		private static Color32 EdgedPixel(float inside, float edge)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Color32 result = Color32.Lerp(s_arrowEdge, s_arrowFill, Mathf.Clamp01(inside - edge + 0.5f));
			result.a = (byte)(Mathf.Clamp01(inside + 0.5f) * 255f);
			return result;
		}
	}
	public class StressFeedback : MonoBehaviour
	{
		private float m_shakeTimer;

		private bool m_dangerShown;

		private static Ship CurrentShip()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			Ship controlledShip = localPlayer.GetControlledShip();
			if (!((Object)(object)controlledShip != (Object)null))
			{
				return Ship.GetLocalShip();
			}
			return controlledShip;
		}

		private void Update()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.Enabled.Value || !ModConfig.StressEnabled.Value)
			{
				return;
			}
			Ship val = CurrentShip();
			if ((Object)(object)val == (Object)null)
			{
				m_dangerShown = false;
				return;
			}
			float num = ShipState.ReadStress(val);
			float value = ModConfig.StressWarnAt.Value;
			bool flag = num >= ModConfig.StressDangerAt.Value;
			if (flag && !m_dangerShown && (Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, "The rigging groans. Reef the sail!", 0, (Sprite)null, false, true);
			}
			m_dangerShown = flag;
			if (!(num < value) && val.IsSailUp() && ModConfig.CameraShake.Value && !((Object)(object)GameCamera.instance == (Object)null))
			{
				m_shakeTimer -= Time.deltaTime;
				if (!(m_shakeTimer > 0f))
				{
					m_shakeTimer = 0.6f;
					float num2 = 0.08f + 0.35f * Mathf.InverseLerp(value, 1f, num);
					GameCamera.instance.AddShake(((Component)val).transform.position, 60f, num2, false);
				}
			}
		}
	}
}
namespace Seamanship.Skill
{
	public static class SeamanshipSkill
	{
		public const string Identifier = "com.stuco.seamanship.skill";

		public static SkillType Type { get; private set; }

		public static void Register()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Type = SkillManager.Instance.AddSkill(new SkillConfig
			{
				Identifier = "com.stuco.seamanship.skill",
				Name = "Seamanship",
				Description = "Handling a boat: a livelier helm, pointing higher into the wind, and a hull that rides out gusts.",
				Icon = LoadIcon(),
				IncreaseStep = 1f
			});
		}

		public static float LocalFactor()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (int)Type == 0 || !ModConfig.SkillEnabled.Value)
			{
				return 0f;
			}
			return Mathf.Clamp01(((Character)localPlayer).GetSkillFactor(Type));
		}

		private static Sprite LoadIcon()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Seamanship.skill_icon.png");
			if (stream == null)
			{
				return null;
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num <= 0)
				{
					break;
				}
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61
			};
			if (!ImageConversion.LoadImage(val, array))
			{
				return null;
			}
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			((Object)obj).hideFlags = (HideFlags)61;
			return obj;
		}
	}
	public static class HelmTracker
	{
		private const float SkillSendInterval = 10f;

		private const float CloseHauledBand = 15f;

		private static readonly TackDetector s_tack = new TackDetector();

		private static Ship s_ship;

		private static float s_grace;

		private static float s_sendTimer;

		private static long s_sentToOwner;

		private static float s_pendingXp;

		private static float s_flushTimer;

		private static bool s_riding;

		private static bool s_rideValid;

		private static bool s_rideLoaded;

		private static int s_rideDamage;

		public static void Update(float dt)
		{
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Ship val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetControlledShip() : null);
			if ((Object)(object)localPlayer == (Object)null)
			{
				s_pendingXp = 0f;
			}
			if ((Object)(object)val != (Object)(object)s_ship)
			{
				s_ship = val;
				s_grace = ModConfig.XpGraceSeconds.Value;
				s_sendTimer = 0f;
				s_riding = false;
				s_tack.Reset();
			}
			if ((Object)(object)val == (Object)null || !ModConfig.Enabled.Value || !ModConfig.SkillEnabled.Value)
			{
				return;
			}
			SendSkill(val, dt);
			if (((Character)localPlayer).IsTeleporting())
			{
				s_grace = ModConfig.XpGraceSeconds.Value;
				s_tack.Reset();
			}
			s_grace -= dt;
			bool flag = val.IsSailUp();
			float speed = val.GetSpeed();
			float value = ModConfig.XpMinSpeed.Value;
			bool flag2 = s_grace <= 0f && flag && speed >= value;
			float num = WindSystem.SignedWindAngle(((Component)val).transform);
			float num2 = Mathf.Abs(num);
			WindLevel level = WindSystem.Local.Level;
			if (s_tack.Update(Time.time, num, speed, value, flag) && s_grace <= 0f)
			{
				s_pendingXp += ModConfig.XpTack.Value;
			}
			if (flag2)
			{
				float[] array = ModConfig.XpPerSecondByLevel();
				s_pendingXp += array[(int)(level - 1)] * dt;
				float num3 = ShipPatches.NoGoAngle(SeamanshipSkill.LocalFactor());
				if (num2 >= num3 && num2 <= num3 + 15f)
				{
					s_pendingXp += ModConfig.XpCloseHauledPerSecond.Value * dt;
				}
			}
			UpdateRideOut(val, flag2);
			s_flushTimer -= dt;
			if (s_flushTimer <= 0f)
			{
				s_flushTimer = 1f;
				if (s_pendingXp > 0f)
				{
					((Character)localPlayer).RaiseSkill(SeamanshipSkill.Type, s_pendingXp * ModConfig.XpMultiplier.Value);
					s_pendingXp = 0f;
				}
			}
		}

		private static void UpdateRideOut(Ship ship, bool earning)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			bool num = WindSystem.Local.Gust.Active || WindSystem.ShiftEventActive;
			bool flag = (int)ship.GetSpeedSetting() == 4;
			int num2 = ShipState.ReadDamageCount(ship);
			if (num)
			{
				if (!s_riding)
				{
					s_riding = true;
					s_rideValid = flag && earning;
					s_rideLoaded = false;
					s_rideDamage = num2;
				}
				else if (!flag || num2 != s_rideDamage)
				{
					s_rideValid = false;
				}
				if (ShipState.ReadStress(ship) > 0.05f)
				{
					s_rideLoaded = true;
				}
			}
			else if (s_riding)
			{
				s_riding = false;
				if (s_rideValid && s_rideLoaded && s_grace <= 0f)
				{
					s_pendingXp += ModConfig.XpRideOut.Value;
				}
			}
		}

		private static void SendSkill(Ship ship, float dt)
		{
			ZNetView nview = ship.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			long owner = nview.GetZDO().GetOwner();
			s_sendTimer -= dt;
			if (!(s_sendTimer > 0f) || owner != s_sentToOwner)
			{
				s_sendTimer = 10f;
				s_sentToOwner = owner;
				if (!nview.IsOwner())
				{
					nview.InvokeRPC("Seamanship_HelmSkill", new object[1] { SeamanshipSkill.LocalFactor() });
				}
			}
		}
	}
}
namespace Seamanship.Ships
{
	[HarmonyPatch(typeof(Ship))]
	internal static class ShipPatches
	{
		private static readonly HashSet<string> s_missingEffects = new HashSet<string>();

		private static bool Simulating(Ship ship)
		{
			if (ModConfig.Enabled.Value && (Object)(object)ship.m_nview != (Object)null && ship.m_nview.IsValid())
			{
				return ship.m_nview.IsOwner();
			}
			return false;
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void Awake(Ship __instance)
		{
			ZNetView nview = __instance.m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null)
			{
				Ship ship = __instance;
				nview.Register<float>("Seamanship_HelmSkill", (Action<long, float>)delegate(long sender, float skill)
				{
					ShipState.Get(ship).ReceiveHelmSkill(skill);
				});
			}
		}

		[HarmonyPatch("CustomFixedUpdate")]
		[HarmonyPrefix]
		private static void BeforePhysics(Ship __instance)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (Simulating(__instance))
			{
				ShipState shipState = ShipState.Get(__instance);
				shipState.Wind.Update(((Component)__instance).transform.position);
				float num = shipState.HelmSkill(__instance);
				shipState.SavedSailForce = __instance.m_sailForceFactor;
				shipState.SavedSteerForce = __instance.m_stearForce;
				shipState.SavedSteerVelForce = __instance.m_stearVelForceFactor;
				shipState.SavedPaddleForce = __instance.m_backwardForce;
				shipState.PhysicsScaled = true;
				float num2 = 1f + ModConfig.SkillRudder.Value * num;
				__instance.m_sailForceFactor *= ModConfig.HandlingSailForce.Value * (1f + ModConfig.SkillSailForce.Value * num);
				__instance.m_backwardForce *= ModConfig.HandlingPaddleForce.Value;
				__instance.m_stearForce *= ModConfig.HandlingTurnPaddle.Value * num2;
				__instance.m_stearVelForceFactor *= ModConfig.HandlingTurnSail.Value * num2;
			}
		}

		[HarmonyPatch("CustomFixedUpdate")]
		[HarmonyFinalizer]
		private static void AfterPhysics(Ship __instance, float fixedDeltaTime)
		{
			ShipState shipState = ShipState.Get(__instance);
			if (shipState.PhysicsScaled)
			{
				__instance.m_sailForceFactor = shipState.SavedSailForce;
				__instance.m_stearForce = shipState.SavedSteerForce;
				__instance.m_stearVelForceFactor = shipState.SavedSteerVelForce;
				__instance.m_backwardForce = shipState.SavedPaddleForce;
				shipState.PhysicsScaled = false;
			}
			if (!Simulating(__instance))
			{
				ClearStressIfSwitchedOff(__instance, shipState);
				return;
			}
			float skill = shipState.HelmSkill(__instance);
			ApplyWindage(__instance, shipState, skill, fixedDeltaTime);
			UpdateStress(__instance, shipState, skill, fixedDeltaTime);
		}

		private static void ClearStressIfSwitchedOff(Ship ship, ShipState state)
		{
			if (!ModConfig.Enabled.Value && !(state.Stress <= 0f) && !((Object)(object)ship.m_nview == (Object)null) && ship.m_nview.IsValid() && ship.m_nview.IsOwner())
			{
				state.Stress = 0f;
				SyncStress(ship, state);
			}
		}

		[HarmonyPatch("ApplyControlls")]
		[HarmonyPrefix]
		private static void BeforeControls(Ship __instance)
		{
			if (ModConfig.Enabled.Value)
			{
				float num = SeamanshipSkill.LocalFactor();
				ShipState shipState = ShipState.Get(__instance);
				shipState.SavedRudderSpeed = __instance.m_rudderSpeed;
				shipState.RudderScaled = true;
				__instance.m_rudderSpeed *= ModConfig.HandlingRudderSpeed.Value * (1f + ModConfig.SkillRudder.Value * num);
			}
		}

		[HarmonyPatch("ApplyControlls")]
		[HarmonyFinalizer]
		private static void AfterControls(Ship __instance)
		{
			ShipState shipState = ShipState.Get(__instance);
			if (shipState.RudderScaled)
			{
				__instance.m_rudderSpeed = shipState.SavedRudderSpeed;
				shipState.RudderScaled = false;
			}
		}

		[HarmonyPatch("GetWindAngleFactor")]
		[HarmonyPostfix]
		private static void WindAngleFactor(Ship __instance, ref float __result)
		{
			if (ModConfig.Enabled.Value && ModConfig.UpwindEnabled.Value && !((Object)(object)EnvMan.instance == (Object)null))
			{
				float skill = ShipState.Get(__instance).HelmSkill(__instance);
				float angleOffWind = Mathf.Abs(WindSystem.SignedWindAngle(((Component)__instance).transform));
				__result = UpwindCurve.Apply(__result, angleOffWind, UpwindFloor(skill), NoGoAngle(skill), ModConfig.NoGoFade.Value, ModConfig.CloseHauledBonus.Value, ModConfig.CloseHauledBand.Value);
			}
		}

		public static float UpwindFloor(float skill)
		{
			return Mathf.Lerp(ModConfig.UpwindFloor.Value, ModConfig.SkillUpwindFloor.Value, skill);
		}

		public static float NoGoAngle(float skill)
		{
			return Mathf.Lerp(ModConfig.NoGoAngle.Value, ModConfig.SkillNoGoAngle.Value, skill);
		}

		private static void ApplyWindage(Ship ship, ShipState state, float skill, float dt)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Invalid comparison between Unknown and I4
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.WindageEnabled.Value || (Object)(object)EnvMan.instance == (Object)null || !ship.HaveControllingPlayer())
			{
				return;
			}
			Rigidbody body = ship.m_body;
			if ((Object)(object)body == (Object)null || !Afloat(ship, body))
			{
				return;
			}
			Transform transform = ((Component)ship).transform;
			Vector3 windDir = EnvMan.instance.GetWindDir();
			Vector3 right = transform.right;
			float num = Vector3.Dot(windDir, right);
			float num2 = Mathf.Clamp(state.Wind.WGust / 0.35f, 0.25f, 1.7f);
			num2 *= Mathf.Lerp(1f, ModConfig.SkillWindage.Value, skill);
			num2 *= Windage.SailFactor((int)ship.m_speed == 4, (int)ship.m_speed == 3, ModConfig.WindageHalfSail.Value, ModConfig.WindageBarePoles.Value);
			Vector3 val = right * (num * num2 * body.mass * dt);
			body.AddForceAtPosition(val * (ModConfig.WindageLeeway.Value * ship.m_backwardForce), body.worldCenterOfMass, (ForceMode)1);
			Vector3 val2 = val * (0.5f * ModConfig.WindageBowFallOff.Value * ship.m_stearForce);
			Vector3 val3 = transform.forward * ship.m_stearForceOffset;
			body.AddForceAtPosition(val2, transform.position - val3, (ForceMode)1);
			body.AddForceAtPosition(-val2, transform.position + val3, (ForceMode)1);
			float num3 = Windage.HeadDepth(WindSystem.SignedWindAngle(transform), NoGoAngle(skill), ModConfig.NoGoFade.Value);
			if (!(num3 <= 0f))
			{
				Vector3 forward = transform.forward;
				float deceleration = ModConfig.WindageHeadWind.Value * ship.m_backwardForce * num2 * num3;
				float num4 = Windage.HeadWindBrake(Vector3.Dot(body.linearVelocity, forward), deceleration, dt, ModConfig.WindageHeadWindMinSpeed.Value);
				if (num4 > 0f)
				{
					body.AddForce(-forward * num4, (ForceMode)2);
				}
			}
		}

		private static bool Afloat(Ship ship, Rigidbody body)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
			return body.worldCenterOfMass.y - num - ship.m_waterLevelOffset <= ship.m_disableLevel + 1f;
		}

		private static void UpdateStress(Ship ship, ShipState state, float skill, float dt)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			if (!ModConfig.StressEnabled.Value)
			{
				if (state.Stress > 0f)
				{
					state.Stress = 0f;
					SyncStress(ship, state);
				}
				return;
			}
			bool fullSail = (int)ship.m_speed == 4;
			bool halfSail = (int)ship.m_speed == 3;
			float angleOffWind = Mathf.Abs(WindSystem.SignedWindAngle(((Component)ship).transform));
			WindLevel windLevel = WindLevels.Bucket(state.Wind.WGust);
			if (windLevel < state.Wind.Level)
			{
				windLevel = state.Wind.Level;
			}
			float buildScale = Mathf.Lerp(1f, ModConfig.SkillStress.Value, skill);
			float num = StressModel.Rate(windLevel, fullSail, halfSail, angleOffWind, buildScale, ModConfig.StressParams());
			state.Stress = Mathf.Clamp01(state.Stress + num * dt);
			if (state.Stress >= ModConfig.StressWarnAt.Value && num > 0f)
			{
				state.WarnTimer -= dt;
				if (state.WarnTimer <= 0f)
				{
					state.WarnTimer = ((state.Stress >= ModConfig.StressDangerAt.Value) ? 1.5f : 4f);
					PlayEffect(ModConfig.StressWarnSound.Value, ship);
				}
			}
			else
			{
				state.WarnTimer = 0f;
			}
			if (state.Stress >= 1f)
			{
				state.DamageTimer -= dt;
				if (state.DamageTimer <= 0f)
				{
					state.DamageTimer = Mathf.Max(0.5f, ModConfig.StressDamageInterval.Value);
					DamageHull(ship, state);
				}
			}
			else
			{
				state.DamageTimer = Mathf.Min(1f, ModConfig.StressDamageInterval.Value);
			}
			SyncStress(ship, state);
		}

		private static void DamageHull(Ship ship, ShipState state)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			WearNTear hull = state.Hull;
			if (!((Object)(object)hull == (Object)null))
			{
				ZDO zDO = ship.m_nview.GetZDO();
				float health = hull.m_health;
				float num = zDO.GetFloat(ZDOVars.s_health, health);
				float num2 = health * ModConfig.StressMinHullPercent.Value / 100f;
				float num3 = Mathf.Min(health * ModConfig.StressDamagePercent.Value / 100f, num - num2);
				if (!(num3 <= 0f))
				{
					HitData val = new HitData();
					val.m_damage.m_damage = num3;
					val.m_point = (((Object)(object)ship.m_mastObject != (Object)null) ? ship.m_mastObject.transform.position : ((Component)ship).transform.position);
					val.m_dir = Vector3.down;
					hull.Damage(val);
					Diagnostics.Note("overpressing damaged the hull");
					state.DamageCount++;
					zDO.Set(ShipState.DamageCountKey, state.DamageCount, false);
					PlayEffect(ModConfig.StressDamageSound.Value, ship);
				}
			}
		}

		private static void SyncStress(Ship ship, ShipState state)
		{
			if (!(Mathf.Abs(state.Stress - state.SyncedStress) < 0.02f) || (state.Stress <= 0f && state.SyncedStress > 0f))
			{
				state.SyncedStress = state.Stress;
				ship.m_nview.GetZDO().Set(ShipState.StressKey, state.Stress);
			}
		}

		private static void PlayEffect(string prefabName, Ship ship)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(prefabName) || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			try
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
				if ((Object)(object)prefab == (Object)null)
				{
					if (s_missingEffects.Add(prefabName))
					{
						Plugin.Log.LogWarning((object)("Sound \"" + prefabName + "\" does not exist in this game version; nothing will play."));
					}
				}
				else
				{
					Diagnostics.Note("played " + prefabName);
					Vector3 val = (((Object)(object)ship.m_mastObject != (Object)null) ? ship.m_mastObject.transform.position : ((Component)ship).transform.position);
					Object.Instantiate<GameObject>(prefab, val, Quaternion.identity);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Could not play " + prefabName + ": " + ex.Message));
			}
		}
	}
	public sealed class ShipState
	{
		public const string HelmSkillRpc = "Seamanship_HelmSkill";

		public static readonly int StressKey = StringExtensionMethods.GetStableHashCode("Seamanship_Stress");

		public static readonly int DamageCountKey = StringExtensionMethods.GetStableHashCode("Seamanship_StressHits");

		private const float RemoteSkillLifetime = 30f;

		private static readonly ConditionalWeakTable<Ship, ShipState> s_states = new ConditionalWeakTable<Ship, ShipState>();

		public readonly WindProbe Wind = new WindProbe();

		public WearNTear Hull;

		public float Stress;

		public float SyncedStress = -1f;

		public float DamageTimer;

		public float WarnTimer;

		public int DamageCount;

		public float SavedSailForce;

		public float SavedSteerForce;

		public float SavedSteerVelForce;

		public float SavedPaddleForce;

		public float SavedRudderSpeed;

		public bool PhysicsScaled;

		public bool RudderScaled;

		private float m_remoteSkill;

		private float m_remoteSkillTime = float.NegativeInfinity;

		public static ShipState Get(Ship ship)
		{
			return s_states.GetValue(ship, (Ship s) => new ShipState
			{
				Hull = ((Component)s).GetComponent<WearNTear>()
			});
		}

		public void ReceiveHelmSkill(float skill)
		{
			m_remoteSkill = Mathf.Clamp01(skill);
			m_remoteSkillTime = Time.time;
		}

		public float HelmSkill(Ship ship)
		{
			if (!ModConfig.SkillEnabled.Value)
			{
				return 0f;
			}
			if (IsLocalHelm(ship))
			{
				return SeamanshipSkill.LocalFactor();
			}
			if (!ship.HaveControllingPlayer())
			{
				return 0f;
			}
			if (!(Time.time - m_remoteSkillTime <= 30f))
			{
				return 0f;
			}
			return m_remoteSkill;
		}

		public static bool IsLocalHelm(Ship ship)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return (Object)(object)localPlayer.GetControlledShip() == (Object)(object)ship;
			}
			return false;
		}

		public static float ReadStress(Ship ship)
		{
			ZNetView nview = ship.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return 0f;
			}
			if (!nview.IsOwner())
			{
				return nview.GetZDO().GetFloat(StressKey, 0f);
			}
			return Get(ship).Stress;
		}

		public static int ReadDamageCount(Ship ship)
		{
			ZNetView nview = ship.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return 0;
			}
			return nview.GetZDO().GetInt(DamageCountKey, 0);
		}
	}
}
namespace Seamanship.Core
{
	public sealed class GustParams
	{
		public float[] ChancePerSlot = new float[5] { 0f, 0f, 0.25f, 0.4f, 0.55f };

		public float SlotSeconds = 20f;

		public float MinDuration = 4f;

		public float MaxDuration = 10f;

		public float MinStrength = 0.05f;

		public float MaxStrength = 0.15f;
	}
	public struct GustState
	{
		public float Strength;

		public long Id;

		public bool Active => Strength > 0f;
	}
	public static class Gusts
	{
		private const int StreamChance = 21;

		private const int StreamDuration = 22;

		private const int StreamStart = 23;

		private const int StreamStrength = 24;

		public static GustState Evaluate(double timeSeconds, int worldSeed, WindLevel level, GustParams p)
		{
			GustState result = new GustState
			{
				Strength = 0f,
				Id = -1L
			};
			if (p == null || p.SlotSeconds <= 0f || timeSeconds < 0.0)
			{
				return result;
			}
			long num = (long)Math.Floor(timeSeconds / (double)p.SlotSeconds);
			int num2 = Math.Min(Math.Max((int)level, 1), 5) - 1;
			if (Hash.Value(worldSeed, 21, num) >= p.ChancePerSlot[num2])
			{