Decompiled source of VikingOarsmen v3.4.0

VikingOarsmen.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Audio;
using UnityEngine.Playables;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("VikingOarsmen")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("3.4.0.0")]
[assembly: AssemblyInformationalVersion("3.4.0+ba7e82957bb61b594d446ab35d60c6216da9b1db")]
[assembly: AssemblyProduct("VikingOarsmen")]
[assembly: AssemblyTitle("VikingOarsmen")]
[assembly: AssemblyVersion("3.4.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace VikingOarsmen
{
	internal class CrewHud : MonoBehaviour
	{
		private class Marker
		{
			public RectTransform Oar;

			public RectTransform Ring;

			public Image Token;

			public RectTransform Helmet;
		}

		private static readonly Vector2 s_panelSize = new Vector2(180f, 340f);

		private const float PanelX = 110f;

		private static readonly Vector2 s_shipArea = new Vector2(138f, 232f);

		private const float ShipAreaY = -21f;

		private const float EndLabelOffset = 124f;

		private const float FlipMargin = 0.2f;

		private const float IconSize = 18f;

		private const float CountFontSize = 22f;

		private const float CaptionFontSize = 10f;

		private static readonly Color s_countColor = Color32.op_Implicit(new Color32((byte)58, (byte)34, (byte)14, byte.MaxValue));

		private static readonly Color s_captionColor = Color32.op_Implicit(new Color32((byte)92, (byte)58, (byte)28, byte.MaxValue));

		private const float MaxToken = 16f;

		private const float MinToken = 11f;

		private const float TokenPerMeter = 1.6f;

		private static readonly Vector2 s_oarSize = new Vector2(32f, 6f);

		private const float RingScale = 1.5f;

		private const float SwingDegrees = 30f;

		private static readonly Color s_passengerColor = new Color(0.55f, 0.55f, 0.55f);

		private readonly List<Player> _aboard = new List<Player>();

		private readonly List<Marker> _markers = new List<Marker>();

		private Hud _hud;

		private RectTransform _root;

		private RectTransform _view;

		private Image _ship;

		private RectTransform _crew;

		private TextMeshProUGUI _topLabel;

		private TextMeshProUGUI _bottomLabel;

		private bool _sternUp;

		private TextMeshProUGUI _rowingCount;

		private TextMeshProUGUI _aboardCount;

		private int _shownRowers = -1;

		private int _shownAboard = -1;

		private void Start()
		{
			//IL_0088: 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_009a: 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_00d9: 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_00f8: 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)
			_hud = ((Component)this).GetComponent<Hud>();
			if ((Object)(object)CrewHudArt.Get("frame") == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Crew panel artwork couldn't be loaded; the crew panel is disabled.");
				return;
			}
			Transform parent = (((Object)(object)_hud.m_rootObject != (Object)null) ? _hud.m_rootObject.transform : ((Component)this).transform);
			_root = NewRect("VikingOarsmen_CrewHud", parent);
			RectTransform root = _root;
			RectTransform root2 = _root;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 0.5f);
			root2.anchorMax = val;
			root.anchorMin = val;
			_root.sizeDelta = s_panelSize;
			_root.anchoredPosition = new Vector2(110f, 0f);
			Image obj = NewImage("Frame", (Transform)(object)_root, "frame");
			((Graphic)obj).rectTransform.anchorMin = Vector2.zero;
			((Graphic)obj).rectTransform.anchorMax = Vector2.one;
			((Graphic)obj).rectTransform.sizeDelta = Vector2.zero;
			_rowingCount = BuildCounter(24f, "icon_oar", "ROWING");
			_aboardCount = BuildCounter(102f, "icon_crew", "ABOARD");
			_view = NewRect("View", (Transform)(object)_root);
			_view.anchoredPosition = new Vector2(0f, -21f);
			_ship = NewImage("Ship", (Transform)(object)_view, null);
			_crew = NewRect("Crew", (Transform)(object)_view);
			_topLabel = NewEndLabel("Top", 124f);
			_bottomLabel = NewEndLabel("Bottom", -124f);
			ApplyOrientation();
			((Component)_root).gameObject.SetActive(false);
		}

		private void LateUpdate()
		{
			//IL_00c1: 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_0146: 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_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: 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_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: 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_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			if (!Plugin.ShowCrewHud.Value || !TryCollectAboard(out var ship, out var shipId))
			{
				((Component)_root).gameObject.SetActive(false);
				return;
			}
			CrewHudArt.ShipArt shipArt = CrewHudArt.For(ship);
			float num = shipArt.ZMax - shipArt.ZMin;
			float num2 = Mathf.Min(s_shipArea.y / num, s_shipArea.x / (shipArt.HalfWidth * 2f));
			float num3 = (shipArt.ZMin + shipArt.ZMax) / 2f;
			_ship.sprite = CrewHudArt.Get(shipArt.Sprite);
			((Graphic)_ship).rectTransform.sizeDelta = new Vector2(shipArt.HalfWidth * 2f * num2, num * num2);
			UpdateOrientation(ship);
			((Component)_root).gameObject.SetActive(true);
			float num4 = Mathf.Clamp(num2 * 1.6f, 11f, 16f);
			AnimationClip stroke = ModAssets.RowStroke(mirrored: false);
			int num5 = 0;
			Vector2 anchoredPosition = default(Vector2);
			for (int i = 0; i < _aboard.Count; i++)
			{
				Player val = _aboard[i];
				Marker marker = GetMarker(i);
				Transform attachPoint = val.GetAttachPoint();
				Vector3 val2 = ((Component)ship).transform.InverseTransformPoint(((Object)(object)attachPoint != (Object)null) ? attachPoint.position : ((Component)val).transform.position);
				((Vector2)(ref anchoredPosition))..ctor(val2.x * num2, (val2.z - num3) * num2);
				bool flag = RowingController.GetRowingShip(val) == shipId;
				RowingGear gear = (flag ? RowingController.GetRowingGear(val) : RowingGear.Stop);
				if (flag)
				{
					num5++;
				}
				((Component)marker.Ring).gameObject.SetActive((Object)(object)val == (Object)(object)Player.m_localPlayer);
				marker.Ring.anchoredPosition = anchoredPosition;
				marker.Ring.sizeDelta = Vector2.one * num4 * 1.5f;
				((Graphic)marker.Token).rectTransform.anchoredPosition = anchoredPosition;
				((Graphic)marker.Token).rectTransform.sizeDelta = Vector2.one * num4;
				((Graphic)marker.Token).color = (flag ? GearColor(gear) : s_passengerColor);
				marker.Helmet.anchoredPosition = anchoredPosition;
				marker.Helmet.sizeDelta = Vector2.one * num4;
				((Component)marker.Oar).gameObject.SetActive(flag);
				marker.Oar.anchoredPosition = anchoredPosition;
				marker.Oar.sizeDelta = s_oarSize * (num4 / 16f);
				((Transform)marker.Oar).localRotation = Quaternion.Euler(0f, 0f, OarAngle(gear, (val2.x < 0f) ? (-1f) : 1f, stroke));
			}
			for (int j = _aboard.Count; j < _markers.Count; j++)
			{
				HideMarker(_markers[j]);
			}
			if (num5 != _shownRowers || _aboard.Count != _shownAboard)
			{
				_shownRowers = num5;
				_shownAboard = _aboard.Count;
				((TMP_Text)_rowingCount).text = num5.ToString();
				((TMP_Text)_aboardCount).text = _aboard.Count.ToString();
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_root).gameObject);
			}
		}

		private bool TryCollectAboard(out Ship ship, out ZDOID shipId)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			_aboard.Clear();
			ship = null;
			shipId = ZDOID.None;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !RowingController.IsRowingMode(localPlayer))
			{
				return false;
			}
			shipId = RowingController.GetRowingShip(localPlayer);
			ship = ((Component)localPlayer).GetComponent<OarVisual>()?.RowingShip;
			if (shipId == ZDOID.None || (Object)(object)ship == (Object)null)
			{
				return false;
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && (ship.IsPlayerInBoat(allPlayer) || RowingController.GetRowingShip(allPlayer) == shipId))
				{
					_aboard.Add(allPlayer);
				}
			}
			return true;
		}

		private void UpdateOrientation(Ship ship)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			Vector3 val = Vector3.ProjectOnPlane(((Component)mainCamera).transform.forward, ((Component)ship).transform.up);
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
			{
				float num = Vector3.Dot(((Vector3)(ref val)).normalized, ((Component)ship).transform.forward);
				bool flag = num < -0.2f || (_sternUp && num < 0.2f);
				if (flag != _sternUp)
				{
					_sternUp = flag;
					ApplyOrientation();
				}
			}
		}

		private void ApplyOrientation()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			((Transform)_view).localRotation = Quaternion.Euler(0f, 0f, _sternUp ? 180f : 0f);
			((TMP_Text)_topLabel).text = (_sternUp ? "STERN" : "BOW");
			((TMP_Text)_bottomLabel).text = (_sternUp ? "BOW" : "STERN");
		}

		private TextMeshProUGUI NewEndLabel(string name, float y)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI obj = NewText(name, 10f, s_captionColor);
			((TMP_Text)obj).rectTransform.anchoredPosition = new Vector2(0f, -21f + y);
			((TMP_Text)obj).rectTransform.sizeDelta = new Vector2(80f, 14f);
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj).characterSpacing = 4f;
			return obj;
		}

		private static float OarAngle(RowingGear gear, float side, AnimationClip stroke)
		{
			float num = ((side > 0f) ? 0f : 180f);
			float num2 = (((Object)(object)stroke != (Object)null) ? RowingBeat.StrokeRate(gear, stroke.length) : 0f);
			if (num2 <= 0f)
			{
				return num;
			}
			float num3 = Mathf.Sin(RowingBeat.GetBeat(num2) * (float)Math.PI * 2f) * 30f;
			if (gear == RowingGear.Back)
			{
				num3 = 0f - num3;
			}
			return num + num3 * side;
		}

		private static Color GearColor(RowingGear gear)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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)
			return (Color)(gear switch
			{
				RowingGear.Back => new Color(1f, 0.55f, 0.15f), 
				RowingGear.Slow => new Color(0.45f, 0.85f, 0.4f), 
				RowingGear.Half => new Color(1f, 0.9f, 0.3f), 
				RowingGear.Full => new Color(0.95f, 0.3f, 0.25f), 
				_ => new Color(0.7f, 0.7f, 0.7f), 
			});
		}

		private TextMeshProUGUI BuildCounter(float x, string icon, string caption)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			PlaceTopLeft(((Graphic)NewImage(icon, (Transform)(object)_root, icon)).rectTransform, x, 13f, new Vector2(18f, 18f));
			TextMeshProUGUI obj = NewText("Count", 22f, s_countColor);
			PlaceTopLeft(((TMP_Text)obj).rectTransform, x + 18f + 4f, 9f, new Vector2(40f, 26f));
			TextMeshProUGUI obj2 = NewText("Caption", 10f, s_captionColor);
			PlaceTopLeft(((TMP_Text)obj2).rectTransform, x, 32f, new Vector2(70f, 14f));
			((TMP_Text)obj2).text = caption;
			((TMP_Text)obj2).characterSpacing = 4f;
			return obj;
		}

		private TextMeshProUGUI NewText(string name, float size, Color color)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI val = ((Component)NewRect(name, (Transform)(object)_root)).gameObject.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)_hud.m_hoverName != (Object)null)
			{
				((TMP_Text)val).font = ((TMP_Text)_hud.m_hoverName).font;
			}
			((TMP_Text)val).fontSize = size;
			((TMP_Text)val).fontStyle = (FontStyles)1;
			((Graphic)val).color = color;
			((TMP_Text)val).alignment = (TextAlignmentOptions)513;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
			((Graphic)val).raycastTarget = false;
			return val;
		}

		private Marker GetMarker(int index)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			while (_markers.Count <= index)
			{
				Marker marker = new Marker();
				marker.Oar = ((Graphic)NewImage("Oar", (Transform)(object)_crew, "oar")).rectTransform;
				marker.Oar.pivot = new Vector2(0f, 0.5f);
				marker.Ring = ((Graphic)NewImage("Self", (Transform)(object)_crew, "self_ring")).rectTransform;
				marker.Token = NewImage("Player", (Transform)(object)_crew, "token");
				marker.Helmet = ((Graphic)NewImage("Helmet", (Transform)(object)_crew, "helmet")).rectTransform;
				_markers.Add(marker);
			}
			Marker marker2 = _markers[index];
			((Component)marker2.Token).gameObject.SetActive(true);
			((Component)marker2.Helmet).gameObject.SetActive(true);
			return marker2;
		}

		private static void HideMarker(Marker marker)
		{
			((Component)marker.Oar).gameObject.SetActive(false);
			((Component)marker.Ring).gameObject.SetActive(false);
			((Component)marker.Token).gameObject.SetActive(false);
			((Component)marker.Helmet).gameObject.SetActive(false);
		}

		private static void PlaceTopLeft(RectTransform rect, float x, float y, Vector2 size)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			rect.anchorMax = val;
			rect.anchorMin = val;
			rect.pivot = new Vector2(0f, 1f);
			rect.anchoredPosition = new Vector2(x, 0f - y);
			rect.sizeDelta = size;
		}

		private static Image NewImage(string name, Transform parent, string sprite)
		{
			Image val = ((Component)NewRect(name, parent)).gameObject.AddComponent<Image>();
			if (sprite != null)
			{
				val.sprite = CrewHudArt.Get(sprite);
			}
			((Graphic)val).raycastTarget = false;
			return val;
		}

		private static RectTransform NewRect(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			component.anchorMax = val;
			component.anchorMin = val;
			return component;
		}
	}
	internal static class CrewHudArt
	{
		internal class ShipArt
		{
			internal readonly string Sprite;

			internal readonly float HalfWidth;

			internal readonly float ZMin;

			internal readonly float ZMax;

			internal ShipArt(string sprite, float halfWidth, float zMin, float zMax)
			{
				Sprite = sprite;
				HalfWidth = halfWidth;
				ZMin = zMin;
				ZMax = zMax;
			}
		}

		private const string ResourcePrefix = "VikingOarsmen.Textures.";

		private const string FallbackShip = "ship_longship";

		private static readonly Dictionary<string, ShipArt> s_ships = new Dictionary<string, ShipArt>
		{
			{
				"Raft",
				new ShipArt("ship_raft", 3.6f, -3.6f, 3.6f)
			},
			{
				"Karve",
				new ShipArt("ship_karve", 1.95f, -6.27f, 6.34f)
			},
			{
				"VikingShip",
				new ShipArt("ship_longship", 3.35f, -12.2f, 12.2f)
			},
			{
				"Trailership",
				new ShipArt("ship_longship", 3.35f, -12.2f, 12.2f)
			},
			{
				"VikingShip_Ashlands",
				new ShipArt("ship_drakkar", 6.6f, -18.8f, 19.8f)
			}
		};

		private static readonly Dictionary<string, Sprite> s_sprites = new Dictionary<string, Sprite>();

		private static readonly MethodInfo s_loadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[2]
		{
			typeof(Texture2D),
			typeof(byte[])
		});

		internal static ShipArt For(Ship ship)
		{
			//IL_0041: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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)
			if (s_ships.TryGetValue(Utils.GetPrefabName(((Component)ship).gameObject), out var value))
			{
				return value;
			}
			BoxCollider floatCollider = ship.m_floatCollider;
			if ((Object)(object)floatCollider == (Object)null)
			{
				return s_ships["VikingShip"];
			}
			return new ShipArt("ship_longship", Mathf.Max(floatCollider.size.x, 0.5f) / 2f, floatCollider.center.z - Mathf.Max(floatCollider.size.z, 0.5f) / 2f, floatCollider.center.z + Mathf.Max(floatCollider.size.z, 0.5f) / 2f);
		}

		internal static Sprite Get(string name)
		{
			if (s_sprites.TryGetValue(name, out var value))
			{
				return value;
			}
			value = Load(name);
			s_sprites[name] = value;
			return value;
		}

		private static Sprite Load(string name)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_0125: 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)
			using Stream stream = typeof(CrewHudArt).Assembly.GetManifestResourceStream("VikingOarsmen.Textures." + name + ".png");
			if (stream == null)
			{
				Plugin.Log.LogError((object)("Embedded texture '" + name + ".png' not found in the DLL."));
				return null;
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num <= 0)
				{
					break;
				}
			}
			if (s_loadImage == null)
			{
				Plugin.Log.LogError((object)"UnityEngine.ImageConversion.LoadImage not found; the crew panel's textures can't be decoded.");
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!(bool)s_loadImage.Invoke(null, new object[2] { val, array }))
			{
				Plugin.Log.LogError((object)("Embedded texture '" + name + ".png' couldn't be decoded."));
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "VikingOarsmen_" + name;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
	}
	internal class GearHud : MonoBehaviour
	{
		private const float HeightAboveGunwale = 0.3f;

		private Hud _hud;

		private GameObject _root;

		private Transform _controlsRoot;

		private GameObject _slow;

		private GameObject _half;

		private GameObject _full;

		private GameObject _back;

		private void Start()
		{
			_hud = ((Component)this).GetComponent<Hud>();
			if (!Build())
			{
				Plugin.Log.LogWarning((object)"Ship HUD layout not recognized; the gear indicator is disabled.");
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
					_root = null;
				}
			}
		}

		private bool Build()
		{
			GameObject shipHudRoot = _hud.m_shipHudRoot;
			if ((Object)(object)shipHudRoot == (Object)null)
			{
				return false;
			}
			_root = Object.Instantiate<GameObject>(shipHudRoot, shipHudRoot.transform.parent);
			((Object)_root).name = "VikingOarsmen_GearHud";
			_root.SetActive(false);
			_controlsRoot = FindCounterpart(_hud.m_shipControlsRoot);
			_slow = FindCounterpartObject(_hud.m_rudderSlow);
			_half = FindCounterpartObject(_hud.m_rudderForward);
			_full = FindCounterpartObject(_hud.m_rudderFastForward);
			_back = FindCounterpartObject(_hud.m_rudderBackward);
			if ((Object)(object)_controlsRoot == (Object)null || (Object)(object)_slow == (Object)null || (Object)(object)_half == (Object)null || (Object)(object)_full == (Object)null || (Object)(object)_back == (Object)null || (Object)(object)FindCounterpart((Component)(object)_hud.m_shipRudderIcon) == (Object)null)
			{
				return false;
			}
			Hide(_hud.m_rudderLeft);
			Hide(_hud.m_rudderRight);
			Hide(_hud.m_halfSail);
			Hide(_hud.m_fullSail);
			Hide(_hud.m_rudder);
			Hide((Component)(object)_hud.m_shipWindIndicatorRoot);
			Hide((Component)(object)_hud.m_shipRudderIndicator);
			return true;
		}

		private void LateUpdate()
		{
			//IL_00c3: 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_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_00d8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !RowingController.IsRowingMode(localPlayer))
			{
				_root.SetActive(false);
				return;
			}
			Camera mainCamera = Utils.GetMainCamera();
			OarVisual component = ((Component)localPlayer).GetComponent<OarVisual>();
			if ((Object)(object)mainCamera == (Object)null || (Object)(object)component == (Object)null || !component.TryGetGunwalePoint(out var point))
			{
				_root.SetActive(false);
				return;
			}
			RowingGear selectedGear = RowingController.GetSelectedGear();
			_root.SetActive(true);
			_slow.SetActive(selectedGear == RowingGear.Slow);
			_half.SetActive(selectedGear == RowingGear.Half);
			_full.SetActive(selectedGear == RowingGear.Full);
			_back.SetActive(selectedGear == RowingGear.Back);
			_controlsRoot.position = Utils.WorldToScreenPointScaled(mainCamera, point + Vector3.up * 0.3f);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
		}

		private Transform FindCounterpart(Component original)
		{
			if (!((Object)(object)original != (Object)null))
			{
				return null;
			}
			return FindCounterpart(original.gameObject);
		}

		private Transform FindCounterpart(GameObject original)
		{
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			Transform transform = _hud.m_shipHudRoot.transform;
			Transform transform2 = original.transform;
			if ((Object)(object)transform2 == (Object)(object)transform)
			{
				return _root.transform;
			}
			string text = ((Object)transform2).name;
			transform2 = transform2.parent;
			while ((Object)(object)transform2 != (Object)null && (Object)(object)transform2 != (Object)(object)transform)
			{
				text = ((Object)transform2).name + "/" + text;
				transform2 = transform2.parent;
			}
			if (!((Object)(object)transform2 == (Object)(object)transform))
			{
				return null;
			}
			return _root.transform.Find(text);
		}

		private GameObject FindCounterpartObject(GameObject original)
		{
			Transform val = FindCounterpart(original);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).gameObject;
		}

		private void Hide(Component original)
		{
			if ((Object)(object)original != (Object)null)
			{
				Hide(original.gameObject);
			}
		}

		private void Hide(GameObject original)
		{
			Transform val = FindCounterpart(original);
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).gameObject.SetActive(false);
			}
		}
	}
	internal static class ModAssets
	{
		private const string BundleName = "vikingoarsmen";

		private const string RowIdleName = "row_idle";

		private const string RowStrokeName = "row_stroke";

		private const string MirrorSuffix = "_mirror";

		private static bool s_loaded;

		private static AssetBundle s_bundle;

		private static AnimationClip s_rowIdle;

		private static AnimationClip s_rowStroke;

		private static AnimationClip s_rowIdleMirror;

		private static AnimationClip s_rowStrokeMirror;

		internal static GameObject LoadPrefab(string name)
		{
			Load();
			if (!((Object)(object)s_bundle != (Object)null))
			{
				return null;
			}
			return s_bundle.LoadAsset<GameObject>(name);
		}

		internal static Sprite LoadSprite(string name)
		{
			Load();
			if (!((Object)(object)s_bundle != (Object)null))
			{
				return null;
			}
			return s_bundle.LoadAsset<Sprite>(name);
		}

		internal static AnimationClip RowIdle(bool mirrored)
		{
			Load();
			if (!mirrored)
			{
				return s_rowIdle;
			}
			return s_rowIdleMirror;
		}

		internal static AnimationClip RowStroke(bool mirrored)
		{
			Load();
			if (!mirrored)
			{
				return s_rowStroke;
			}
			return s_rowStrokeMirror;
		}

		private static void Load()
		{
			if (s_loaded)
			{
				return;
			}
			s_loaded = true;
			Assembly assembly = typeof(ModAssets).Assembly;
			string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith("vikingoarsmen"));
			if (text == null)
			{
				Plugin.Log.LogError((object)"Embedded AssetBundle 'vikingoarsmen' not found in the DLL.");
				return;
			}
			byte[] array;
			using (Stream stream = assembly.GetManifestResourceStream(text))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			AssetBundle val = AssetBundle.LoadFromMemory(array);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Failed to load the AssetBundle 'vikingoarsmen' (built for another platform or Unity version?).");
				return;
			}
			s_bundle = val;
			s_rowIdle = val.LoadAsset<AnimationClip>("row_idle");
			s_rowStroke = val.LoadAsset<AnimationClip>("row_stroke");
			s_rowIdleMirror = val.LoadAsset<AnimationClip>("row_idle_mirror");
			s_rowStrokeMirror = val.LoadAsset<AnimationClip>("row_stroke_mirror");
			if ((Object)(object)s_rowIdle == (Object)null || (Object)(object)s_rowStroke == (Object)null || (Object)(object)s_rowIdleMirror == (Object)null || (Object)(object)s_rowStrokeMirror == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Rowing animations missing from the AssetBundle; rowers will just sit.");
			}
		}
	}
	internal static class ModSounds
	{
		private const string ResourcePrefix = "VikingOarsmen.Sounds.";

		private static AudioClip[] s_single;

		private static AudioClip[] s_multiple;

		private static AudioMixerGroup s_sfxGroup;

		private static bool s_sfxGroupResolved;

		internal static AudioClip[] Single
		{
			get
			{
				if (s_single == null)
				{
					s_single = LoadAll("single", "single2");
				}
				return s_single;
			}
		}

		internal static AudioClip[] Multiple
		{
			get
			{
				if (s_multiple == null)
				{
					s_multiple = LoadAll("multiple", "multiple2");
				}
				return s_multiple;
			}
		}

		internal static AudioMixerGroup SfxGroup
		{
			get
			{
				if (!s_sfxGroupResolved)
				{
					ResolveSfxGroup();
				}
				return s_sfxGroup;
			}
		}

		private static void ResolveSfxGroup()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)AudioMan.instance == (Object)null)
			{
				return;
			}
			s_sfxGroupResolved = true;
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null) && ((Object)prefab).name.StartsWith("sfx_"))
				{
					AudioSource componentInChildren = prefab.GetComponentInChildren<AudioSource>(true);
					if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.outputAudioMixerGroup != (Object)null)
					{
						s_sfxGroup = componentInChildren.outputAudioMixerGroup;
						Plugin.Log.LogInfo((object)("Rowing sound plays through mixer group '" + ((Object)s_sfxGroup).name + "' (from " + ((Object)prefab).name + ")."));
						return;
					}
				}
			}
			AudioMixer masterMixer = AudioMan.instance.m_masterMixer;
			AudioMixerGroup[] array = (((Object)(object)masterMixer != (Object)null) ? masterMixer.FindMatchingGroups("SFX") : null);
			if (array != null && array.Length != 0)
			{
				s_sfxGroup = array[0];
				Plugin.Log.LogWarning((object)("No vanilla sound effect to copy the mixer group from; using '" + ((Object)s_sfxGroup).name + "'."));
			}
			else
			{
				Plugin.Log.LogWarning((object)"Game's sound effects mixer group not found; the rowing sound ignores the effects volume.");
			}
		}

		private static AudioClip[] LoadAll(params string[] names)
		{
			return (from clip in names.Select(Load)
				where (Object)(object)clip != (Object)null
				select clip).ToArray();
		}

		private static AudioClip Load(string name)
		{
			using Stream stream = typeof(ModSounds).Assembly.GetManifestResourceStream("VikingOarsmen.Sounds." + name + ".wav");
			if (stream == null)
			{
				Plugin.Log.LogError((object)("Embedded sound '" + name + ".wav' not found in the DLL."));
				return null;
			}
			using BinaryReader reader = new BinaryReader(stream);
			return Decode(name, reader);
		}

		private static AudioClip Decode(string name, BinaryReader reader)
		{
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Expected O, but got Unknown
			//IL_0195: Expected O, but got Unknown
			if (ReadId(reader) != "RIFF")
			{
				return Reject(name, "not a RIFF file");
			}
			reader.ReadInt32();
			if (ReadId(reader) != "WAVE")
			{
				return Reject(name, "not a WAVE file");
			}
			int num = 0;
			bool flag = false;
			while (reader.BaseStream.Position + 8 <= reader.BaseStream.Length)
			{
				string text = ReadId(reader);
				int num2 = reader.ReadInt32();
				long position = reader.BaseStream.Position + num2 + (num2 & 1);
				if (text == "fmt ")
				{
					short num3 = reader.ReadInt16();
					short num4 = reader.ReadInt16();
					num = reader.ReadInt32();
					reader.ReadInt32();
					reader.ReadInt16();
					short num5 = reader.ReadInt16();
					flag = num3 == 1 && num4 == 1 && num5 == 16;
					if (!flag)
					{
						return Reject(name, $"format {num3}, {num4} channels, {num5} bit; expected 16-bit mono PCM");
					}
				}
				else if (text == "data")
				{
					if (!flag)
					{
						return Reject(name, "data before fmt chunk");
					}
					int count = num2 / 2;
					float[] samples = new float[count];
					for (int i = 0; i < count; i++)
					{
						samples[i] = (float)reader.ReadInt16() / 32768f;
					}
					int position2 = 0;
					return AudioClip.Create("VikingOarsmen_" + name, count, 1, num, false, (PCMReaderCallback)delegate(float[] data)
					{
						int num6 = Mathf.Clamp(count - position2, 0, data.Length);
						Array.Copy(samples, position2, data, 0, num6);
						Array.Clear(data, num6, data.Length - num6);
						position2 += num6;
					}, (PCMSetPositionCallback)delegate(int newPosition)
					{
						position2 = newPosition;
					});
				}
				reader.BaseStream.Position = position;
			}
			return Reject(name, "no data chunk");
		}

		private static string ReadId(BinaryReader reader)
		{
			return Encoding.ASCII.GetString(reader.ReadBytes(4));
		}

		private static AudioClip Reject(string name, string reason)
		{
			Plugin.Log.LogWarning((object)("Embedded sound '" + name + ".wav' can't be read (" + reason + "); it won't play."));
			return null;
		}
	}
	internal static class OarItem
	{
		private class OarKind
		{
			internal string PrefabName;

			internal string ModelPrefab;

			internal string Icon;

			internal string Name;

			internal string Description;

			internal int StationLevel;

			internal RequirementConfig[] Requirements;

			internal Vector3 BackPosition;

			internal Quaternion BackRotation = Quaternion.identity;

			internal float Weight = 4f;

			internal float Durability = 50f;

			internal float Knockback = 50f;

			internal float AttackStamina = 12f;

			internal float AttackRange;

			internal float Blunt;

			internal float BluntMaxQuality;

			internal float BladeTipZ = 2.8f;
		}

		private static readonly Vector3 s_backPosition = new Vector3(0f, 0f, -0.8708f);

		private static readonly Vector3 s_drakkarBackPosition = new Vector3(0.0883f, 1.2553f, -2.2153f);

		private static readonly Quaternion s_drakkarBackRotation = new Quaternion(0.091977f, 0.220199f, -0.921992f, 0.304931f);

		private static readonly OarKind s_oar = new OarKind
		{
			PrefabName = "VikingOarsmen_Oar",
			ModelPrefab = "VikingOarsmen_Oar",
			Icon = "oar_icon",
			Name = "Oar",
			Description = "A viking oar. Equip it and sit on a ship's bench to row.",
			StationLevel = 1,
			Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
			{
				new RequirementConfig("FineWood", 6, 0, true)
			},
			BackPosition = s_backPosition
		};

		private static readonly OarKind s_drakkarOar = new OarKind
		{
			PrefabName = "VikingOarsmen_DrakkarOar",
			ModelPrefab = "VikingOarsmen_DrakkarOar",
			Icon = "drakkar_oar_icon",
			Name = "Drakkar Oar",
			Description = "A long oar for the Drakkar's high benches, the only one that reaches the water from them. Equip it and sit on a ship's bench to row.",
			StationLevel = 2,
			Requirements = (RequirementConfig[])(object)new RequirementConfig[2]
			{
				new RequirementConfig("YggdrasilWood", 16, 0, true),
				new RequirementConfig("Resin", 8, 0, true)
			},
			BackPosition = s_drakkarBackPosition,
			BackRotation = s_drakkarBackRotation,
			Weight = 8f,
			Durability = 90f,
			Knockback = 300f,
			AttackStamina = 15f,
			AttackRange = 6f,
			Blunt = 70f,
			BluntMaxQuality = 90f
		};

		private static readonly OarKind[] s_kinds = new OarKind[2] { s_oar, s_drakkarOar };

		private const float Weight = 4f;

		private const float Durability = 50f;

		private const float Knockback = 50f;

		private const float BackstabBonus = 2f;

		private const float StaggerMultiplier = 1.5f;

		private const float AttackStamina = 12f;

		private const float AttackAdrenaline = 1f;

		private const float RangeMultiplier = 2f;

		private const float AttackDurationMultiplier = 1.75f;

		internal const float SwingSpeed = 0.5714286f;

		private const string SwingSource = "Battleaxe";

		private const float HandOffsetZ = -0.4f;

		private const float RowingGripZ = -0.08f;

		private const float RowingGripRoll = -90f;

		private const float DefaultBladeTipZ = 2.8f;

		internal static void SetRowingGrip(GameObject handInstance, bool rowing)
		{
			//IL_0060: 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_003a: 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)
			Transform val = (((Object)(object)handInstance != (Object)null) ? handInstance.transform.Find("model") : null);
			if (!((Object)(object)val == (Object)null))
			{
				if (!rowing)
				{
					val.localPosition = new Vector3(0f, 0f, -0.4f);
					val.localRotation = Quaternion.identity;
				}
				else
				{
					val.localPosition = new Vector3(0f, 0f, -0.08f);
					val.localRotation = Quaternion.Euler(0f, 0f, -90f);
				}
			}
		}

		internal static bool IsOar(ItemData item)
		{
			return KindOf(item) != null;
		}

		internal static bool IsDrakkarOar(ItemData item)
		{
			return KindOf(item) == s_drakkarOar;
		}

		internal static float BladeTipZ(ItemData item)
		{
			return KindOf(item)?.BladeTipZ ?? 2.8f;
		}

		internal static bool IsOarHash(int prefabHash)
		{
			return KindOf(prefabHash) != null;
		}

		internal static float BladeTipZ(int prefabHash)
		{
			return KindOf(prefabHash)?.BladeTipZ ?? 2.8f;
		}

		private static OarKind KindOf(int prefabHash)
		{
			OarKind[] array = s_kinds;
			foreach (OarKind oarKind in array)
			{
				if (StringExtensionMethods.GetStableHashCode(oarKind.PrefabName) == prefabHash)
				{
					return oarKind;
				}
			}
			return null;
		}

		private static OarKind KindOf(ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return null;
			}
			string name = ((Object)item.m_dropPrefab).name;
			OarKind[] array = s_kinds;
			foreach (OarKind oarKind in array)
			{
				if (oarKind.PrefabName == name)
				{
					return oarKind;
				}
			}
			return null;
		}

		internal static void Setup()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Create;
		}

		private static void Create()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Create;
			OarKind[] array = s_kinds;
			for (int i = 0; i < array.Length; i++)
			{
				Create(array[i]);
			}
		}

		private static void Create(OarKind kind)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0028: 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_0041: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			ItemConfig val = new ItemConfig
			{
				Name = kind.Name,
				Description = kind.Description,
				CraftingStation = CraftingStations.Workbench,
				MinStationLevel = kind.StationLevel,
				Weight = kind.Weight
			};
			RequirementConfig[] requirements = kind.Requirements;
			foreach (RequirementConfig val2 in requirements)
			{
				val.AddRequirement(val2);
			}
			CustomItem val3 = new CustomItem(kind.PrefabName, "Club", val);
			if ((Object)(object)val3.ItemPrefab == (Object)null)
			{
				Plugin.Log.LogError((object)("Failed to create the " + kind.Name + " item: couldn't clone 'Club' (not loaded yet?)."));
				return;
			}
			SharedData shared = val3.ItemDrop.m_itemData.m_shared;
			shared.m_maxDurability = kind.Durability;
			shared.m_attackForce = kind.Knockback;
			if (kind.Blunt > 0f)
			{
				SetBluntDamage(shared, kind.Blunt, kind.BluntMaxQuality);
			}
			shared.m_backstabBonus = 2f;
			MakeTwoHanded(shared);
			Attack attack = shared.m_attack;
			attack.m_attackStamina = kind.AttackStamina;
			attack.m_attackAdrenaline = 1f;
			attack.m_staggerMultiplier = 1.5f;
			attack.m_attackRange = ((kind.AttackRange > 0f) ? kind.AttackRange : (attack.m_attackRange * 2f));
			ApplyOarVisual(val3.ItemPrefab, kind);
			Sprite val4 = ModAssets.LoadSprite(kind.Icon);
			if ((Object)(object)val4 == (Object)null)
			{
				Plugin.Log.LogWarning((object)(kind.Name + " item: icon '" + kind.Icon + "' not in the bundle; rendering one from the model."));
				val4 = RenderManager.Instance.Render(val3.ItemPrefab);
			}
			if ((Object)(object)val4 != (Object)null)
			{
				shared.m_icons = (Sprite[])(object)new Sprite[1] { val4 };
			}
			if (!ItemManager.Instance.AddItem(val3))
			{
				Plugin.Log.LogError((object)("Failed to register the " + kind.Name + " item with Jötunn."));
			}
			else
			{
				Plugin.Log.LogInfo((object)($"{kind.Name} item registered: range {attack.m_attackRange:F2}, swing speed x{0.5714286f:F2}, " + $"durability {shared.m_maxDurability:F0}, knockback {shared.m_attackForce:F0}, " + $"blunt {shared.m_damages.m_blunt:F0} (+{shared.m_damagesPerLevel.m_blunt:F1}/level, max quality {shared.m_maxQuality})."));
			}
		}

		private static void SetBluntDamage(SharedData shared, float blunt, float bluntMaxQuality)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.Max(shared.m_maxQuality - 1, 0);
			shared.m_damages = new DamageTypes
			{
				m_blunt = blunt
			};
			shared.m_damagesPerLevel = new DamageTypes
			{
				m_blunt = ((num > 0) ? ((bluntMaxQuality - blunt) / (float)num) : 0f)
			};
		}

		private static void MakeTwoHanded(SharedData shared)
		{
			//IL_004b: 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)
			GameObject prefab = PrefabManager.Instance.GetPrefab("Battleaxe");
			ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Oar item: 'Battleaxe' not found; keeping the Club's one-handed swings.");
				return;
			}
			SharedData shared2 = val.m_itemData.m_shared;
			shared.m_itemType = (ItemType)14;
			shared.m_animationState = shared2.m_animationState;
			CopySwing(shared2.m_attack, shared.m_attack);
			CopySwing(shared2.m_secondaryAttack, shared.m_secondaryAttack);
		}

		private static void CopySwing(Attack source, Attack target)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			target.m_attackType = source.m_attackType;
			target.m_attackAnimation = source.m_attackAnimation;
			target.m_attackRandomAnimations = source.m_attackRandomAnimations;
			target.m_attackChainLevels = source.m_attackChainLevels;
			target.m_attackOriginJoint = source.m_attackOriginJoint;
			target.m_attackAngle = source.m_attackAngle;
			target.m_attackRayWidth = source.m_attackRayWidth;
			target.m_attackHeight = source.m_attackHeight;
			target.m_attackOffset = source.m_attackOffset;
		}

		private static void ApplyOarVisual(GameObject itemPrefab, OarKind kind)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: 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_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ModAssets.LoadPrefab(kind.ModelPrefab);
			Transform val2 = (((Object)(object)val != (Object)null) ? val.transform : null);
			Transform val3 = (((Object)(object)val2 != (Object)null) ? val2.Find("attach") : null);
			Transform val4 = itemPrefab.transform.Find("attach");
			Transform val5 = (((Object)(object)val4 != (Object)null) ? val4.Find("model") : null);
			if ((Object)(object)val3 == (Object)null || (Object)(object)val5 == (Object)null)
			{
				Plugin.Log.LogWarning((object)(kind.Name + " item: bundle prefab or the Club's attach/model not found; keeping the Club's own mesh."));
				return;
			}
			Mesh sharedMesh = ((Component)val3).GetComponent<MeshFilter>().sharedMesh;
			MeshRenderer component = ((Component)val5).GetComponent<MeshRenderer>();
			Material val6 = new Material(((Renderer)((Component)val3).GetComponent<MeshRenderer>()).sharedMaterial)
			{
				shader = ((Renderer)component).sharedMaterial.shader
			};
			((Component)val5).GetComponent<MeshFilter>().sharedMesh = sharedMesh;
			((Renderer)component).sharedMaterials = (Material[])(object)new Material[1] { val6 };
			Vector3 val7 = default(Vector3);
			((Vector3)(ref val7))..ctor(0f, 0f, -0.4f);
			val5.localPosition = val7;
			val5.localRotation = Quaternion.identity;
			AddBackAttach(itemPrefab.transform, val4, val5, kind);
			BoxCollider componentInChildren = ((Component)val4).GetComponentInChildren<BoxCollider>(true);
			Bounds bounds;
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((Component)componentInChildren).transform.localPosition = val7;
				bounds = sharedMesh.bounds;
				componentInChildren.center = ((Bounds)(ref bounds)).center;
				bounds = sharedMesh.bounds;
				componentInChildren.size = ((Bounds)(ref bounds)).size;
			}
			foreach (Transform item in val3)
			{
				Transform val8 = item;
				Transform transform = new GameObject(((Object)val8).name).transform;
				transform.SetParent(val4, false);
				transform.localPosition = val8.localPosition + val7;
				transform.localRotation = val8.localRotation;
			}
			Transform val9 = val4.Find("equiped/trail");
			Transform val10 = (((Object)(object)val9 != (Object)null) ? val9.Find("base") : null);
			Transform val11 = (((Object)(object)val9 != (Object)null) ? val9.Find("tip") : null);
			if ((Object)(object)val10 != (Object)null && (Object)(object)val11 != (Object)null)
			{
				val10.localPosition = val3.Find("fulcrum").localPosition + val7;
				val11.localPosition = val3.Find("blade_tip").localPosition + val7;
			}
			Transform val12 = val3.Find("blade_tip");
			if ((Object)(object)val12 != (Object)null)
			{
				kind.BladeTipZ = val12.localPosition.z;
			}
			ManualLogSource log = Plugin.Log;
			object[] obj = new object[4]
			{
				kind.Name,
				((Object)sharedMesh).name,
				null,
				null
			};
			bounds = sharedMesh.bounds;
			obj[2] = ((Bounds)(ref bounds)).size.z;
			obj[3] = ((Object)val6.shader).name;
			log.LogInfo((object)string.Format("{0} item: model '{1}' ({2:F2} m) from the bundle, shader '{3}'.", obj));
		}

		private static void AddBackAttach(Transform itemRoot, Transform attach, Transform model, OarKind kind)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("attach_back");
			val.SetActive(false);
			val.transform.SetParent(itemRoot, false);
			val.transform.localPosition = attach.localPosition;
			val.transform.localRotation = attach.localRotation;
			val.transform.SetSiblingIndex(attach.GetSiblingIndex());
			Transform obj = Object.Instantiate<Transform>(model, val.transform, false);
			((Object)obj).name = ((Object)model).name;
			obj.localPosition = kind.BackPosition;
			obj.localRotation = kind.BackRotation;
		}
	}
	internal static class OarSplash
	{
		private static readonly string[] s_sourceProjectiles = new string[3] { "bow_projectile", "bow_projectile_fire", "bow_projectile_frost" };

		private static readonly FieldInfo s_hitWaterEffectsField = AccessTools.Field(typeof(Projectile), "m_hitWaterEffects");

		private static readonly GameObject[] s_none = (GameObject[])(object)new GameObject[0];

		private static GameObject[] s_prefabs;

		internal static void Play(Vector3 position)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Splash.Value)
			{
				GameObject[] prefabs = GetPrefabs();
				for (int i = 0; i < prefabs.Length; i++)
				{
					Object.Instantiate<GameObject>(prefabs[i], position, Quaternion.identity);
				}
			}
		}

		private static GameObject[] GetPrefabs()
		{
			if (s_prefabs != null)
			{
				return s_prefabs;
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return s_none;
			}
			string[] array = s_sourceProjectiles;
			foreach (string text in array)
			{
				if (TryGetSplash(instance.GetPrefab(text), out s_prefabs))
				{
					return s_prefabs;
				}
			}
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab != (Object)null && (((Object)prefab).name.StartsWith("bow_projectile") || ((Object)prefab).name.ToLowerInvariant().Contains("arrow")) && TryGetSplash(prefab, out s_prefabs))
				{
					return s_prefabs;
				}
			}
			Plugin.Log.LogWarning((object)"No arrow water splash found in the game; the oar won't splash.");
			s_prefabs = s_none;
			return s_prefabs;
		}

		private static bool TryGetSplash(GameObject projectilePrefab, out GameObject[] prefabs)
		{
			prefabs = null;
			Projectile val = (((Object)(object)projectilePrefab != (Object)null) ? projectilePrefab.GetComponent<Projectile>() : null);
			EffectList val2 = (EffectList)(((Object)(object)val != (Object)null && s_hitWaterEffectsField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (val2 == null || val2.m_effectPrefabs == null)
			{
				return false;
			}
			List<GameObject> list = new List<GameObject>();
			EffectData[] effectPrefabs = val2.m_effectPrefabs;
			foreach (EffectData val3 in effectPrefabs)
			{
				if (val3 != null && val3.m_enabled && (Object)(object)val3.m_prefab != (Object)null && (Object)(object)val3.m_prefab.GetComponent<ZNetView>() == (Object)null)
				{
					list.Add(val3.m_prefab);
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			prefabs = list.ToArray();
			Plugin.Log.LogInfo((object)("Oar splash: the " + ((Object)projectilePrefab).name + " water hit effect (" + string.Join(", ", list.ConvertAll((GameObject p) => ((Object)p).name)) + ")."));
			return true;
		}
	}
	internal static class OarSwing
	{
		private static readonly FieldInfo s_characterField = AccessTools.Field(typeof(CharacterAnimEvent), "m_character");

		private static readonly FieldInfo s_animatorField = AccessTools.Field(typeof(CharacterAnimEvent), "m_animator");

		private static readonly HashSet<CharacterAnimEvent> s_slowed = new HashSet<CharacterAnimEvent>();

		internal static void OnFixedUpdate(CharacterAnimEvent animEvent)
		{
			if (!TryGetOarAttackAnimator(animEvent, out var animator))
			{
				s_slowed.Remove(animEvent);
			}
			else if (s_slowed.Add(animEvent))
			{
				Animator obj = animator;
				obj.speed *= 0.5714286f;
			}
		}

		internal static void OnSpeedSet(CharacterAnimEvent animEvent)
		{
			if (s_slowed.Contains(animEvent) && TryGetOarAttackAnimator(animEvent, out var animator))
			{
				Animator obj = animator;
				obj.speed *= 0.5714286f;
			}
		}

		private static bool TryGetOarAttackAnimator(CharacterAnimEvent animEvent, out Animator animator)
		{
			animator = null;
			if (s_characterField == null || s_animatorField == null)
			{
				return false;
			}
			object? value = s_characterField.GetValue(animEvent);
			Humanoid val = (Humanoid)((value is Humanoid) ? value : null);
			if ((Object)(object)val == (Object)null || !((Character)val).IsOwner() || !((Character)val).InAttack() || !OarItem.IsOar(val.GetCurrentWeapon()))
			{
				return false;
			}
			object? value2 = s_animatorField.GetValue(animEvent);
			animator = (Animator)((value2 is Animator) ? value2 : null);
			return (Object)(object)animator != (Object)null;
		}
	}
	internal class OarVisual : MonoBehaviour
	{
		private const float GunwaleProbeTop = 1.5f;

		private const float GunwaleProbeBottom = -0.5f;

		private const float GunwaleProbeStep = 0.1f;

		private const float HullProbeDistance = 10f;

		private const float HullThickness = 0.15f;

		private static readonly List<Vector3> s_hullHits = new List<Vector3>();

		private const float FallbackSideOffset = 0.6f;

		private const float FallbackHeight = 0.6f;

		private const float RelocateDistance = 0.3f;

		private const float DefaultSeaLevel = 30f;

		private const float NoWater = -1000f;

		private static readonly FieldInfo s_rightItemInstanceField = AccessTools.Field(typeof(VisEquipment), "m_rightItemInstance");

		private static readonly FieldInfo s_rightItemHashField = AccessTools.Field(typeof(VisEquipment), "m_currentRightItemHash");

		private Player _player;

		private VisEquipment _visEquipment;

		private RowerAnimation _animation;

		private Ship _ship;

		private ZDOID _shipId = ZDOID.None;

		private ZDOID _missingShipLogged = ZDOID.None;

		private bool _fitted;

		private Vector3 _localRowerPosition;

		private Vector3 _localGunwalePoint;

		private Transform _turnedVisual;

		private ShipFit _fit;

		private float _side = 1f;

		private Transform _spine;

		private Quaternion _spinePosed;

		private Quaternion _spineLeant;

		private GameObject _rowingGripInstance;

		private bool _rowingGripMirrored;

		private bool _bladeWet;

		private bool _bladeTracked;

		private WaterVolume _waterVolume;

		internal Ship RowingShip => ResolveRowingShip();

		private void Awake()
		{
			_player = ((Component)this).GetComponent<Player>();
			_visEquipment = ((Component)this).GetComponent<VisEquipment>();
			_animation = new RowerAnimation(_player);
		}

		private void Update()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Ship val = ResolveRowingShip();
			bool flag = (Object)(object)val != (Object)null;
			bool mirrored = flag && ((Component)val).transform.InverseTransformPoint(((Component)this).transform.position).x < 0f;
			_animation.Update(flag, flag ? RowingController.GetRowingGear(_player) : RowingGear.Stop, mirrored);
			UpdateGrip(flag, mirrored);
		}

		private void LateUpdate()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Ship ship = _ship;
			if ((Object)(object)ship == (Object)null)
			{
				_fitted = false;
				FaceForward();
				return;
			}
			Vector3 val = ((Component)ship).transform.InverseTransformPoint(((Component)this).transform.position);
			if (_fitted)
			{
				Vector3 val2 = val - _localRowerPosition;
				if (!(((Vector3)(ref val2)).sqrMagnitude > 0.09f))
				{
					goto IL_0060;
				}
			}
			FitToShip(ship, val);
			goto IL_0060;
			IL_0060:
			if (Plugin.DebugSkipFaceStern.Value)
			{
				FaceForward();
			}
			else
			{
				FaceStern(ship);
			}
			if (!Plugin.DebugSkipLean.Value)
			{
				LeanOverGunwale(ship);
			}
			TrackSplash();
		}

		private void OnDestroy()
		{
			FaceForward();
			_animation.Destroy();
		}

		private void TrackSplash()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)_rowingGripInstance != (Object)null) ? _rowingGripInstance.transform.Find("model") : null);
			if ((Object)(object)val == (Object)null)
			{
				_bladeTracked = false;
				return;
			}
			Vector3 val2 = val.TransformPoint(0f, 0f, OarItem.BladeTipZ(HeldItemHash()));
			float num = WaterLevel(val2);
			bool flag = val2.y < num;
			if (flag && !_bladeWet && _bladeTracked)
			{
				OarSplash.Play(new Vector3(val2.x, num, val2.z));
			}
			_bladeWet = flag;
			_bladeTracked = true;
		}

		private float WaterLevel(Vector3 world)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
			float waterLevel = Floating.GetWaterLevel(new Vector3(world.x, num, world.z), ref _waterVolume);
			if (!(waterLevel > -1000f))
			{
				return num;
			}
			return waterLevel;
		}

		private void UpdateGrip(bool rowing, bool mirrored)
		{
			GameObject val = (GameObject)((s_rightItemInstanceField != null && (Object)(object)_visEquipment != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (rowing && (Object)(object)val != (Object)null && OarItem.IsOarHash(HeldItemHash()))
			{
				if ((Object)(object)val != (Object)(object)_rowingGripInstance || mirrored != _rowingGripMirrored)
				{
					OarItem.SetRowingGrip(val, rowing: true);
					Transform val2 = (mirrored ? _visEquipment.m_leftHand : _visEquipment.m_rightHand);
					if ((Object)(object)val2 != (Object)null)
					{
						val.transform.SetParent(val2, false);
					}
					_rowingGripInstance = val;
					_rowingGripMirrored = mirrored;
					if (Plugin.DebugRowingSync.Value)
					{
						Plugin.Log.LogInfo((object)("[sync] " + _player.GetPlayerName() + ": oar in the rowing grip, " + (mirrored ? "left" : "right") + " hand"));
					}
				}
			}
			else
			{
				if ((Object)(object)_rowingGripInstance != (Object)null)
				{
					ReleaseGrip(_rowingGripInstance);
				}
				_rowingGripInstance = null;
			}
		}

		private int HeldItemHash()
		{
			if (s_rightItemHashField != null && (Object)(object)_visEquipment != (Object)null)
			{
				object value = s_rightItemHashField.GetValue(_visEquipment);
				if (value is int)
				{
					return (int)value;
				}
			}
			return 0;
		}

		private void ReleaseGrip(GameObject instance)
		{
			OarItem.SetRowingGrip(instance, rowing: false);
			if ((Object)(object)_visEquipment != (Object)null && (Object)(object)_visEquipment.m_rightHand != (Object)null)
			{
				instance.transform.SetParent(_visEquipment.m_rightHand, false);
			}
		}

		internal bool TryGetGunwalePoint(out Vector3 point)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!_fitted || (Object)(object)_ship == (Object)null)
			{
				point = Vector3.zero;
				return false;
			}
			point = ((Component)_ship).transform.TransformPoint(_localGunwalePoint);
			return true;
		}

		private Ship ResolveRowingShip()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00c5: 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)
			if ((Object)(object)_player == (Object)null)
			{
				return null;
			}
			ZDOID rowingShip = RowingController.GetRowingShip(_player);
			if (rowingShip == ZDOID.None)
			{
				_shipId = ZDOID.None;
				_ship = null;
				return null;
			}
			if (rowingShip != _shipId || (Object)(object)_ship == (Object)null)
			{
				_shipId = rowingShip;
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(rowingShip) : null);
				_ship = (((Object)(object)val != (Object)null) ? val.GetComponent<Ship>() : null);
				_fitted = false;
			}
			if ((Object)(object)_ship == (Object)null && Plugin.DebugRowingSync.Value && rowingShip != _missingShipLogged)
			{
				_missingShipLogged = rowingShip;
				Plugin.Log.LogInfo((object)$"[sync] {_player.GetPlayerName()}: rowing ship {rowingShip} not found in this client's scene");
			}
			return _ship;
		}

		private void FitToShip(Ship ship, Vector3 localRower)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_006e: 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_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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			_localRowerPosition = localRower;
			float num = (_side = ((localRower.x >= 0f) ? 1f : (-1f)));
			_fit = ShipFit.For(ship);
			float halfWidth;
			float top;
			bool flag = TryFindGunwale(ship, localRower, num, out halfWidth, out top);
			_localGunwalePoint = (Vector3)(flag ? new Vector3(num * halfWidth, top, localRower.z) : (localRower + new Vector3(num * 0.6f, 0.6f, 0f)));
			Plugin.Log.LogInfo((object)($"Rower on {((Object)ship).name}: at {localRower:F2}, gunwale " + (flag ? $"{halfWidth:F2} wide, top {top:F2}" : "not found") + " (ship space)."));
			_fitted = true;
		}

		private void FaceStern(Ship ship)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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)
			GameObject visual = ((Character)_player).GetVisual();
			if (!((Object)(object)visual == (Object)null))
			{
				Vector3 up = ((Component)this).transform.up;
				Vector3 val = Vector3.ProjectOnPlane(-((Component)ship).transform.forward, up);
				if (!(((Vector3)(ref val)).sqrMagnitude < 1E-06f))
				{
					_turnedVisual = visual.transform;
					_turnedVisual.rotation = Quaternion.LookRotation(val, up);
					float num = ((_fit != null) ? _fit.Outboard : 0f);
					_turnedVisual.position = ((Component)this).transform.position + ((Component)ship).transform.right * (_side * num);
				}
			}
		}

		private void LeanOverGunwale(Ship ship)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			float num = ((_fit != null) ? _fit.Lean : 0f);
			if (num != 0f && TryGetSpine(out var spine))
			{
				if (spine.localRotation != _spineLeant)
				{
					_spinePosed = spine.localRotation;
				}
				spine.localRotation = _spinePosed;
				Vector3 val = ((Component)ship).transform.right * _side;
				Vector3 val2 = Vector3.Cross(((Component)ship).transform.up, val);
				spine.rotation = Quaternion.AngleAxis(num, val2) * spine.rotation;
				_spineLeant = spine.localRotation;
			}
		}

		private bool TryGetSpine(out Transform spine)
		{
			if ((Object)(object)_spine == (Object)null)
			{
				GameObject visual = ((Character)_player).GetVisual();
				Animator val = (((Object)(object)visual != (Object)null) ? visual.GetComponentInChildren<Animator>() : null);
				_spine = (((Object)(object)val != (Object)null && val.isHuman) ? val.GetBoneTransform((HumanBodyBones)7) : null);
			}
			spine = _spine;
			return (Object)(object)spine != (Object)null;
		}

		private void FaceForward()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_turnedVisual != (Object)null)
			{
				_turnedVisual.localRotation = Quaternion.identity;
				_turnedVisual.localPosition = Vector3.zero;
			}
			_turnedVisual = null;
		}

		private bool TryFindGunwale(Ship ship, Vector3 localRower, float side, out float halfWidth, out float top)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_00d2: 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_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)ship).transform;
			Rigidbody component = ((Component)ship).GetComponent<Rigidbody>();
			Transform ignore = (((Object)(object)ship.m_rudderObject != (Object)null) ? ship.m_rudderObject.transform : null);
			Vector3 direction = transform.TransformDirection(Vector3.left * side);
			s_hullHits.Clear();
			Vector3 val = default(Vector3);
			for (float num = 1.5f; num >= -0.5f; num -= 0.1f)
			{
				((Vector3)(ref val))..ctor(side * 10f, localRower.y + num, localRower.z);
				if (TryHitHull(transform.TransformPoint(val), direction, component, ignore, out var point))
				{
					s_hullHits.Add(transform.InverseTransformPoint(point));
				}
			}
			halfWidth = -1f;
			top = float.MinValue;
			foreach (Vector3 s_hullHit in s_hullHits)
			{
				halfWidth = Mathf.Max(halfWidth, Mathf.Abs(s_hullHit.x));
			}
			foreach (Vector3 s_hullHit2 in s_hullHits)
			{
				if (Mathf.Abs(s_hullHit2.x) > halfWidth - 0.15f)
				{
					top = Mathf.Max(top, s_hullHit2.y);
				}
			}
			return s_hullHits.Count > 0;
		}

		private static bool TryHitHull(Vector3 origin, Vector3 direction, Rigidbody shipBody, Transform ignore, out Vector3 point)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_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_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)
			point = Vector3.zero;
			float num = float.MaxValue;
			RaycastHit[] array = Physics.RaycastAll(origin, direction, 10f, -5, (QueryTriggerInteraction)1);
			for (int i = 0; i < array.Length; i++)
			{
				RaycastHit val = array[i];
				if (!((Object)(object)((RaycastHit)(ref val)).collider.attachedRigidbody != (Object)(object)shipBody) && !(((RaycastHit)(ref val)).distance >= num) && (!((Object)(object)ignore != (Object)null) || !((RaycastHit)(ref val)).transform.IsChildOf(ignore)))
				{
					num = ((RaycastHit)(ref val)).distance;
					point = ((RaycastHit)(ref val)).point;
				}
			}
			return num < float.MaxValue;
		}
	}
	[BepInPlugin("com.fabenejr.vikingoarsmen", "Viking Oarsmen", "3.4.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.fabenejr.vikingoarsmen";

		public const string PluginName = "Viking Oarsmen";

		public const string PluginVersion = "3.4.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> PowerPerRower;

		internal static ConfigEntry<float> Gear2Multiplier;

		internal static ConfigEntry<float> Gear3Multiplier;

		internal static ConfigEntry<float> ReverseMultiplier;

		internal static ConfigEntry<float> MaxRowingPower;

		internal static ConfigEntry<float> MaxRowingSpeed;

		internal static ConfigEntry<float> CrewFalloff;

		internal static ConfigEntry<float> SteeringMultiplier;

		internal static ConfigEntry<float> RampUpTime;

		internal static ConfigEntry<float> StaminaDrainAmount;

		internal static ConfigEntry<float> StaminaDrainIntervalSlow;

		internal static ConfigEntry<float> StaminaDrainIntervalHalf;

		internal static ConfigEntry<float> StaminaDrainIntervalFull;

		internal static ConfigEntry<bool> ShowOarHint;

		internal static ConfigEntry<float> StrokeSpeed;

		internal static ConfigEntry<bool> Splash;

		internal static ConfigEntry<bool> ShowCrewHud;

		internal static ConfigEntry<bool> RowingSound;

		internal static ConfigEntry<float> RowingSoundVolume;

		internal static ConfigEntry<bool> DebugRowingSync;

		internal static ConfigEntry<bool> DebugSkipFaceStern;

		internal static ConfigEntry<bool> DebugSkipLean;

		private Harmony _harmony;

		private ConfigEntry<T> BindSynced<T>(string section, string key, T defaultValue, string description, AcceptableValueBase range = null)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		private void Awake()
		{
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Expected O, but got Unknown
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PowerPerRower = BindSynced("Physics", "PowerPerRower", 0.08f, "Share of the maximum thrust a rower in Slow gear (1) adds. 0.25 = four rowers in Slow gear reach full speed.");
			Gear2Multiplier = BindSynced("Physics", "Gear2Multiplier", 1.4f, "Thrust in Half gear (2), as a multiple of PowerPerRower.");
			Gear3Multiplier = BindSynced("Physics", "Gear3Multiplier", 1.8f, "Thrust in Full gear (3), as a multiple of PowerPerRower.");
			ReverseMultiplier = BindSynced("Physics", "ReverseMultiplier", -1f, "Thrust in reverse, as a multiple of PowerPerRower. Negative pushes the ship backward.");
			MaxRowingPower = BindSynced("Physics", "MaxRowingPower", 1.5f, "Maximum total rowing thrust, ahead or astern. 1.0 = same as a full sail with a strong tailwind.");
			MaxRowingSpeed = BindSynced("Physics", "MaxRowingSpeed", 0f, "Speed in m/s (along the bow) above which rowing stops adding thrust, so oars and sail can't stack without limit. 0 = no limit.");
			CrewFalloff = BindSynced("Physics", "CrewFalloff", 0f, "How much each extra rower adds less than the one before, from 0 (every rower adds the same) to 0.5. With 0.1 the second rower adds 90%, the third 81%, and so on; the strongest strokes count first.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f));
			SteeringMultiplier = BindSynced("Physics", "SteeringMultiplier", 0f, "How strongly rowing turns the ship. 0 = off. Above 0 the ship turns away from the side the rowers sit on, and the other way when rowing in reverse; rowers on both sides cancel out. Negative values turn it towards that side.");
			RampUpTime = BindSynced("Physics", "RampUpTime", 1.5f, "Seconds for the rowing thrust to reach a new target (crew changes, gear shifts, stopping).");
			StaminaDrainAmount = BindSynced("Gameplay", "StaminaDrainAmount", 6f, "Stamina spent on every stroke in any gear but neutral. Set to 0 to disable the cost entirely.");
			StaminaDrainIntervalSlow = BindSynced("Gameplay", "StaminaDrainIntervalSlow", 2f, "Seconds between strokes in Slow gear (1) and reverse.");
			StaminaDrainIntervalHalf = BindSynced("Gameplay", "StaminaDrainIntervalHalf", 1.5f, "Seconds between strokes in Half gear (2).");
			StaminaDrainIntervalFull = BindSynced("Gameplay", "StaminaDrainIntervalFull", 1f, "Seconds between strokes in Full gear (3).");
			ShowOarHint = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "ShowOarHint", true, "Show the \"Equip the oar to row.\" message (and the Drakkar Oar one) when you sit on a bench without the right oar and press W or S.");
			StrokeSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "StrokeSpeed", 1f, "Speed of the rowing animation in every gear. 1.0 = the animation's own pace in Half gear (2); Slow and reverse are slower, Full faster.");
			Splash = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "Splash", true, "Splash and play a sound when the oar blade hits the water.");
			ShowCrewHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ShowCrewHud", true, "While you row, show a panel with how many aboard are rowing and a top-down drawing of the ship with where each of them sits.");
			RowingSound = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "RowingSound", true, "Play the sound of the oars on every stroke: a lone rower's, or the whole crew's when two or more row together.");
			RowingSoundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "RowingSoundVolume", 1f, new ConfigDescription("Volume of the rowing sound, relative to the game's sound effects volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			DebugRowingSync = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugRowingSync", false, "Log every rower's stroke phase against the crew's beat to BepInEx/LogOutput.log, to track down rowers whose animation drifts or stalls in multiplayer.");
			DebugSkipFaceStern = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugSkipFaceStern", false, "Do not turn the rower to face the stern. Only to find out what makes the cape or head jitter while rowing; the rower then faces the way the bench does.");
			DebugSkipLean = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugSkipLean", false, "Do not lean the rower over the gunwale. Only to find out what makes the cape or head jitter while rowing.");
			_harmony = new Harmony("com.fabenejr.vikingoarsmen");
			_harmony.PatchAll();
			OarItem.Setup();
			Log.LogInfo((object)"Viking Oarsmen v3.4.0 loaded. Equip the oar and sit on a ship bench to row.");
		}

		private void Update()
		{
			RowingController.Update();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class RaftSeat
	{
		private const string RaftPrefab = "Raft";

		private const string StoolSource = "VikingShip";

		private const string StoolPath = "interactive/sit_box (1)";

		private const string SeatName = "VikingOarsmen_sit_box";

		private static readonly Vector3 s_seatPosition = new Vector3(-1.1f, 0.48f, 1.9f);

		internal static void AddTo(Ship ship)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (!(Utils.GetPrefabName(((Component)ship).gameObject) != "Raft"))
			{
				Transform val = ((Component)ship).transform.Find("interactive");
				GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("VikingShip") : null);
				Transform val3 = (((Object)(object)val2 != (Object)null) ? val2.transform.Find("interactive/sit_box (1)") : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val3 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Couldn't add the raft bench: raft interactive or VikingShip stool not found.");
				}
				else if (!((Object)(object)val.Find("VikingOarsmen_sit_box") != (Object)null))
				{
					GameObject obj = Object.Instantiate<GameObject>(((Component)val3).gameObject, val, false);
					((Object)obj).name = "VikingOarsmen_sit_box";
					obj.transform.localPosition = s_seatPosition;
					obj.transform.localRotation = Quaternion.identity;
				}
			}
		}
	}
	internal class RowerAnimation
	{
		private const float BlendTime = 0.3f;

		private const float SyncTime = 0.5f;

		private const float MaxCatchUp = 0.5f;

		private const float DebugInterval = 1f;

		private readonly Player _player;

		private PlayableGraph _graph;

		private AnimationPlayableOutput _output;

		private AnimationMixerPlayable _mixer;

		private AnimationClipPlayable _idle;

		private AnimationClipPlayable _stroke;

		private float _weight;

		private float _strokeWeight;

		private float _phase;

		private bool _mirrored;

		private float _rate;

		private float _gap;

		private float _speed = 1f;

		private float _debugTimer;

		internal RowerAnimation(Player player)
		{
			_player = player;
		}

		internal void Update(bool rowing, RowingGear gear, bool mirrored)
		{
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: 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)
			AnimationClip val = ModAssets.RowIdle(mirrored);
			AnimationClip val2 = ModAssets.RowStroke(mirrored);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return;
			}
			if (((PlayableGraph)(ref _graph)).IsValid() && mirrored != _mirrored)
			{
				DebugLog("seat side changed, rebuilding the graph");
				Destroy();
			}
			_weight = Mathf.MoveTowards(_weight, rowing ? 1f : 0f, Time.deltaTime / 0.3f);
			if (_weight <= 0f)
			{
				Destroy();
				return;
			}
			float num = RowingBeat.StrokeRate(gear, val2.length);
			if (!((PlayableGraph)(ref _graph)).IsValid())
			{
				if (!Create(val, val2, num))
				{
					return;
				}
				_mirrored = mirrored;
			}
			bool flag = rowing && gear != RowingGear.Stop;
			_strokeWeight = Mathf.MoveTowards(_strokeWeight, flag ? 1f : 0f, Time.deltaTime / 0.3f);
			if (_rate <= 0f && num > 0f)
			{
				_phase = RowingBeat.GetBeat(num);
				DebugLog($"stroke starts in {gear}: phase set to the beat {_phase:F2}");
			}
			else if (num > 0f && !Mathf.Approximately(num, _rate))
			{
				DebugLog($"pace {_rate:F3} -> {num:F3} ({gear}): {BeatGap(num):F2} off the beat");
			}
			_rate = num;
			AdvancePhase(num);
			float num2 = ((gear == RowingGear.Back) ? (1f - _phase) : _phase);
			PlayableExtensions.SetTime<AnimationClipPlayable>(_idle, (double)Mathf.Repeat(Time.time, val.length));
			PlayableExtensions.SetTime<AnimationClipPlayable>(_stroke, (double)(num2 * val2.length));
			PlayableExtensions.SetInputWeight<AnimationMixerPlayable>(_mixer, 0, 1f - _strokeWeight);
			PlayableExtensions.SetInputWeight<AnimationMixerPlayable>(_mixer, 1, _strokeWeight);
			PlayableOutputExtensions.SetWeight<AnimationPlayableOutput>(_output, _weight);
			if (Plugin.DebugRowingSync.Value)
			{
				_debugTimer -= Time.deltaTime;
				if (_debugTimer <= 0f)
				{
					_debugTimer = 1f;
					DebugLog($"{gear} rate {num:F3} phase {_phase:F2} beat {((num > 0f) ? RowingBeat.GetBeat(num) : 0f):F2} " + $"gap {_gap:F2} speed x{_speed:F2} weight {_weight:F2} stroke {_strokeWeight:F2} mirrored {_mirrored}");
				}
			}
		}

		internal void Destroy()
		{
			if (((PlayableGraph)(ref _graph)).IsValid())
			{
				DebugLog("graph destroyed");
				((PlayableGraph)(ref _graph)).Destroy();
			}
			_strokeWeight = 0f;
			_rate = 0f;
		}

		private bool Create(AnimationClip idle, AnimationClip stroke, float rate)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_006d: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_0109: Unknown result type (might be due to invalid IL or missing references)
			GameObject visual = ((Character)_player).GetVisual();
			Animator val = (((Object)(object)visual != (Object)null) ? visual.GetComponentInChildren<Animator>() : null);
			if ((Object)(object)val == (Object)null || !val.isHuman)
			{
				return false;
			}
			_graph = PlayableGraph.Create("VikingOarsmen_Rowing");
			((PlayableGraph)(ref _graph)).SetTimeUpdateMode((DirectorUpdateMode)1);
			_idle = AnimationClipPlayable.Create(_graph, idle);
			_stroke = AnimationClipPlayable.Create(_graph, stroke);
			PlayableExtensions.SetSpeed<AnimationClipPlayable>(_idle, 0.0);
			PlayableExtensions.SetSpeed<AnimationClipPlayable>(_stroke, 0.0);
			_mixer = AnimationMixerPlayable.Create(_graph, 2);
			((PlayableGraph)(ref _graph)).Connect<AnimationClipPlayable, AnimationMixerPlayable>(_idle, 0, _mixer, 0);
			((PlayableGraph)(ref _graph)).Connect<AnimationClipPlayable, AnimationMixerPlayable>(_stroke, 0, _mixer, 1);
			_output = AnimationPlayableOutput.Create(_graph, "Rowing", val);
			PlayableOutputExtensions.SetSourcePlayable<AnimationPlayableOutput, AnimationMixerPlayable>(_output, _mixer);
			PlayableOutputExtensions.SetWeight<AnimationPlayableOutput>(_output, 0f);
			((PlayableGraph)(ref _graph)).Play();
			_phase = ((rate > 0f) ? RowingBeat.GetBeat(rate) : 0f);
			_rate = rate;
			DebugLog($"graph created, phase {_phase:F2}, pace {rate:F3}");
			return true;
		}

		private void AdvancePhase(float rate)
		{
			if (!(rate <= 0f))
			{
				float phase = _phase;
				float deltaTime = Time.deltaTime;
				_phase = Mathf.Repeat(_phase + deltaTime * rate, 1f);
				float num = BeatGap(rate);
				float num2 = Mathf.Clamp(num * Mathf.Clamp01(deltaTime / 0.5f), -0.5f * rate * deltaTime, 0.5f * rate * deltaTime);
				_phase = Mathf.Repeat(_phase + num2, 1f);
				_gap = num;
				if (deltaTime > 0f)
				{
					_speed = (Mathf.Repeat(_phase - phase + 0.5f, 1f) - 0.5f) / (deltaTime * rate);
				}
			}
		}

		private float BeatGap(float rate)
		{
			return Mathf.Repeat(RowingBeat.GetBeat(rate) - _phase + 0.5f, 1f) - 0.5f;
		}

		private void DebugLog(string message)
		{
			if (Plugin.DebugRowingSync.Value)
			{
				string text = (((Object)(object)_player != (Object)null) ? _player.GetPlayerName() : "?");
				string text2 = (((Object)(object)_player == (Object)(object)Player.m_localPlayer) ? "local" : "remote");
				double num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : (-1.0));
				Plugin.Log.LogInfo((object)$"[sync] {text} ({text2}) t={num:F2}: {message}");
			}
		}
	}
	internal static class RowingBeat
	{
		private const float SlowPace = 0.75f;

		private const float HalfPace = 1f;

		private const float FullPace = 1.3f;

		internal static float StrokeRate(RowingGear gear, float clipLength)
		{
			float num;
			switch (gear)
			{
			case RowingGear.Back:
			case RowingGear.Slow:
				num = 0.75f;
				break;
			case RowingGear.Half:
				num = 1f;
				break;
			case RowingGear.Full:
				num = 1.3f;
				break;
			default:
				return 0f;
			}
			return Mathf.Max(0f, Plugin.StrokeSpeed.Value) * num / Mathf.Max(clipLength, 0.01f);
		}

		internal static float GetBeat(float rate)
		{
			double num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : ((double)Time.time)) * (double)rate;
			return (float)(num - Math.Floor(num));
		}
	}
	internal static class RowingController
	{
		private const string RowingShipKey = "VikingOarsmen_RowingShip";

		private const string RowingGearKey = "VikingOarsmen_Gear";

		private const string DrakkarPrefab = "VikingShip_Ashlands";

		private const string HoldfastName = "$ship_holdfast";

		private static RowingGear s_desiredGear;

		private static bool s_suspended;

		private static float s_staminaTimer;

		private const float PressThreshold = 0.6f;

		private const float ReleaseThreshold = 0.3f;

		private static bool s_forwardHeld;

		private static bool s_backwardHeld;

		private static bool s_forwardPressed;

		private static bool s_backwardPressed;

		internal static void ReadMoveInput(Vector3 movedir)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			s_forwardPressed |= Press(movedir.z, ref s_forwardHeld);
			s_backwardPressed |= Press(0f - movedir.z, ref s_backwardHeld);
		}

		private static bool Press(float axis, ref bool held)
		{
			bool flag = axis > (held ? 0.3f : 0.6f);
			bool result = flag && !held;
			held = flag;
			return result;
		}

		internal static void Update()
		{
			bool flag = s_forwardPressed;
			bool flag2 = s_backwardPressed;
			s_forwardPressed = false;
			s_backwardPressed = false;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				s_desiredGear = RowingGear.Stop;
				return;
			}
			Ship seatShip = GetSeatShip(localPlayer);
			bool flag3 = (Object)(object)seatShip != (Object)null && IsOnBench(localPlayer) && !((Character)localPlayer).IsDead();
			bool flag4 = HasOarFor(localPlayer, seatShip);
			bool flag5 = CanTakeGameplayInput();
			if (!flag3 || !flag4)
			{
				if (Plugin.ShowOarHint.Value && flag3 && !flag4 && flag5 && (flag || flag2))
				{
					((Character)localPlayer).Message((MessageType)2, OarItem.IsOar(((Humanoid)localPlayer).GetCurrentWeapon()) ? "This oar can't reach the water from here. Equip the Drakkar Oar." : "Equip the oar to row.", 0, (Sprite)null, false);
				}
				s_desiredGear = RowingGear.Stop;
				s_suspended = false;
				SetRowingGear(localPlayer, RowingGear.Stop);
				SetRowingShip(localPlayer, null);
				return;
			}
			if (flag5 && ZInput.GetButtonDown("Use"))
			{
				((Character)localPlayer).AttachStop();
				return;
			}
			RowingGear rowingGear = s_desiredGear;
			if (flag5)
			{
				if (flag)
				{
					s_desiredGear = StepForward(s_desiredGear);
				}
				else if (flag2)
				{
					s_desiredGear = StepBackward(s_desiredGear);
				}
			}
			bool flag6 = s_desiredGear != rowingGear;
			if (flag6)
			{
				Plugin.Log.LogDebug((object)$"Gear {rowingGear} -> {s_desiredGear}");
			}
			RowingGear gear = ResolveEffectiveGear(localPlayer, flag6);
			SetRowingGear(localPlayer, gear);
			SetRowingShip(localPlayer, seatShip);
		}

		internal static ZDOID GetRowingShip(Player player)
		{
			//IL_0018: 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)
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return ZDOID.None;
			}
			return component.GetZDO().GetZDOID("VikingOarsmen_RowingShip");
		}

		internal static RowingGear GetRowingGear(Player player)
		{
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return RowingGear.Stop;
			}
			return (RowingGear)component.GetZDO().GetInt("VikingOarsmen_Gear", 0);
		}

		internal static RowingGear GetSelectedGear()
		{
			return s_desiredGear;
		}

		private static void SetRowingGear(Player player, RowingGear gear)
		{
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner())
			{
				ZDO zDO = component.GetZDO();
				if (zDO.GetInt("VikingOarsmen_Gear", 0) != (int)gear)
				{
					zDO.Set("VikingOarsmen_Gear", (int)gear);
				}
			}
		}

		internal static bool IsRowingMode(Player player)
		{
			Ship seatShip = GetSeatShip(player);
			if ((Object)(object)seatShip != (Object)null && !((Character)player).IsDead() && IsOnBench(player))
			{
				return HasOarFor(player, seatShip);
			}
			return false;
		}

		private static Ship GetSeatShip(Player player)
		{
			if (!((Character)player).IsAttachedToShip())
			{
				return null;
			}
			Transform attachPoint = player.GetAttachPoint();
			if (!((Object)(object)attachPoint != (Object)null))
			{
				return null;
			}
			return ((Component)attachPoint).GetComponentInParent<Ship>();
		}

		private static bool IsOnBench(Player player)
		{
			if (!((Character)player).IsAttachedToShip() || player.GetDoodadController() != null)
			{
				return false;
			}
			Transform attachPoint = player.GetAttachPoint();
			Chair val = (((Object)(object)attachPoint != (Object)null) ? ((Component)attachPoint).GetComponentInParent<Chai