Decompiled source of SeaLegs v0.11.0

plugins/SeaLegs/SeaLegs.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SeaLegs;

[HarmonyPatch]
internal static class HelmView
{
	private class SailSwap
	{
		public Renderer R;

		public Material[] Original;

		public Material[] SeeThrough;
	}

	private static readonly FieldInfo CamDistance = AccessTools.Field(typeof(GameCamera), "m_distance");

	private static Ship _ship;

	private static float _origMaxBoat = -1f;

	private static readonly List<SailSwap> Swaps = new List<SailSwap>();

	private static Shader _shader;

	private static bool _shaderChecked;

	private static float _appliedOpacity = -1f;

	private static float _side = float.NaN;

	private static float _height = float.NaN;

	private static bool _moving;

	internal static void Update()
	{
		Ship val = Plugin.Steering();
		if ((Object)(object)val != (Object)(object)_ship)
		{
			Leave();
			if ((Object)(object)val != (Object)null)
			{
				Enter(val);
			}
		}
		if (!((Object)(object)_ship == (Object)null))
		{
			Song.UpdateHelm(_ship);
			ArrowKeys();
			if (Plugin.SeeThroughSail.Value && Swaps.Count == 0)
			{
				SailOn(_ship);
			}
			if (!Plugin.SeeThroughSail.Value && Swaps.Count > 0)
			{
				SailOff();
			}
			if (Swaps.Count > 0 && !Mathf.Approximately(_appliedOpacity, Plugin.SailOpacity.Value))
			{
				SetOpacity();
			}
		}
	}

	private static void Enter(Ship ship)
	{
		_ship = ship;
		Plugin.Debug("[helm] took the helm of " + ((Object)ship).name);
		GameCamera instance = GameCamera.instance;
		float value = Plugin.CameraDistance.Value;
		if ((Object)(object)instance != (Object)null && value > 0f)
		{
			if (_origMaxBoat < 0f)
			{
				_origMaxBoat = instance.m_maxDistanceBoat;
			}
			instance.m_maxDistanceBoat = Mathf.Max(_origMaxBoat, value);
			if (CamDistance != null)
			{
				CamDistance.SetValue(instance, value);
			}
			Plugin.Debug("[helm] camera distance " + value + " (boat max was " + _origMaxBoat + ")");
		}
	}

	internal static void Leave()
	{
		if (!((Object)(object)_ship == (Object)null))
		{
			Plugin.Debug("[helm] left the helm");
			SailOff();
			GameCamera instance = GameCamera.instance;
			if ((Object)(object)instance != (Object)null && _origMaxBoat >= 0f)
			{
				instance.m_maxDistanceBoat = _origMaxBoat;
			}
			_ship = null;
			Song.UpdateHelm(null);
		}
	}

	private static void ArrowKeys()
	{
		if (float.IsNaN(_side))
		{
			_side = Plugin.CameraSide.Value;
			_height = Plugin.CameraHeight.Value;
		}
		if (Plugin.CameraKeys.Value && !Busy())
		{
			float num = (Input.GetKey((KeyCode)275) ? 1f : 0f) - (Input.GetKey((KeyCode)276) ? 1f : 0f);
			float num2 = (Input.GetKey((KeyCode)273) ? 1f : 0f) - (Input.GetKey((KeyCode)274) ? 1f : 0f);
			if (num != 0f || num2 != 0f)
			{
				float num3 = Plugin.CameraKeySpeed.Value * Time.deltaTime;
				_side = Mathf.Clamp(_side + num * num3, -8f, 8f);
				_height = Mathf.Clamp(_height + num2 * num3, -2f, 10f);
				_moving = true;
			}
			else if (_moving)
			{
				_moving = false;
				Plugin.CameraSide.Value = (float)Math.Round(_side, 2);
				Plugin.CameraHeight.Value = (float)Math.Round(_height, 2);
				Plugin.Debug("[helm] camera moved: side " + Plugin.CameraSide.Value + " m, height " + Plugin.CameraHeight.Value + " m");
			}
		}
	}

	internal static bool Busy()
	{
		if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !TextInput.IsVisible())
		{
			return Minimap.IsOpen();
		}
		return true;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameCamera), "GetCameraPosition")]
	private static void Raise(ref Vector3 pos, ref Quaternion rot)
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: 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_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: 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_00a7: 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_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_ship == (Object)null) && Plugin.Enabled.Value)
		{
			if (!_moving)
			{
				_side = Plugin.CameraSide.Value;
				_height = Plugin.CameraHeight.Value;
			}
			Vector3 val = rot * Vector3.right;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
			{
				pos += ((Vector3)(ref val)).normalized * _side;
			}
			pos += Vector3.up * _height;
		}
	}

	private static void SailOn(Ship ship)
	{
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ship.m_sailObject == (Object)null)
		{
			return;
		}
		Shader val = FindShader();
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Renderer[] componentsInChildren = ship.m_sailObject.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val2 in componentsInChildren)
		{
			Material[] sharedMaterials = val2.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				continue;
			}
			SailSwap sailSwap = new SailSwap();
			sailSwap.R = val2;
			sailSwap.Original = sharedMaterials;
			sailSwap.SeeThrough = (Material[])(object)new Material[sharedMaterials.Length];
			SailSwap sailSwap2 = sailSwap;
			for (int j = 0; j < sharedMaterials.Length; j++)
			{
				Material val3 = new Material(val);
				((Object)val3).hideFlags = (HideFlags)61;
				if ((Object)(object)sharedMaterials[j] != (Object)null && sharedMaterials[j].HasProperty("_MainTex"))
				{
					val3.mainTexture = sharedMaterials[j].mainTexture;
					val3.mainTextureScale = sharedMaterials[j].mainTextureScale;
					val3.mainTextureOffset = sharedMaterials[j].mainTextureOffset;
				}
				sailSwap2.SeeThrough[j] = val3;
			}
			val2.sharedMaterials = sailSwap2.SeeThrough;
			Swaps.Add(sailSwap2);
		}
		_appliedOpacity = -1f;
		SetOpacity();
		Plugin.Debug("[helm] sail see-through: " + Swaps.Count + " renderer(s), shader " + ((Object)val).name);
	}

	private static void SetOpacity()
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Clamp01(Plugin.SailOpacity.Value);
		foreach (SailSwap swap in Swaps)
		{
			Material[] seeThrough = swap.SeeThrough;
			foreach (Material val in seeThrough)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.color = new Color(0.85f, 0.85f, 0.85f, num);
				}
			}
		}
		foreach (SailSwap swap2 in Swaps)
		{
			if ((Object)(object)swap2.R != (Object)null)
			{
				swap2.R.enabled = num > 0.001f;
			}
		}
		_appliedOpacity = Plugin.SailOpacity.Value;
	}

	internal static void Glow(float k)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: 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_0091: Unknown result type (might be due to invalid IL or missing references)
		if (Swaps.Count == 0)
		{
			return;
		}
		float num = Mathf.Clamp01(Plugin.SailOpacity.Value);
		Color color = Color.Lerp(new Color(0.85f, 0.85f, 0.85f, num), new Color(1f, 0.8f, 0.42f, Mathf.Clamp01(num + 0.12f)), Mathf.Clamp01(k));
		foreach (SailSwap swap in Swaps)
		{
			Material[] seeThrough = swap.SeeThrough;
			foreach (Material val in seeThrough)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.color = color;
				}
			}
		}
	}

	private static void SailOff()
	{
		foreach (SailSwap swap in Swaps)
		{
			if ((Object)(object)swap.R != (Object)null)
			{
				swap.R.sharedMaterials = swap.Original;
				swap.R.enabled = true;
			}
			Material[] seeThrough = swap.SeeThrough;
			foreach (Material val in seeThrough)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
		if (Swaps.Count > 0)
		{
			Plugin.Debug("[helm] sail back to normal");
		}
		Swaps.Clear();
	}

	private static Shader FindShader()
	{
		if (_shaderChecked)
		{
			return _shader;
		}
		_shaderChecked = true;
		string[] array = new string[3] { "Sprites/Default", "UI/Default", "Legacy Shaders/Transparent/Diffuse" };
		foreach (string text in array)
		{
			Shader val = Shader.Find(text);
			if ((Object)(object)val != (Object)null && val.isSupported)
			{
				_shader = val;
				break;
			}
		}
		if ((Object)(object)_shader == (Object)null)
		{
			Plugin.Log.LogWarning((object)"No see-through shader found; the sail stays normal.");
		}
		return _shader;
	}
}
internal class PitchShift : MonoBehaviour
{
	private const int Window = 2048;

	private const int Size = 8192;

	internal volatile float Ratio = 1f;

	private float[][] _buf;

	private int _write;

	private float _phase;

	private void OnAudioFilterRead(float[] data, int channels)
	{
		float ratio = Ratio;
		if (channels <= 0 || Mathf.Abs(ratio - 1f) < 0.001f)
		{
			return;
		}
		if (_buf == null || _buf.Length != channels)
		{
			_buf = new float[channels][];
			for (int i = 0; i < channels; i++)
			{
				_buf[i] = new float[8192];
			}
			_write = 0;
			_phase = 0f;
		}
		float num = 1f - ratio;
		int num2 = data.Length / channels;
		for (int j = 0; j < num2; j++)
		{
			float phase = _phase;
			float num3 = phase + 1024f;
			if (num3 >= 2048f)
			{
				num3 -= 2048f;
			}
			float num4 = 1f - Mathf.Abs(2f * phase / 2048f - 1f);
			float num5 = 1f - num4;
			for (int k = 0; k < channels; k++)
			{
				float[] array = _buf[k];
				array[_write] = data[j * channels + k];
				data[j * channels + k] = Tap(array, phase) * num4 + Tap(array, num3) * num5;
			}
			_write = (_write + 1) % 8192;
			_phase += num;
			if (_phase >= 2048f)
			{
				_phase -= 2048f;
			}
			else if (_phase < 0f)
			{
				_phase += 2048f;
			}
		}
	}

	private float Tap(float[] b, float delay)
	{
		float num;
		for (num = (float)_write - 1f - delay; num < 0f; num += 8192f)
		{
		}
		int num2 = (int)num;
		float num3 = num - (float)num2;
		int num4 = (num2 + 1) % 8192;
		return b[num2] + (b[num4] - b[num2]) * num3;
	}
}
[BepInPlugin("felih.SeaLegs", "Sea Legs", "0.11.0")]
public class Plugin : BaseUnityPlugin
{
	internal enum RockLook
	{
		Outline,
		Fill,
		Both
	}

	public const string GUID = "felih.SeaLegs";

	public const string NAME = "Sea Legs";

	public const string VERSION = "0.11.0";

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> AutoSing;

	internal static ConfigEntry<bool> SingWhenSeated;

	internal static ConfigEntry<bool> SingStanding;

	internal static ConfigEntry<bool> CrewMessage;

	internal static ConfigEntry<bool> HelmGlow;

	internal static ConfigEntry<bool> HelmSings;

	internal static ConfigEntry<bool> SingHint;

	internal static ConfigEntry<KeyboardShortcut> SingKey;

	internal static ConfigEntry<string> HoldAnimation;

	internal static ConfigEntry<float> StandStillSeconds;

	internal static ConfigEntry<float> BonusPerSinger;

	internal static ConfigEntry<float> MaxBonus;

	internal static ConfigEntry<float> EmoteMinSeconds;

	internal static ConfigEntry<float> EmoteMaxSeconds;

	internal static ConfigEntry<int> VibeWeight;

	internal static ConfigEntry<int> DanceWeight;

	internal static ConfigEntry<int> RoarWeight;

	internal static ConfigEntry<bool> OnlyWhileSteered;

	internal static ConfigEntry<bool> RockSilhouettes;

	internal static ConfigEntry<RockLook> RockStyle;

	internal static ConfigEntry<float> RockRange;

	internal static ConfigEntry<float> RockSecondsAhead;

	internal static ConfigEntry<float> RockMaxRange;

	internal static ConfigEntry<float> RockDepth;

	internal static ConfigEntry<float> RockOpacity;

	internal static ConfigEntry<float> RockOutlineWidth;

	internal static ConfigEntry<float> RockAboveHeight;

	internal static ConfigEntry<Color> RockColor;

	internal static ConfigEntry<Color> RockAboveColor;

	internal static ConfigEntry<float> CameraDistance;

	internal static ConfigEntry<float> CameraHeight;

	internal static ConfigEntry<float> CameraSide;

	internal static ConfigEntry<float> CameraKeySpeed;

	internal static ConfigEntry<float> SailOpacity;

	internal static ConfigEntry<bool> SeeThroughSail;

	internal static ConfigEntry<bool> CameraKeys;

	internal static ConfigEntry<bool> SongSound;

	internal static ConfigEntry<bool> ShowSongName;

	internal static ConfigEntry<float> SongVolume;

	internal static ConfigEntry<float> InstrumentVolume;

	internal static ConfigEntry<float> VoiceLayerVolume;

	internal static ConfigEntry<float> ChoirSpreadMs;

	internal static ConfigEntry<float> GameMusicWhileSinging;

	internal static ConfigEntry<float> ReshuffleSeconds;

	internal static ConfigEntry<float> SongGapSeconds;

	internal static ConfigEntry<float> VoiceFineTune;

	internal static ConfigEntry<int> MaxVoiceLayers;

	internal static ConfigEntry<Shanty.Voice> VoiceType;

	internal static ConfigEntry<string> DisabledSongs;

	internal static ConfigEntry<bool> SideRowing;

	internal static ConfigEntry<float> RowSpeed;

	internal static ConfigEntry<float> MaxRowSpeed;

	internal static ConfigEntry<float> RowAcceleration;

	internal static ConfigEntry<float> RowSpotReach;

	internal static ConfigEntry<float> RowSpotInset;

	internal static ConfigEntry<float> RowEmoteSeconds;

	internal static ConfigEntry<int> MaxRowers;

	internal static ConfigEntry<string> RowEmote;

	internal static ConfigEntry<string> RowAnimation;

	internal static ConfigEntry<Color> RowSpotColor;

	internal static ConfigEntry<bool> DetailedLog;

	internal static Plugin Instance;

	private void Awake()
	{
		//IL_0049: 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_0156: Expected O, but got Unknown
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Expected O, but got Unknown
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f6: Expected O, but got Unknown
		//IL_0334: Unknown result type (might be due to invalid IL or missing references)
		//IL_033e: Expected O, but got Unknown
		//IL_0392: Unknown result type (might be due to invalid IL or missing references)
		//IL_039c: Expected O, but got Unknown
		//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03da: Expected O, but got Unknown
		//IL_040e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0418: Expected O, but got Unknown
		//IL_044c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0456: Expected O, but got Unknown
		//IL_047f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0489: Expected O, but got Unknown
		//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c7: Expected O, but got Unknown
		//IL_051b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0525: Expected O, but got Unknown
		//IL_0559: Unknown result type (might be due to invalid IL or missing references)
		//IL_0563: Expected O, but got Unknown
		//IL_05fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0605: Expected O, but got Unknown
		//IL_0639: Unknown result type (might be due to invalid IL or missing references)
		//IL_0643: Expected O, but got Unknown
		//IL_0677: Unknown result type (might be due to invalid IL or missing references)
		//IL_0681: Expected O, but got Unknown
		//IL_06b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_06bf: Expected O, but got Unknown
		//IL_06f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_06fd: Expected O, but got Unknown
		//IL_0721: Unknown result type (might be due to invalid IL or missing references)
		//IL_0764: Unknown result type (might be due to invalid IL or missing references)
		//IL_076e: Expected O, but got Unknown
		//IL_07a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_07ac: Expected O, but got Unknown
		//IL_07d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0813: Unknown result type (might be due to invalid IL or missing references)
		//IL_081d: Expected O, but got Unknown
		//IL_0851: Unknown result type (might be due to invalid IL or missing references)
		//IL_085b: Expected O, but got Unknown
		//IL_08af: Unknown result type (might be due to invalid IL or missing references)
		//IL_08b9: Expected O, but got Unknown
		//IL_08ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_08f7: Expected O, but got Unknown
		//IL_094b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0955: Expected O, but got Unknown
		//IL_09a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b3: Expected O, but got Unknown
		//IL_09e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_09f1: Expected O, but got Unknown
		//IL_0a25: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a2f: Expected O, but got Unknown
		//IL_0a58: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a62: Expected O, but got Unknown
		//IL_0a96: Unknown result type (might be due to invalid IL or missing references)
		//IL_0aa0: Expected O, but got Unknown
		//IL_0ad4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ade: Expected O, but got Unknown
		//IL_0b07: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b6e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b78: Expected O, but got Unknown
		//IL_0bdb: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Turns the whole mod on or off.");
		SingKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("2 - Crew song", "SingKey", new KeyboardShortcut((KeyCode)107, (KeyCode[])(object)new KeyCode[0]), "Starts a song on the boat (typing /vibe, /dance or /roar on a boat does the same). Press it again to stop singing; you stay quiet until that song is over.");
		AutoSing = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "AutoSing", true, "Join in automatically when someone on your boat starts a song, while you sit or stand still on the deck. Nobody starts singing on their own.");
		HelmSings = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "HelmsmanSingsAlong", true, "At the helm you sing along while the crew sings (you count as a singer too), but you never start the song.");
		SingHint = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "SingHint", true, "When you get on a boat where nobody sings, show which key starts a song.");
		OnlyWhileSteered = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "OnlyWhileSomeoneSteers", false, "Sing only while someone else is at the helm. Off = you also sing alone, or on an anchored boat.");
		SingWhenSeated = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "SingWhenSeated", true, "Sing while sitting on the boat.");
		SingStanding = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "SingStanding", true, "Sing while standing still on the deck. You are held in place like on a bench, so bumps can't throw you off; any movement key lets go.");
		StandStillSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Crew song", "StandStillSeconds", 3f, new ConfigDescription("How long you must stand still on the deck before you start singing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0]));
		HoldAnimation = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Crew song", "HoldAnimation", "", "Animation used while you are held on the deck. Empty = stand normally, so the dance shows.");
		BonusPerSinger = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Crew song", "BonusPerSinger", 0.15f, new ConfigDescription("Extra sail force per crew member singing (0.15 = +15%). Used by the helmsman's game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		MaxBonus = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Crew song", "MaxBonus", 0.45f, new ConfigDescription("Most extra sail force the song can give (0.45 = +45%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), new object[0]));
		CrewMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "CrewMessage", true, "At the helm, show a message when the number of singers changes.");
		HelmGlow = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Crew song", "HelmGlow", true, "At the helm, show the song working: golden wind ribbons through the sail, a golden sail, and a golden wind icon with clinking mugs and the number of singers under it.");
		EmoteMinSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Crew song", "EmoteMinSeconds", 8f, "Shortest time before switching to another dance move.");
		EmoteMaxSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Crew song", "EmoteMaxSeconds", 16f, "Longest time before switching to another dance move.");
		VibeWeight = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Crew song", "VibeWeight", 75, "How often the Vibe emote is picked (relative weight).");
		DanceWeight = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Crew song", "DanceWeight", 20, "How often the Dance emote is picked (relative weight).");
		RoarWeight = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Crew song", "RoarWeight", 5, "How often the Roar emote is picked (relative weight).");
		SongSound = ((BaseUnityPlugin)this).Config.Bind<bool>("2b - Song sound", "SongSound", true, "Hear the crew song while people sing on your boat. Everyone on board with the mod hears the same song at the same time.");
		SongVolume = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "Volume", 0.6f, new ConfigDescription("Volume of the crew song (also follows the game's sound effects volume).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		VoiceType = ((BaseUnityPlugin)this).Config.Bind<Shanty.Voice>("2b - Song sound", "VoiceType", Shanty.Voice.Auto, "Your singing voice. Each singer adds a voice to the choir at their own pitch; others hear yours. Auto = picked from your character.");
		VoiceFineTune = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "VoiceFineTune", 0f, new ConfigDescription("Move your voice up or down a little more (semitones).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-3f, 3f), new object[0]));
		InstrumentVolume = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "InstrumentVolume", 0.5f, new ConfigDescription("Volume of the instruments under the choir, and of the instrumental songs, compared with the voices (1 = as recorded, 0.5 = half, so the band stays in the background). Songs that come as a single file are not changed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		VoiceLayerVolume = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "VoiceVolume", 1f, new ConfigDescription("Volume of the singers' voices compared with the instruments. Songs that come as a single file get quieter extra voices on top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		ChoirSpreadMs = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "ChoirSpreadMs", 100f, new ConfigDescription("Singers are not exactly together, like a real choir: the first one sings on the beat, the others a little late or early. This is the most time between the earliest and the latest voice (milliseconds, 0 = all exactly together).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), new object[0]));
		MaxVoiceLayers = ((BaseUnityPlugin)this).Config.Bind<int>("2b - Song sound", "MaxChoirVoices", 4, new ConfigDescription("Most singers heard with their own voice at once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[0]));
		GameMusicWhileSinging = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "GameMusicWhileSinging", 0.15f, new ConfigDescription("Game music volume while the crew sings (1 = unchanged, 0 = silent).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		ShowSongName = ((BaseUnityPlugin)this).Config.Bind<bool>("2b - Song sound", "ShowSongName", true, "Show the name of each song when it starts.");
		ReshuffleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "ReshuffleSeconds", 60f, new ConfigDescription("When nobody has sung on the boat for this long, the playlist ends and the next song starts a new shuffle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), new object[0]));
		SongGapSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2b - Song sound", "GapBetweenSongs", 2f, new ConfigDescription("Silence between two songs (seconds). Set by whoever leads the playlist.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 15f), new object[0]));
		DisabledSongs = ((BaseUnityPlugin)this).Config.Bind<string>("2b - Song sound", "DisabledSongs", "", "Songs you never want in the playlist when you lead it: file names without extension, separated by commas (example: m_cursed_compass, i_wellerman_epic). Your own songs go in BepInEx/config/SeaLegs/songs.");
		RockSilhouettes = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Rocks", "RockSilhouettes", true, "At the helm, show rocks hidden just under the water.");
		RockStyle = ((BaseUnityPlugin)this).Config.Bind<RockLook>("3 - Rocks", "Style", RockLook.Outline, "Outline = only a line around the rocks. Fill = paint the whole rock. Both = filled with a line around.");
		RockOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "OutlineWidth", 2f, new ConfigDescription("Width of the outline in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), new object[0]));
		RockRange = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "Range", 30f, new ConfigDescription("How far around the boat to look for rocks (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 150f), new object[0]));
		RockSecondsAhead = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "SecondsAhead", 6f, new ConfigDescription("The faster the boat, the further ahead rocks show: Range plus this many seconds of sailing in front of the bow (0 = always Range).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[0]));
		RockMaxRange = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "MaxRange", 100f, new ConfigDescription("How far ahead rocks can show at full speed (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 200f), new object[0]));
		RockDepth = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "Depth", 3f, new ConfigDescription("Show the parts of rocks from the water surface down to this depth (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[0]));
		RockColor = ((BaseUnityPlugin)this).Config.Bind<Color>("3 - Rocks", "Color", new Color(1f, 0.45f, 0.1f), "Color of rocks under the water.");
		RockOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "Opacity", 0.15f, new ConfigDescription("Opacity of the rock silhouettes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[0]));
		RockAboveHeight = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Rocks", "AboveWaterHeight", 1.5f, new ConfigDescription("Also show rocks from the water surface up to this height, in their own color (0 = off).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[0]));
		RockAboveColor = ((BaseUnityPlugin)this).Config.Bind<Color>("3 - Rocks", "AboveWaterColor", new Color(1f, 0.9f, 0.2f), "Color of rocks just above the water.");
		CameraDistance = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Helm view", "CameraDistance", 12f, new ConfigDescription("Camera distance when you take the helm (0 = leave the camera alone). You can still zoom with the mouse wheel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), new object[0]));
		CameraHeight = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Helm view", "CameraHeight", 1.5f, new ConfigDescription("Raise the camera this much while steering (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 10f), new object[0]));
		CameraKeys = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Helm view", "ArrowKeysMoveCamera", true, "While steering, the arrow keys move the camera: left/right slides it sideways, up/down raises or lowers it. The position is saved.");
		CameraSide = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Helm view", "CameraSide", 0f, new ConfigDescription("Slide the camera sideways while steering (metres, negative = left).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-8f, 8f), new object[0]));
		CameraKeySpeed = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Helm view", "ArrowKeySpeed", 3f, new ConfigDescription("How fast the arrow keys move the camera (metres per second).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 15f), new object[0]));
		SeeThroughSail = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Helm view", "SeeThroughSail", true, "Make the sail see-through while you steer.");
		SailOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Helm view", "SailOpacity", 0.25f, new ConfigDescription("How visible the sail stays while you steer (0 = invisible, 1 = normal).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		SideRowing = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Side rowing", "SideRowing", true, "Two spots on every boat, at the left and right edge near the middle, marked with rings on the deck. Use one to work the ropes and push the boat straight sideways toward that side. Any movement key lets go.");
		RowSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "SpeedPerRower", 1f, new ConfigDescription("Sideways speed each rower adds (metres per second). Rowers on opposite sides cancel out. Used by the game that moves the boat (usually the helmsman's).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), new object[0]));
		MaxRowSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "MaxSpeed", 2.5f, new ConfigDescription("Fastest the crew can push the boat sideways (metres per second).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), new object[0]));
		RowAcceleration = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "Acceleration", 2f, new ConfigDescription("How quickly the boat picks up sideways speed (metres per second, each second).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), new object[0]));
		MaxRowers = ((BaseUnityPlugin)this).Config.Bind<int>("5 - Side rowing", "MaxRowersPerSide", 4, new ConfigDescription("Most rowers counted on each side.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[0]));
		RowSpotReach = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "SpotReach", 1.4f, new ConfigDescription("How close you must stand to a spot to use it (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 4f), new object[0]));
		RowSpotInset = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "SpotInset", 0f, new ConfigDescription("Move the spots toward the middle of the boat (positive) or toward the edge and its ropes (negative), in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1.5f, 2f), new object[0]));
		RowSpotColor = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Side rowing", "SpotColor", new Color(0.55f, 0.9f, 1f, 0.8f), "Color of the rings on the deck.");
		RowEmote = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Side rowing", "RowEmote", "comehere", "Emote played over and over while you row, so the hands pull the rope. Empty = none. Others: point, wave, cheer, challenge, flex.");
		RowEmoteSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Side rowing", "RowEmoteSeconds", 1.8f, new ConfigDescription("Time between two pulls of the rope.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[0]));
		RowAnimation = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Side rowing", "RowAnimation", "", "Pose while you hold the spot. Empty = stand, so the emote shows.");
		DetailedLog = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Debug", "DetailedLog", false, "Writes details to the BepInEx log.");
		SongLib.Scan(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
		new Harmony("felih.SeaLegs").PatchAll(typeof(Plugin).Assembly);
		Log.LogInfo((object)"Sea Legs 0.11.0 loaded");
	}

	private void Update()
	{
		if (!Enabled.Value)
		{
			SongGlow.Clear();
			HelmView.Leave();
			Rowing.StopRowing();
			Rowing.Clear();
			Song.Stop();
			Shanty.StopAudio();
		}
		else
		{
			Rowing.Update();
			Song.Update();
			Shanty.Update();
			HelmView.Update();
			SongGlow.Update(Steering());
		}
	}

	private void OnGUI()
	{
		if (Enabled.Value)
		{
			SongGlow.OnGUI();
		}
	}

	private void LateUpdate()
	{
		Rocks.LateUpdate();
	}

	internal static void Debug(string msg)
	{
		if (DetailedLog.Value)
		{
			Log.LogInfo((object)msg);
		}
	}

	internal static Ship Steering()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
		{
			return null;
		}
		Ship controlledShip = localPlayer.GetControlledShip();
		if ((Object)(object)controlledShip != (Object)null)
		{
			return controlledShip;
		}
		Ship localShip = Ship.GetLocalShip();
		if ((Object)(object)localShip != (Object)null && (Object)(object)localShip.m_shipControlls != (Object)null && localShip.m_shipControlls.HaveValidUser() && localShip.m_shipControlls.GetUser() == localPlayer.GetPlayerID() && ((Character)localPlayer).IsAttached())
		{
			return localShip;
		}
		return null;
	}
}
internal static class Rocks
{
	private class Rock
	{
		public Mesh Baked;

		public Mesh Shape;

		public Matrix4x4 Matrix;

		public float Top;
	}

	private const int RockBit = 1;

	private const int NearBit = 2;

	private const int AboveBit = 4;

	private const int AboveNearBit = 8;

	private const int AllBits = 15;

	private static Camera _cam;

	private static CommandBuffer _cb;

	private static bool _hooked;

	private static bool _made;

	private static Material _clear;

	private static Material _markBand;

	private static Material _markWhole;

	private static Material _markNear;

	private static Material _markNearWhole;

	private static Material _markAbove;

	private static Material _markAboveNear;

	private static Material _fillBelow;

	private static Material _fillAbove;

	private static Material _ringBelow;

	private static Material _ringAbove;

	private static Mesh _quad;

	private static readonly Dictionary<int, Rock> Cache = new Dictionary<int, Rock>();

	private static readonly List<Rock> Near = new List<Rock>();

	private static readonly Collider[] Hits = (Collider[])(object)new Collider[2048];

	private static readonly FieldInfo ShipBody = AccessTools.Field(typeof(Ship), "m_body");

	private static float _lastRange = -1f;

	private static float _nextScan;

	private static int _mask = -1;

	private static Ship _scannedFor;

	private static Mesh _box;

	internal static void LateUpdate()
	{
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Expected O, but got Unknown
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Expected O, but got Unknown
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Expected O, but got Unknown
		Camera val = (((Object)(object)GameCamera.instance != (Object)null) ? Utils.GetMainCamera() : null);
		if ((Object)(object)val != (Object)(object)_cam)
		{
			if ((Object)(object)_cam != (Object)null && _cb != null)
			{
				_cam.RemoveCommandBuffer((CameraEvent)24, _cb);
			}
			_cam = val;
			if ((Object)(object)_cam != (Object)null)
			{
				if (_cb == null)
				{
					_cb = new CommandBuffer();
					_cb.name = "Sea Legs rocks";
				}
				_cam.AddCommandBuffer((CameraEvent)24, _cb);
			}
		}
		if (!_hooked)
		{
			Camera.onPreRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreRender, (Delegate?)new CameraCallback(PreRender));
			_hooked = true;
		}
		Ship val2 = ((Plugin.Enabled.Value && Plugin.RockSilhouettes.Value) ? Plugin.Steering() : null);
		if ((Object)(object)val2 == (Object)null)
		{
			if (Near.Count > 0)
			{
				Near.Clear();
			}
			_scannedFor = null;
		}
		else if ((Object)(object)val2 != (Object)(object)_scannedFor || Time.time >= _nextScan)
		{
			Scan(val2);
		}
	}

	private static void Scan(Ship ship)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: 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_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0223: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		_scannedFor = ship;
		_nextScan = Time.time + 1f;
		if (_mask < 0)
		{
			_mask = LayerMask.GetMask(new string[2] { "static_solid", "Default" });
		}
		float num = Water();
		float value = Plugin.RockDepth.Value;
		float num2 = Mathf.Max(0f, Plugin.RockAboveHeight.Value);
		Vector3 val = ((Component)ship).transform.position;
		float num3 = Plugin.RockRange.Value;
		Rigidbody val2 = (Rigidbody)((ShipBody != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val2 != (Object)null && Plugin.RockSecondsAhead.Value > 0f)
		{
			Vector3 linearVelocity = val2.linearVelocity;
			linearVelocity.y = 0f;
			float num4 = Mathf.Min(((Vector3)(ref linearVelocity)).magnitude * Plugin.RockSecondsAhead.Value, Mathf.Max(0f, Plugin.RockMaxRange.Value - num3));
			if (num4 > 1f)
			{
				val += ((Vector3)(ref linearVelocity)).normalized * (num4 * 0.5f);
				num3 += num4 * 0.5f;
			}
			if (Plugin.DetailedLog.Value && Mathf.Abs(num3 - _lastRange) > 10f)
			{
				_lastRange = num3;
				Plugin.Log.LogInfo((object)("[rocks] speed " + ((Vector3)(ref linearVelocity)).magnitude.ToString("0.0") + " m/s: looking " + (Plugin.RockRange.Value + num4).ToString("0") + " m ahead"));
			}
		}
		int num5 = Physics.OverlapSphereNonAlloc(val, num3, Hits, _mask, (QueryTriggerInteraction)1);
		Near.Clear();
		int num6 = 0;
		int num7 = 0;
		int num8 = 0;
		for (int i = 0; i < num5; i++)
		{
			Collider val3 = Hits[i];
			if ((Object)(object)val3 == (Object)null)
			{
				continue;
			}
			Bounds bounds = val3.bounds;
			if (((Bounds)(ref bounds)).max.y < num - value || ((Bounds)(ref bounds)).min.y > num + num2)
			{
				continue;
			}
			int instanceID = ((Object)val3).GetInstanceID();
			if (!Cache.TryGetValue(instanceID, out var value2))
			{
				value2 = Make(val3);
				Cache[instanceID] = value2;
				num8++;
			}
			if (value2 != null)
			{
				if ((Object)(object)value2.Baked != (Object)null)
				{
					Near.Add(value2);
					num6++;
				}
				else if ((Object)(object)value2.Shape != (Object)null && value2.Top < num + 1f)
				{
					Near.Add(value2);
					num7++;
				}
			}
		}
		if (Cache.Count > 4000)
		{
			Cache.Clear();
		}
		if (num8 > 0)
		{
			Plugin.Debug("[rocks] " + num5 + " colliders around the boat, showing " + num6 + " (cut to the band) + " + num7 + " (whole, shape not copyable); " + num8 + " new; water " + num.ToString("0.0"));
		}
	}

	private static Rock Make(Collider c)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: 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_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: 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_016a: Expected O, but got Unknown
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)c.attachedRigidbody != (Object)null)
		{
			return null;
		}
		if ((Object)(object)((Component)c).GetComponentInParent<Piece>() != (Object)null)
		{
			return null;
		}
		if ((Object)(object)((Component)c).GetComponentInParent<Ship>() != (Object)null)
		{
			return null;
		}
		if ((Object)(object)((Component)c).GetComponentInParent<Character>() != (Object)null)
		{
			return null;
		}
		Bounds bounds = c.bounds;
		if (((Bounds)(ref bounds)).size.x > 300f || ((Bounds)(ref bounds)).size.z > 300f)
		{
			return null;
		}
		Rock rock = new Rock();
		rock.Top = ((Bounds)(ref bounds)).max.y;
		Rock rock2 = rock;
		Mesh val = null;
		MeshCollider val2 = (MeshCollider)(object)((c is MeshCollider) ? c : null);
		if ((Object)(object)val2 != (Object)null)
		{
			val = val2.sharedMesh;
		}
		BoxCollider val3 = (BoxCollider)(object)((c is BoxCollider) ? c : null);
		Matrix4x4 val4 = ((Component)c).transform.localToWorldMatrix;
		if ((Object)(object)val3 != (Object)null)
		{
			val = Box();
			val4 *= Matrix4x4.TRS(val3.center, Quaternion.identity, val3.size);
		}
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		rock2.Matrix = val4;
		if (val.isReadable)
		{
			Vector3[] vertices = val.vertices;
			int[] triangles = val.triangles;
			if (vertices.Length > 0 && triangles.Length > 0)
			{
				for (int i = 0; i < vertices.Length; i++)
				{
					ref Vector3 reference = ref vertices[i];
					reference = ((Matrix4x4)(ref val4)).MultiplyPoint3x4(vertices[i]);
				}
				Mesh val5 = new Mesh();
				((Object)val5).name = "Sea Legs rock " + ((Object)c).name;
				if (vertices.Length > 65000)
				{
					val5.indexFormat = (IndexFormat)1;
				}
				val5.vertices = vertices;
				Color32[] array = (Color32[])(object)new Color32[vertices.Length];
				for (int j = 0; j < array.Length; j++)
				{
					ref Color32 reference2 = ref array[j];
					reference2 = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
				}
				val5.colors32 = array;
				val5.triangles = triangles;
				val5.RecalculateBounds();
				rock2.Baked = val5;
				return rock2;
			}
		}
		rock2.Shape = val;
		return rock2;
	}

	private static float Water()
	{
		if (!((Object)(object)ZoneSystem.instance != (Object)null))
		{
			return 30f;
		}
		return ZoneSystem.instance.m_waterLevel;
	}

	private static void PreRender(Camera cam)
	{
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_035c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0370: Unknown result type (might be due to invalid IL or missing references)
		//IL_0372: Unknown result type (might be due to invalid IL or missing references)
		//IL_02af: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_022f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0332: Unknown result type (might be due to invalid IL or missing references)
		//IL_0343: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_0319: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)cam != (Object)(object)_cam || _cb == null)
		{
			return;
		}
		_cb.Clear();
		if (Near.Count == 0 || (Object)(object)_cam == (Object)null)
		{
			return;
		}
		Make();
		if ((Object)(object)_quad == (Object)null)
		{
			return;
		}
		float num = Water();
		float num2 = Mathf.Max(0f, Plugin.RockAboveHeight.Value);
		Vector4 val = default(Vector4);
		((Vector4)(ref val))..ctor(-10000000f, num - Plugin.RockDepth.Value, 10000000f, num);
		Vector4 val2 = default(Vector4);
		((Vector4)(ref val2))..ctor(-10000000f, num, 10000000f, num + num2);
		_markBand.SetVector("_ClipRect", val);
		_markNear.SetVector("_ClipRect", val);
		_markAbove.SetVector("_ClipRect", val2);
		_markAboveNear.SetVector("_ClipRect", val2);
		float value = Plugin.RockOpacity.Value;
		Color value2 = Plugin.RockColor.Value;
		value2.a = value;
		Color value3 = Plugin.RockAboveColor.Value;
		value3.a = value;
		Plugin.RockLook value4 = Plugin.RockStyle.Value;
		bool flag = value4 != Plugin.RockLook.Outline;
		bool flag2 = value4 != Plugin.RockLook.Fill;
		bool flag3 = num2 > 0.01f;
		Matrix4x4 worldToCameraMatrix = _cam.worldToCameraMatrix;
		Matrix4x4 projectionMatrix = _cam.projectionMatrix;
		Matrix4x4 val3 = AtNear(projectionMatrix);
		_cb.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
		_cb.DrawMesh(_quad, Matrix4x4.identity, _clear);
		_cb.SetViewProjectionMatrices(worldToCameraMatrix, val3);
		Mark(_markBand, _markWhole, flag3 ? _markAbove : null);
		if (flag2)
		{
			float value5 = Plugin.RockOutlineWidth.Value;
			float num3 = Mathf.Max(1, _cam.pixelWidth);
			float num4 = Mathf.Max(1, _cam.pixelHeight);
			for (int i = 0; i < 8; i++)
			{
				float num5 = (float)i * (float)Math.PI / 4f;
				_cb.SetViewProjectionMatrices(worldToCameraMatrix, Shift(val3, Mathf.Cos(num5) * value5 * 2f / num3, Mathf.Sin(num5) * value5 * 2f / num4));
				Mark(_markNear, _markNearWhole, flag3 ? _markAboveNear : null);
			}
		}
		_cb.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
		if (flag)
		{
			_fillBelow.SetColor("_Color", value2);
			_cb.DrawMesh(_quad, Matrix4x4.identity, _fillBelow);
			if (flag3)
			{
				_fillAbove.SetColor("_Color", value3);
				_cb.DrawMesh(_quad, Matrix4x4.identity, _fillAbove);
			}
		}
		if (flag2)
		{
			value2.a = Mathf.Min(1f, value * 2.5f);
			value3.a = value2.a;
			if (flag3)
			{
				_ringAbove.SetColor("_Color", value3);
				_cb.DrawMesh(_quad, Matrix4x4.identity, _ringAbove);
			}
			_ringBelow.SetColor("_Color", value2);
			_cb.DrawMesh(_quad, Matrix4x4.identity, _ringBelow);
		}
		_cb.DrawMesh(_quad, Matrix4x4.identity, _clear);
		_cb.SetViewProjectionMatrices(worldToCameraMatrix, projectionMatrix);
	}

	private static void Mark(Material band, Material whole, Material above)
	{
		//IL_002e: 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_004d: Unknown result type (might be due to invalid IL or missing references)
		foreach (Rock item in Near)
		{
			if ((Object)(object)item.Baked != (Object)null)
			{
				_cb.DrawMesh(item.Baked, Matrix4x4.identity, band);
				if ((Object)(object)above != (Object)null)
				{
					_cb.DrawMesh(item.Baked, Matrix4x4.identity, above);
				}
			}
			else if ((Object)(object)item.Shape != (Object)null)
			{
				_cb.DrawMesh(item.Shape, item.Matrix, whole);
			}
		}
	}

	private static Matrix4x4 Shift(Matrix4x4 p, float ox, float oy)
	{
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		p.m00 += ox * p.m30;
		p.m01 += ox * p.m31;
		p.m02 += ox * p.m32;
		p.m03 += ox * p.m33;
		p.m10 += oy * p.m30;
		p.m11 += oy * p.m31;
		p.m12 += oy * p.m32;
		p.m13 += oy * p.m33;
		return p;
	}

	private static Matrix4x4 AtNear(Matrix4x4 p)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		p.m20 = -0.999f * p.m30;
		p.m21 = -0.999f * p.m31;
		p.m22 = -0.999f * p.m32;
		p.m23 = -0.999f * p.m33;
		return p;
	}

	private static Material StencilMat(Shader sh, int reference, int readMask, int writeMask, CompareFunction comp, StencilOp op, int colorMask)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		Material val = new Material(sh);
		((Object)val).hideFlags = (HideFlags)61;
		val.SetFloat("_Stencil", (float)reference);
		val.SetFloat("_StencilReadMask", (float)readMask);
		val.SetFloat("_StencilWriteMask", (float)writeMask);
		val.SetFloat("_StencilComp", (float)comp);
		val.SetFloat("_StencilOp", (float)op);
		val.SetFloat("_ColorMask", (float)colorMask);
		return val;
	}

	private static void Make()
	{
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01aa: 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_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_024b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0261: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: 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_027e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0283: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		if (!_made)
		{
			_made = true;
			Shader val = Shader.Find("UI/Default");
			if ((Object)(object)val == (Object)null || !val.isSupported)
			{
				Plugin.Log.LogWarning((object)"Rock silhouettes are off: no usable shader was found");
				return;
			}
			_clear = StencilMat(val, 0, 0, 15, (CompareFunction)8, (StencilOp)2, 0);
			_markWhole = StencilMat(val, 1, 0, 1, (CompareFunction)8, (StencilOp)2, 0);
			_markNearWhole = StencilMat(val, 2, 0, 2, (CompareFunction)8, (StencilOp)2, 0);
			_markBand = Clipped(StencilMat(val, 1, 0, 1, (CompareFunction)8, (StencilOp)2, 0));
			_markNear = Clipped(StencilMat(val, 2, 0, 2, (CompareFunction)8, (StencilOp)2, 0));
			_markAbove = Clipped(StencilMat(val, 4, 0, 4, (CompareFunction)8, (StencilOp)2, 0));
			_markAboveNear = Clipped(StencilMat(val, 8, 0, 8, (CompareFunction)8, (StencilOp)2, 0));
			_fillBelow = StencilMat(val, 1, 1, 0, (CompareFunction)3, (StencilOp)0, 15);
			_fillAbove = StencilMat(val, 4, 5, 0, (CompareFunction)3, (StencilOp)0, 15);
			_ringBelow = StencilMat(val, 2, 7, 0, (CompareFunction)3, (StencilOp)0, 15);
			_ringAbove = StencilMat(val, 8, 15, 0, (CompareFunction)3, (StencilOp)0, 15);
			_quad = new Mesh();
			((Object)_quad).name = "Sea Legs screen quad";
			_quad.vertices = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(-1f, -1f, -0.999f),
				new Vector3(1f, -1f, -0.999f),
				new Vector3(1f, 1f, -0.999f),
				new Vector3(-1f, 1f, -0.999f)
			};
			_quad.uv = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(0f, 0f),
				new Vector2(1f, 0f),
				new Vector2(1f, 1f),
				new Vector2(0f, 1f)
			};
			_quad.colors = (Color[])(object)new Color[4]
			{
				Color.white,
				Color.white,
				Color.white,
				Color.white
			};
			_quad.triangles = new int[6] { 0, 1, 2, 0, 2, 3 };
			_quad.bounds = new Bounds(Vector3.zero, Vector3.one * 100000f);
			Plugin.Debug("[rocks] silhouettes ready (shader " + ((Object)val).name + ")");
		}
	}

	private static Material Clipped(Material m)
	{
		m.EnableKeyword("UNITY_UI_CLIP_RECT");
		m.EnableKeyword("UNITY_UI_ALPHACLIP");
		return m;
	}

	private static Mesh Box()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_box != (Object)null)
		{
			return _box;
		}
		_box = new Mesh();
		((Object)_box).name = "Sea Legs box";
		Vector3[] array = (Vector3[])(object)new Vector3[8];
		for (int i = 0; i < 8; i++)
		{
			ref Vector3 reference = ref array[i];
			reference = new Vector3(((i & 1) == 0) ? (-0.5f) : 0.5f, ((i & 2) == 0) ? (-0.5f) : 0.5f, ((i & 4) == 0) ? (-0.5f) : 0.5f);
		}
		_box.vertices = array;
		_box.triangles = new int[36]
		{
			0, 2, 1, 1, 2, 3, 4, 5, 6, 5,
			7, 6, 0, 1, 4, 1, 5, 4, 2, 6,
			3, 3, 6, 7, 0, 4, 2, 2, 4, 6,
			1, 3, 5, 3, 7, 5
		};
		_box.RecalculateBounds();
		return _box;
	}
}
[HarmonyPatch]
internal static class Rowing
{
	private class Rig
	{
		public Ship Ship;

		public Spot[] Spots = new Spot[2];
	}

	internal class Spot : MonoBehaviour, Hoverable, Interactable
	{
		public Ship Ship;

		public int Side;

		public Transform Hold;

		public Transform Inner;

		public Transform Outer;

		public LineRenderer InnerLine;

		public LineRenderer OuterLine;

		public string GetHoverText()
		{
			string text = ((Side < 0) ? "left" : "right");
			string text2 = (((Object)(object)_rowingAt == (Object)(object)this) ? "Stop rowing" : ("Row the boat to the " + text));
			return Localization.instance.Localize("Side rowing\n[<color=yellow><b>$KEY_Use</b></color>] " + text2);
		}

		public string GetHoverName()
		{
			return "Side rowing";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if ((Object)(object)_rowingAt == (Object)(object)this)
			{
				StopRowing();
				return false;
			}
			return StartRowing(this);
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void Update()
		{
			//IL_0065: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: 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_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Ship == (Object)null))
			{
				bool flag = Plugin.SideRowing.Value && ShowFor(Ship);
				if (((Renderer)InnerLine).enabled != flag)
				{
					((Renderer)InnerLine).enabled = flag;
					((Renderer)OuterLine).enabled = flag;
				}
				if (flag)
				{
					float num = Time.time * 2.2f;
					float num2 = Plugin.RowSpotColor.Value.a * (((Object)(object)_rowingAt == (Object)(object)this) ? 0.45f : 1f);
					Outer.localScale = Vector3.one * (0.55f + 0.08f * Mathf.Sin(num));
					Inner.localScale = Vector3.one * (0.3f + 0.07f * Mathf.Sin(num + 1.6f));
					Color value = Plugin.RowSpotColor.Value;
					Color val = default(Color);
					((Color)(ref val))..ctor(value.r, value.g, value.b, num2 * (0.55f + 0.25f * Mathf.Sin(num)));
					Color val2 = default(Color);
					((Color)(ref val2))..ctor(value.r, value.g, value.b, num2 * (0.75f + 0.25f * Mathf.Sin(num + 1.6f)));
					LineRenderer outerLine = OuterLine;
					Color startColor = (OuterLine.endColor = val);
					outerLine.startColor = startColor;
					LineRenderer innerLine = InnerLine;
					Color startColor2 = (InnerLine.endColor = val2);
					innerLine.startColor = startColor2;
				}
			}
		}
	}

	internal const string RowKey = "SeaLegs_row";

	private static readonly FieldInfo ShipBody = AccessTools.Field(typeof(Ship), "m_body");

	private static readonly FieldInfo ShipView = AccessTools.Field(typeof(Ship), "m_nview");

	private static readonly Dictionary<Ship, Rig> Rigs = new Dictionary<Ship, Rig>();

	private static Material _mat;

	private static Spot _rowingAt;

	private static float _nextEmote;

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

	private static float _lastLog;

	internal static bool IsRowing => (Object)(object)_rowingAt != (Object)null;

	private static bool ShowFor(Ship ship)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && (Object)(object)localPlayer.GetControlledShip() == (Object)null && (Object)(object)Ship.GetLocalShip() == (Object)(object)ship)
		{
			return Song.OnBoard(ship, localPlayer);
		}
		return false;
	}

	internal static void Update()
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!Plugin.SideRowing.Value)
		{
			StopRowing();
			Clear();
			return;
		}
		Ship val = (((Object)(object)localPlayer != (Object)null) ? Ship.GetLocalShip() : null);
		if ((Object)(object)val != (Object)null && !Rigs.ContainsKey(val) && Song.OnBoard(val, localPlayer))
		{
			Build(val);
		}
		if (Rigs.Count > 0)
		{
			List<Ship> list = null;
			foreach (KeyValuePair<Ship, Rig> rig in Rigs)
			{
				if ((Object)(object)rig.Key == (Object)null || (Object)(object)localPlayer == (Object)null || Vector3.Distance(((Component)rig.Key).transform.position, ((Component)localPlayer).transform.position) > 80f)
				{
					(list ?? (list = new List<Ship>())).Add(rig.Key);
				}
			}
			if (list != null)
			{
				foreach (Ship item in list)
				{
					Drop(item);
				}
			}
		}
		if ((Object)(object)_rowingAt == (Object)null)
		{
			return;
		}
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)_rowingAt.Ship == (Object)null || !((Character)localPlayer).IsAttached() || (Object)(object)localPlayer.GetAttachPoint() != (Object)(object)_rowingAt.Hold || (Object)(object)localPlayer.GetControlledShip() != (Object)null)
		{
			StopRowing();
			return;
		}
		ZDO val2 = Song.Zdo((Character)(object)localPlayer);
		if (val2 != null)
		{
			if (val2.GetInt("SeaLegs_row", 0) != _rowingAt.Side)
			{
				val2.Set("SeaLegs_row", _rowingAt.Side);
			}
			if (Time.time >= _nextEmote && !string.IsNullOrEmpty(Plugin.RowEmote.Value))
			{
				val2.Set(ZDOVars.s_emoteID, val2.GetInt(ZDOVars.s_emoteID, 0) + 1, false);
				val2.Set(ZDOVars.s_emote, Plugin.RowEmote.Value);
				val2.Set(ZDOVars.s_emoteOneshot, true);
				_nextEmote = Time.time + Mathf.Max(0.5f, Plugin.RowEmoteSeconds.Value);
			}
		}
	}

	private static bool StartRowing(Spot spot)
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)localPlayer.GetControlledShip() != (Object)null || !Song.OnBoard(spot.Ship, localPlayer))
		{
			return false;
		}
		if (((Character)localPlayer).IsAttached())
		{
			((Character)localPlayer).AttachStop();
		}
		if ((Object)(object)spot.Hold != (Object)null && (Object)(object)spot.Hold != (Object)(object)((Component)spot).transform)
		{
			Object.Destroy((Object)(object)((Component)spot.Hold).gameObject);
		}
		GameObject val = new GameObject("SeaLegs_row_hold");
		val.transform.SetParent(((Component)spot.Ship).transform, false);
		val.transform.position = ((Component)localPlayer).transform.position;
		val.transform.localRotation = Quaternion.LookRotation(new Vector3((float)spot.Side, 0f, 0f), Vector3.up);
		spot.Hold = val.transform;
		((Character)localPlayer).AttachStart(spot.Hold, (GameObject)null, false, false, true, Plugin.RowAnimation.Value, new Vector3(0f, 0.3f, 0f), (Transform)null);
		if (!((Character)localPlayer).IsAttached())
		{
			Plugin.Debug("[row] the game did not hold you at the " + ((spot.Side < 0) ? "left" : "right") + " spot");
			return false;
		}
		_rowingAt = spot;
		_nextEmote = 0f;
		Plugin.Debug("[row] rowing to the " + ((spot.Side < 0) ? "left" : "right") + " on " + ((Object)spot.Ship).name);
		return true;
	}

	internal static void StopRowing()
	{
		if ((Object)(object)_rowingAt == (Object)null)
		{
			return;
		}
		Spot rowingAt = _rowingAt;
		_rowingAt = null;
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsAttached() && (Object)(object)rowingAt != (Object)null && (Object)(object)localPlayer.GetAttachPoint() == (Object)(object)rowingAt.Hold)
		{
			((Character)localPlayer).AttachStop();
		}
		if ((Object)(object)rowingAt != (Object)null && (Object)(object)rowingAt.Hold != (Object)null && (Object)(object)rowingAt.Hold != (Object)(object)((Component)rowingAt).transform)
		{
			Object.Destroy((Object)(object)((Component)rowingAt.Hold).gameObject);
			rowingAt.Hold = ((Component)rowingAt).transform;
		}
		ZDO val = Song.Zdo((Character)(object)localPlayer);
		if (val != null)
		{
			val.Set("SeaLegs_row", 0);
			if (val.GetString(ZDOVars.s_emote, "") == Plugin.RowEmote.Value)
			{
				val.Set(ZDOVars.s_emoteID, val.GetInt(ZDOVars.s_emoteID, 0) + 1, false);
				val.Set(ZDOVars.s_emote, "");
			}
		}
		Plugin.Debug("[row] stopped rowing");
	}

	internal static void Clear()
	{
		foreach (Ship item in new List<Ship>(Rigs.Keys))
		{
			Drop(item);
		}
	}

	private static void Drop(Ship ship)
	{
		if (!Rigs.TryGetValue(ship, out var value))
		{
			return;
		}
		Rigs.Remove(ship);
		if ((Object)(object)_rowingAt != (Object)null && ((Object)(object)value.Spots[0] == (Object)(object)_rowingAt || (Object)(object)value.Spots[1] == (Object)(object)_rowingAt))
		{
			StopRowing();
		}
		Spot[] spots = value.Spots;
		foreach (Spot spot in spots)
		{
			if ((Object)(object)spot != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)spot).gameObject);
			}
		}
	}

	private static void Build(Ship ship)
	{
		//IL_0025: 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_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_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: 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)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: 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_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Expected O, but got Unknown
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		Rig rig = new Rig();
		rig.Ship = ship;
		Rig rig2 = rig;
		Rigs[ship] = rig2;
		Transform transform = ((Component)ship).transform;
		Vector3 center = Vector3.zero;
		float half = 1.5f;
		BoxCollider floatCollider = ship.m_floatCollider;
		if ((Object)(object)floatCollider != (Object)null)
		{
			center = transform.InverseTransformPoint(((Component)floatCollider).transform.TransformPoint(floatCollider.center));
			half = Mathf.Abs(floatCollider.size.x * ((Component)floatCollider).transform.lossyScale.x) * 0.5f;
		}
		Rigidbody body = (Rigidbody)((ShipBody != null) ? /*isinst with value type is only supported in some contexts*/: null);
		Vector3[] array = RopeFeet(ship, center);
		Player localPlayer = Player.m_localPlayer;
		float deckY = (((Object)(object)localPlayer != (Object)null) ? transform.InverseTransformPoint(((Component)localPlayer).transform.position).y : center.y);
		float num = (((Object)(object)ship.m_mastObject != (Object)null) ? transform.InverseTransformPoint(ship.m_mastObject.transform.position).z : center.z);
		Vector3 val = default(Vector3);
		for (int i = 0; i < 2; i++)
		{
			int num2 = ((i != 0) ? 1 : (-1));
			bool flag = array[i] != Vector3.zero;
			float num3 = (flag ? array[i].z : num);
			float num4 = DeckEdge(ship, body, center.x, num3, num2, half, deckY);
			float num5 = center.x + (float)num2 * Mathf.Max(0.2f, num4 - 0.15f - Plugin.RowSpotInset.Value);
			((Vector3)(ref val))..ctor(num5, center.y, num3);
			val.y = Deck(ship, body, val);
			GameObject val2 = new GameObject("SeaLegs_row_" + ((num2 < 0) ? "left" : "right"));
			val2.transform.SetParent(transform, false);
			val2.transform.localPosition = val;
			val2.transform.localRotation = Quaternion.LookRotation(new Vector3((float)num2, 0f, 0f), Vector3.up);
			Spot spot = val2.AddComponent<Spot>();
			spot.Ship = ship;
			spot.Side = num2;
			spot.Hold = val2.transform;
			spot.Outer = Ring(val2.transform, "outer", out spot.OuterLine);
			spot.Inner = Ring(val2.transform, "inner", out spot.InnerLine);
			rig2.Spots[i] = spot;
			Plugin.Debug("[row] " + ((Object)ship).name + " " + ((num2 < 0) ? "left" : "right") + " spot at " + ((Vector3)(ref val)).ToString("0.00") + ", deck edge " + num4.ToString("0.00") + " m from the middle" + (flag ? (", rope foot " + ((Vector3)(ref array[i])).ToString("0.00")) : ", level with the mast"));
		}
	}

	private static float DeckEdge(Ship ship, Rigidbody body, float cx, float z, int side, float half, float deckY)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		float result = 0f;
		bool flag = false;
		for (float num = 0.3f; num < half + 1f; num += 0.1f)
		{
			float num2 = DeckHit(ship, body, new Vector3(cx + (float)side * num, deckY, z));
			if (float.IsNaN(num2))
			{
				if (flag)
				{
					break;
				}
				continue;
			}
			if (num2 > deckY + 0.45f)
			{
				break;
			}
			if (num2 < deckY - 0.8f)
			{
				if (flag)
				{
					break;
				}
			}
			else
			{
				flag = true;
				result = num;
			}
		}
		if (!flag)
		{
			return Mathf.Max(0.6f, half * 0.6f);
		}
		return result;
	}

	private static float DeckHit(Ship ship, Rigidbody body, Vector3 local)
	{
		//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_002d: 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_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)ship).transform;
		Vector3 val = transform.TransformPoint(new Vector3(local.x, local.y + 1.6f, local.z));
		float num = float.NaN;
		RaycastHit[] array = Physics.RaycastAll(val, -transform.up, 3.5f, -1, (QueryTriggerInteraction)1);
		for (int i = 0; i < array.Length; i++)
		{
			RaycastHit val2 = array[i];
			if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !(((RaycastHit)(ref val2)).normal.y < 0.6f) && (!((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody != (Object)(object)body) || !((Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Ship>() != (Object)(object)ship)) && !((Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Character>() != (Object)null))
			{
				float y = transform.InverseTransformPoint(((RaycastHit)(ref val2)).point).y;
				if (float.IsNaN(num) || y > num)
				{
					num = y;
				}
			}
		}
		return num;
	}

	private static float Deck(Ship ship, Rigidbody body, Vector3 local)
	{
		//IL_0008: 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_0013: 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_0022: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)ship).transform;
		Vector3 val = transform.TransformPoint(local + Vector3.up * 4f);
		float num = float.NegativeInfinity;
		RaycastHit[] array = Physics.RaycastAll(val, -transform.up, 8f, -1, (QueryTriggerInteraction)1);
		for (int i = 0; i < array.Length; i++)
		{
			RaycastHit val2 = array[i];
			if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !(((RaycastHit)(ref val2)).normal.y < 0.6f) && (!((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody != (Object)(object)body) || !((Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Ship>() != (Object)(object)ship)))
			{
				float y = transform.InverseTransformPoint(((RaycastHit)(ref val2)).point).y;
				if (y > num)
				{
					num = y;
				}
			}
		}
		if (!float.IsNegativeInfinity(num))
		{
			return num;
		}
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer != (Object)null))
		{
			return local.y;
		}
		return transform.InverseTransformPoint(((Component)localPlayer).transform.position).y;
	}

	private static Vector3[] RopeFeet(Ship ship, Vector3 center)
	{
		//IL_0031: 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_044a: Unknown result type (might be due to invalid IL or missing references)
		//IL_044f: Unknown result type (might be due to invalid IL or missing references)
		//IL_047a: Unknown result type (might be due to invalid IL or missing references)
		//IL_047f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: 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_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0495: Unknown result type (might be due to invalid IL or missing references)
		//IL_049a: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: 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_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0503: Unknown result type (might be due to invalid IL or missing references)
		//IL_0508: Unknown result type (might be due to invalid IL or missing references)
		//IL_0313: Unknown result type (might be due to invalid IL or missing references)
		//IL_0305: Unknown result type (might be due to invalid IL or missing references)
		//IL_035f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0364: Unknown result type (might be due to invalid IL or missing references)
		//IL_0367: Unknown result type (might be due to invalid IL or missing references)
		//IL_0369: Unknown result type (might be due to invalid IL or missing references)
		//IL_036e: Unknown result type (might be due to invalid IL or missing references)
		//IL_051b: Unknown result type (might be due to invalid IL or missing references)
		//IL_051d: Unknown result type (might be due to invalid IL or missing references)
		//IL_052b: Unknown result type (might be due to invalid IL or missing references)
		//IL_052d: Unknown result type (might be due to invalid IL or missing references)
		//IL_052f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0534: Unknown result type (might be due to invalid IL or missing references)
		//IL_0562: Unknown result type (might be due to invalid IL or missing references)
		//IL_0567: Unknown result type (might be due to invalid IL or missing references)
		//IL_056c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0333: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Unknown result type (might be due to invalid IL or missing references)
		//IL_033d: Unknown result type (might be due to invalid IL or missing references)
		//IL_039e: Unknown result type (might be due to invalid IL or missing references)
		Vector3[] array = (Vector3[])(object)new Vector3[2];
		Transform transform = ((Component)ship).transform;
		float num = (((Object)(object)ship.m_mastObject != (Object)null) ? transform.InverseTransformPoint(ship.m_mastObject.transform.position).z : center.z);
		List<Vector3>[] array2 = new List<Vector3>[2]
		{
			new List<Vector3>(),
			new List<Vector3>()
		};
		List<string> list = new List<string>();
		Renderer[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (((Object)val).name.StartsWith("SeaLegs"))
			{
				continue;
			}
			string text = ((Object)val).name;
			Mesh val2 = null;
			MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
			if ((Object)(object)component != (Object)null)
			{
				val2 = component.sharedMesh;
			}
			SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
			if ((Object)(object)val3 != (Object)null)
			{
				val2 = val3.sharedMesh;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				text = text + "/" + ((Object)val2).name;
			}
			if (text.ToLowerInvariant().IndexOf("rope") < 0)
			{
				continue;
			}
			LineRenderer val4 = (LineRenderer)(object)((val is LineRenderer) ? val : null);
			string[] array3 = new string[8]
			{
				text,
				" [",
				((object)val).GetType().Name,
				val.enabled ? "" : ", off",
				((Object)(object)val4 != (Object)null) ? (", " + val4.positionCount + " points") : (((Object)(object)val2 != (Object)null) ? (", " + (val2.isReadable ? (val2.vertexCount + " vertices") : "not readable")) : ""),
				", size ",
				null,
				null
			};
			Bounds bounds = val.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			array3[6] = ((Vector3)(ref size)).ToString("0.0");
			array3[7] = "]";
			list.Add(string.Concat(array3));
			List<Vector3> list2 = new List<Vector3>();
			LineRenderer val5 = (LineRenderer)(object)((val is LineRenderer) ? val : null);
			if ((Object)(object)val5 != (Object)null)
			{
				for (int j = 0; j < val5.positionCount; j++)
				{
					list2.Add(val5.useWorldSpace ? val5.GetPosition(j) : ((Component)val5).transform.TransformPoint(val5.GetPosition(j)));
				}
			}
			else if ((Object)(object)val2 != (Object)null && val2.isReadable)
			{
				Vector3[] vertices = val2.vertices;
				for (int k = 0; k < vertices.Length; k += Mathf.Max(1, vertices.Length / 400))
				{
					list2.Add(((Component)val).transform.TransformPoint(vertices[k]));
				}
			}
			else
			{
				Bounds bounds2 = val.bounds;
				for (int l = 0; l < 8; l++)
				{
					list2.Add(new Vector3(((l & 1) == 0) ? ((Bounds)(ref bounds2)).min.x : ((Bounds)(ref bounds2)).max.x, ((l & 2) == 0) ? ((Bounds)(ref bounds2)).min.y : ((Bounds)(ref bounds2)).max.y, ((l & 4) == 0) ? ((Bounds)(ref bounds2)).min.z : ((Bounds)(ref bounds2)).max.z));
				}
			}
			foreach (Vector3 item2 in list2)
			{
				Vector3 item = transform.InverseTransformPoint(item2);
				float num2 = item.x - center.x;
				if (!(Mathf.Abs(num2) < 0.4f))
				{
					array2[(!(num2 < 0f)) ? 1u : 0u].Add(item);
				}
			}
		}
		if (Listed.Add(((Object)ship).name))
		{
			Plugin.Debug("[row] " + ((Object)ship).name + " ropes: " + ((list.Count == 0) ? "none" : string.Join(", ", list.ToArray())));
		}
		for (int m = 0; m < 2; m++)
		{
			List<Vector3> list3 = array2[m];
			if (list3.Count == 0)
			{
				continue;
			}
			float num3 = float.MaxValue;
			foreach (Vector3 item3 in list3)
			{
				num3 = Mathf.Min(num3, item3.y);
			}
			Vector3 val6 = Vector3.zero;
			float num4 = float.MaxValue;
			foreach (Vector3 item4 in list3)
			{
				if (item4.y < num3 + 0.5f && Mathf.Abs(item4.z - num) < num4)
				{
					num4 = Mathf.Abs(item4.z - num);
					val6 = item4;
				}
			}
			Vector3 val7 = Vector3.zero;
			int num5 = 0;
			foreach (Vector3 item5 in list3)
			{
				if (item5.y < num3 + 0.5f && Vector3.Distance(item5, val6) < 0.8f)
				{
					val7 += item5;
					num5++;
				}
			}
			if (num5 > 0)
			{
				ref Vector3 reference = ref array[m];
				reference = val7 / (float)num5;
			}
		}
		return array;
	}

	private static Material Mat()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Expected O, but got Unknown
		if ((Object)(object)_mat != (Object)null)
		{
			return _mat;
		}
		Shader val = Shader.Find("Sprites/Default");
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("UI/Default");
		}
		_mat = new Material(val);
		((Object)_mat).hideFlags = (HideFlags)61;
		return _mat;
	}

	private static Transform Ring(Transform parent, string name, out LineRenderer lr)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name);
		val.transform.SetParent(parent, false);
		val.transform.localPosition = new Vector3(0f, 0.06f, 0f);
		lr = val.AddComponent<LineRenderer>();
		lr.useWorldSpace = false;
		lr.loop = true;
		((Renderer)lr).sharedMaterial = Mat();
		lr.widthMultiplier = 0.035f;
		((Renderer)lr).shadowCastingMode = (ShadowCastingMode)0;
		((Renderer)lr).receiveShadows = false;
		lr.positionCount = 40;
		for (int i = 0; i < 40; i++)
		{
			float num = (float)i * (float)Math.PI * 2f / 40f;
			lr.SetPosition(i, new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)));
		}
		return val.transform;
	}

	[HarmonyPatch(typeof(Player), "FindHoverObject")]
	[HarmonyPostfix]
	private static void Hover(Player __instance, ref GameObject hover)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.Enabled.Value || !Plugin.SideRowing.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || Rigs.Count == 0 || ((Object)(object)hover != (Object)null && hover.GetComponentInParent<Interactable>() != null && (Object)(object)hover.GetComponentInParent<Spot>() == (Object)null))
		{
			return;
		}
		Ship localShip = Ship.GetLocalShip();
		if ((Object)(object)localShip == (Object)null || !Rigs.TryGetValue(localShip, out var value) || !ShowFor(localShip))
		{
			return;
		}
		Vector3 position = ((Component)__instance).transform.position;
		Spot spot = null;
		float num = Plugin.RowSpotReach.Value;
		Spot[] spots = value.Spots;
		foreach (Spot spot2 in spots)
		{
			if ((Object)(object)spot2 == (Object)null)
			{
				continue;
			}
			Vector3 val = ((Component)spot2).transform.position - position;
			if (!(Mathf.Abs(val.y) > 1.5f))
			{
				val.y = 0f;
				float magnitude = ((Vector3)(ref val)).magnitude;
				if (magnitude < num)
				{
					num = magnitude;
					spot = spot2;
				}
			}
		}
		if ((Object)(object)spot != (Object)null)
		{
			hover = ((Component)spot).gameObject;
		}
	}

	[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
	[HarmonyPostfix]
	private static void Push(Ship __instance, float fixedDeltaTime)
	{
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: 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_0201: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.Enabled.Value || !Plugin.SideRowing.Value)
		{
			return;
		}
		ZNetView val = (ZNetView)((ShipView != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner())
		{
			return;
		}
		Rigidbody val2 = (Rigidbody)((ShipBody != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val2 == (Object)null || val2.isKinematic)
		{
			return;
		}
		List<Player> list = Song.Players(__instance);
		if (list == null || list.Count == 0)
		{
			return;
		}
		long num = (((Object)(object)__instance.m_shipControlls != (Object)null) ? __instance.m_shipControlls.GetUser() : 0);
		int num2 = 0;
		int num3 = 0;
		foreach (Player item in list)
		{
			if ((Object)(object)item == (Object)null || ((Character)item).IsDead() || item.GetPlayerID() == num)
			{
				continue;
			}
			ZDO val3 = Song.Zdo((Character)(object)item);
			if (val3 != null)
			{
				int num4 = val3.GetInt("SeaLegs_row", 0);
				if (num4 < 0)
				{
					num2++;
				}
				else if (num4 > 0)
				{
					num3++;
				}
			}
		}
		int num5 = Mathf.Max(1, Plugin.MaxRowers.Value);
		int num6 = Mathf.Min(num3, num5) - Mathf.Min(num2, num5);
		if (num6 == 0)
		{
			return;
		}
		Vector3 right = ((Component)__instance).transform.right;
		right.y = 0f;
		if (((Vector3)(ref right)).sqrMagnitude < 0.01f)
		{
			return;
		}
		((Vector3)(ref right)).Normalize();
		float num7 = Mathf.Sign((float)num6);
		float num8 = Mathf.Min(Plugin.MaxRowSpeed.Value, (float)Mathf.Abs(num6) * Plugin.RowSpeed.Value);
		float num9 = Vector3.Dot(val2.linearVelocity, right) * num7;
		if (!(num9 >= num8))
		{
			float num10 = Mathf.Min(Plugin.RowAcceleration.Value, (num8 - num9) * 3f);
			val2.AddForce(right * (num7 * num10), (ForceMode)5);
			if (Plugin.DetailedLog.Value && Time.time - _lastLog > 5f)
			{
				_lastLog = Time.time;
				Plugin.Log.LogInfo((object)("[row] " + ((Object)__instance).name + ": " + num2 + " rowing left, " + num3 + " right; sideways " + (num9 * num7).ToString("0.00") + " m/s, target " + (num8 * num7).ToString("0.00")));
			}
		}
	}
}
[HarmonyPatch]
internal static class Shanty
{
	private class State
	{
		public long Born;

		public long Start;

		public long Live;

		public string Ship = "";

		public string Queue = "";

		public int Index;

		public int Gen;

		public int Pool;

		public float Len;

		public string[] Names
		{
			get
			{
				if (Queue.Length != 0)
				{
					return Queue.Split('|');
				}
				return new string[0];
			}
		}

		public string Track
		{
			get
			{
				string[] names = Names;
				if (Index < 0 || Index >= names.Length)
				{
					return null;
				}
				return names[Index];
			}
		}

		public string Next
		{
			get
			{
				string[] names = Names;
				if (Index + 1 < 0 || Index + 1 >= names.Length)
				{
					return null;
				}
				return names[Index + 1];
			}
		}

		public bool Same(State o)
		{
			if (o != null && Born == o.Born && Start == o.Start && Live == o.Live && Ship == o.Ship && Queue == o.Queue && Index == o.Index && Gen == o.Gen && Pool == o.Pool)
			{
				return Len == o.Len;
			}
			return false;
		}

		public State Copy()
		{
			return (State)MemberwiseClone();
		}
	}

	internal enum Voice
	{
		Auto,
		Bass,
		Baritone,
		Tenor,
		Alto,
		MezzoSoprano
	}

	private const string KBorn = "SeaLegs_born";

	private const string KShip = "SeaLegs_ship";

	private const string KQueue = "SeaLegs_queue";

	private const string KIndex = "SeaLegs_index";

	private const string KStart = "SeaLegs_start";

	private const string KLen = "SeaLegs_len";

	private const string KLive = "SeaLegs_live";

	private const string KGen = "SeaLegs_gen";

	private const string KPool = "SeaLegs_pool";

	private const string KVoice = "SeaLegs_voicelevel";

	private const float NoVoice = -999f;

	private const float WomenSong = 4.5f;

	private const float MaxShift = 3f;

	private static State _me;

	private static State _written;

	private static float _writtenVoice = -999f;

	private static float _nextThink;

	private static Ship _ship;

	private static string _shipId;

	private static int _singers;

	private static bool _leading;

	private static readonly FieldInfo MusicSource = AccessTools.Field(typeof(MusicMan), "m_musicSource");

	private static readonly FieldInfo SfxMixer = AccessTools.Field(typeof(AudioMan), "m_ambientMixer");

	private static readonly List<SongLib.Track> Sung = new List<SongLib.Track>();

	private static readonly List<SongLib.Track> Breaks = new List<SongLib.Track>();

	private static AudioSource _main;

	private static readonly Dictionary<Player, AudioSource> Layers = new Dictionary<Player, AudioSource>();

	private static readonly List<Player> Gone = new List<Player>();

	private static float _fade;

	private static string _shown;

	private static readonly float[] Lag = new float[10] { 0f, 1f, -1f, 0.5f, -0.5f, 0.75f, -0.25f, 0.25f, -0.75f, 0.1f };

	private static long Timeout => (long)(Mathf.Max(5f, Plugin.ReshuffleSeconds.Value) * 1000f);

	private static long Now()
	{
		if (!((Object)(object)ZNet.instance != (Object)null))
		{
			return 0L;
		}
		return (long)(ZNet.instance.GetTimeSeconds() * 1000.0);
	}

	internal static void Update()
	{
		if (!Plugin.SongSound.Value)
		{
			StopAudio();
			return;
		}
		if (Time.time >= _nextThink)
		{
			_nextThink = Time.time + 0.25f;
			Think();
		}
		Audio();
	}

	private static void Think()
	{
		Player localPlayer = Player.m_localPlayer;
		_ship = null;
		_singers = 0;
		_leading = false;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null)
		{
			return;
		}
		long num = Now();
		if (_me != null && num - _me.Live > Timeout)
		{
			Plugin.Debug("[shanty] nobody sang for " + Timeout / 1000 + " s: the playlist ends, the next song starts a new shuffle");
			_me = null;
		}
		WriteVoice(localPlayer);
		Ship localShip = Ship.GetLocalShip();
		if (((Character)localPlayer).IsDead() || (Object)(object)localShip == (Object)null || !Song.OnBoard(localShip, localPlayer))
		{
			Write(localPlayer);
			return;
		}
		string text = ShipId(localShip);
		if (text == null)
		{
			Write(localPlayer);
			return;
		}
		_ship = localShip;
		_shipId = text;
		_singers = Song.Singers(localShip);
		State state = null;
		long num2 = 0L;
		List<Player> list = Song.Players(localShip);
		if (list != null)
		{
			foreach (Player item in list)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				State state2 = (((Object)(object)item == (Object)(object)localPlayer) ? _me : Read(Song.Zdo((Character)(object)item)));
				if (state2 != null && !(state2.Ship != text) && state2.Born > 0 && state2.Queue.Length != 0 && num - state2.Live <= Timeout)
				{
					long playerID = item.GetPlayerID();
					if (state == null || state2.Born < state.Born || (state2.Born == state.Born && (state2.Gen > state.Gen || (state2.Gen == state.Gen && playerID < num2))))
					{
						state = state2;
						num2 = playerID;
					}
				}
			}
		}
		if (state == null)
		{
			if (_singers > 0 && SongLib.HasAny)
			{
				State state3 = new State();
				state3.Born = num;
				state3.Live = num;
				state3.Ship = text;
				state3.Gen = 1;
				_me = state3;
				Shuffle(localShip, null);
				Plugin.Debug("[shanty] new playlist for " + ((Object)localShip).name + ": " + _me.Queue.Replace("|", ", "));
				if (_me.Queue.Length == 0)
				{
					_me = null;
				}
			}
			else if (_me != null && _me.Ship != text)
			{
				_me = null;
			}
		}
		else if (state != _me)
		{
			if (_me == null || _me.Born != state.Born)
			{
				Plugin.Debug("[shanty] joined the playlist already playing on " + ((Object)localShip).name);
			}
			_me = state.Copy();
		}
		_leading = _me != null && (state == null || state == _me || num2 == localPlayer.GetPlayerID());
		if (_leading && _me != null)
		{
			Lead(localShip, num);
		}
		Write(localPlayer);
	}

	private static void Lead(Ship ship, long now)
	{
		if (_singers > 0 && now - _me.Live > 1000)
		{
			_me.Live = now;
		}
		string track = _me.Track;
		if (track == null)
		{
			Shuffle(ship, null);
		}
		else if (_me.Start == 0)
		{
			SongLib.Track track2 = SongLib.Find(track);
			if (track2 == null || SongLib.HasFailed(track2))
			{
				Advance(ship, track);
				return;
			}
			AudioClip val = SongLib.Get(track2);
			if (!((Object)(object)val == (Object)null) && SongLib.Ready(track2))
			{
				_me.Start = now + 400;
				_me.Len = val.length;
				_me.Gen++;
				Plugin.Debug("[shanty] leading: song " + (_me.Index + 1) + "/" + _me.Names.Length + " " + track);
			}
		}
		else if (now >= _me.Start + (long)(_me.Len * 1000f) + (long)(Plugin.SongGapSeconds.Value * 1000f))
		{
			Advance(ship, track);
		}
	}

	private static void Advance(Ship ship, string last)
	{
		_me.Index++;
		_me.Start = 0L;
		_me.Len = 0f;
		_me.Gen++;
		if (_me.Index >= _me.Names.Length || Pool(ship) != _me.Pool)
		{
			if (_me.Index < _me.Names.Length)
			{
				Plugin.Debug("[shanty] the crew changed: new shuffle for it");
			}
			Shuffle(ship, last);
		}
	}

	private static int Pool(Ship ship)
	{
		bool flag = false;
		bool flag2 = false;
		List<Player> list = Song.Players(ship);
		if (list != null)
		{
			foreach (Player item in list)
			{
				ZDO val = Song.Zdo((Character)(object)item);
				if (val != null)
				{
					if (val.GetInt(ZDOVars.s_modelIndex, 0) == 1)
					{
						flag2 = true;
					}
					else
					{
						flag = true;
					}
				}
			}
		}
		if (!flag || flag2)
		{
			if (!flag2 || flag)
			{
				return 3;
			}
			return 2;
		}
		return 1;
	}

	private static void Shuffle(Ship ship, string last)
	{
		int num = Pool(ship);
		SongLib.ForCrew(num != 2, num != 1, Sung, Breaks);
		Mix(Sung);
		Mix(Breaks);
		if (last != null && Sung.Count > 1 && Sung[0].Name == last)
		{
			SongLib.Track item = Sung[0];
			Sung.RemoveAt(0);
			Sung.Add(item);
		}
		List<string> list = new List<string>();
		int num2 = 0;
		for (int i = 0; i < Sung.Count; i++)
		{
			list.Add(Sung[i].Name);
			if (i % 2 == 1 && Breaks.Count > 0)
			{
				list.Add(Breaks[num2++ % Breaks.Count].Name);
			}
		}
		if (Sung.Count == 0)
		{
			foreach (SongLib.Track @break in Breaks)
			{
				list.Add(@break.Name);
			}
		}
		_me.Queue = string.Join("|", list.ToArray());
		_me.Index = 0;
		_me.Start = 0L;
		_me.Len = 0f;
		_me.Poo