Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Heave Ho v1.0.0
plugins/HeaveHo.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HeaveHo.Shared; using Microsoft.CodeAnalysis; using Nick.Shared; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.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 HeaveHo { internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? IsAdvanced; public bool? ReadOnly; } internal static class Cfg { private static ConfigFile file; internal static ServerSettings Synced; private static Dictionary<ConfigDefinition, string> orphans; private static int nextOrder = 100000; private static readonly HashSet<string> OldSections = new HashSet<string> { "0. Seats", "0. Controls", "1. Rowing", "2. Stamina", "3. Oars", "4. HUD", "5. Splashes", "9. Debug", "5. Oar rendering (experimental)", "3. Oars (all ships)", "4. Splashes", "5. Rower HUD", "9. Advanced" }; internal static void Init(ConfigFile config, ServerSettings synced) { file = config; Synced = synced; orphans = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(config) as Dictionary<ConfigDefinition, string>; } internal static ConfigEntry<T> Bind<T>(string section, string key, T def, string desc, AcceptableValueBase range = null, bool advanced = false, int? order = null, string from = null, bool local = false) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = (order ?? nextOrder--), IsAdvanced = (advanced ? new bool?(true) : ((bool?)null)) }; if (!local && !desc.Contains("[Synced with Server]")) { desc += " [Synced with Server]"; } ConfigEntry<T> val = file.Bind<T>(section, key, def, new ConfigDescription(desc, range, new object[1] { configurationManagerAttributes })); if (!local) { Synced?.Add<T>(val); } if (from != null) { Migrate((ConfigEntryBase)(object)val, from); } return val; } private static void Migrate(ConfigEntryBase entry, string from) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown if (orphans == null) { return; } string[] array = from.Split(';'); foreach (string text in array) { int num = text.IndexOf('/'); ConfigDefinition val = new ConfigDefinition(text.Substring(0, num), text.Substring(num + 1)); if (orphans.TryGetValue(val, out var value)) { orphans.Remove(val); entry.SetSerializedValue(value); Plugin.Log.LogInfo((object)("Config: moved '" + val.Section + " / " + val.Key + "' to '" + entry.Definition.Section + " / " + entry.Definition.Key + "' (" + value + ").")); break; } } } internal static void PurgeSection(string section) { if (orphans == null) { return; } List<ConfigDefinition> list = new List<ConfigDefinition>(); foreach (ConfigDefinition key in orphans.Keys) { if (key.Section == section) { list.Add(key); } } foreach (ConfigDefinition item in list) { orphans.Remove(item); } } internal static void PurgeOldSections() { if (orphans != null) { List<ConfigDefinition> list = new List<ConfigDefinition>(); foreach (ConfigDefinition key in orphans.Keys) { if (OldSections.Contains(key.Section)) { list.Add(key); } } foreach (ConfigDefinition item in list) { orphans.Remove(item); } } file.Save(); } internal static void Save() { file.Save(); } } internal static class ObjLoader { private static readonly CultureInfo Inv = CultureInfo.InvariantCulture; internal static Mesh Load(string path) { //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) List<Vector3> list = new List<Vector3>(); List<Vector2> list2 = new List<Vector2>(); List<Vector3> list3 = new List<Vector3>(); List<Vector3> list4 = new List<Vector3>(); List<Vector2> list5 = new List<Vector2>(); List<Vector3> list6 = new List<Vector3>(); List<int> list7 = new List<int>(); bool flag = true; string[] array = File.ReadAllLines(path); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } string[] array2 = text2.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); switch (array2[0]) { case "v": list.Add(new Vector3(F(array2[1]), F(array2[2]), 0f - F(array2[3]))); break; case "vt": list2.Add(new Vector2(F(array2[1]), (array2.Length > 2) ? F(array2[2]) : 0f)); break; case "vn": list3.Add(new Vector3(F(array2[1]), F(array2[2]), 0f - F(array2[3]))); break; case "f": { int count = list4.Count; int num = array2.Length - 1; for (int j = 1; j < array2.Length; j++) { string[] array3 = array2[j].Split('/'); list4.Add(list[Index(array3[0], list.Count)]); list5.Add((array3.Length > 1 && array3[1].Length > 0) ? list2[Index(array3[1], list2.Count)] : Vector2.zero); if (array3.Length > 2 && array3[2].Length > 0) { list6.Add(list3[Index(array3[2], list3.Count)]); } else { flag = false; } } for (int k = 1; k < num - 1; k++) { list7.Add(count); list7.Add(count + k + 1); list7.Add(count + k); } break; } } } if (list7.Count == 0) { throw new InvalidDataException("OBJ has no faces"); } Mesh val = new Mesh { name = Path.GetFileNameWithoutExtension(path) }; if (list4.Count > 65000) { val.indexFormat = (IndexFormat)1; } val.SetVertices(list4); val.SetUVs(0, list5); val.SetTriangles(list7, 0); if (flag && list6.Count == list4.Count) { val.SetNormals(list6); } else { val.RecalculateNormals(); } val.RecalculateBounds(); val.RecalculateTangents(); return val; } internal static Texture2D LoadTexture(string objPath) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown string path = Path.GetDirectoryName(objPath) ?? ""; List<string> list = new List<string>(); string path2 = Path.ChangeExtension(objPath, ".mtl"); if (File.Exists(path2)) { string[] array = File.ReadAllLines(path2); foreach (string text in array) { string text2 = text.Trim(); if (text2.StartsWith("map_Kd", StringComparison.OrdinalIgnoreCase)) { string path3 = text2.Substring(6).Trim().Replace('\\', '/'); list.Add(Path.Combine(path, Path.GetFileName(path3))); } } } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(objPath); list.Add(Path.Combine(path, fileNameWithoutExtension + ".png")); list.Add(Path.Combine(path, fileNameWithoutExtension + ".jpg")); list.Add(Path.Combine(path, fileNameWithoutExtension + ".jpeg")); foreach (string item in list) { if (File.Exists(item)) { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true) { name = Path.GetFileName(item) }; if (ImageConversion.LoadImage(val, File.ReadAllBytes(item))) { ((Texture)val).wrapMode = (TextureWrapMode)0; ((Texture)val).filterMode = (FilterMode)1; return val; } } } return null; } private static float F(string s) { return float.Parse(s, NumberStyles.Float, Inv); } private static int Index(string s, int count) { int num = int.Parse(s, Inv); if (num <= 0) { return count + num; } return num - 1; } } [BepInPlugin("nick.valheim.heaveho", "Heave Ho", "1.0.0")] public class Plugin : BaseUnityPlugin { internal class OarModel { public Mesh Mesh; public Texture2D Texture; public bool HullTexture; } internal delegate bool OarlockMeasure(out float distance, out float height); internal class ShipOarSettings { public ConfigEntry<float> Outboard; public ConfigEntry<float> Height; public ConfigEntry<string> Placement; public ConfigEntry<float> PerSeatHeight; public ConfigEntry<float> PerSeatOutboard; public ConfigEntry<float> Spacing; public ConfigEntry<float> Scale; public ConfigEntry<bool> Visuals; public ConfigEntry<bool> Physics; public ConfigEntry<bool> ShowAtRest; } public const string ModGuid = "nick.valheim.heaveho"; public const string ModName = "Heave Ho"; public const string ModVersion = "1.0.0"; internal static ManualLogSource Log; internal static Plugin Instance; internal static ConfigEntry<float> RowerStrength; internal static ConfigEntry<float> EasyMultiplier; internal static ConfigEntry<float> BackMultiplier; internal static ConfigEntry<float> TurnStrength; internal static ConfigEntry<float> MaxRowingSpeed; internal static ConfigEntry<string> RowerBonuses; internal static ConfigEntry<float> StaminaPerSecond; internal static ConfigEntry<float> EasyStaminaMultiplier; internal static ConfigEntry<float> RecoverStamina; internal static ConfigEntry<bool> ShowOars; internal static ConfigEntry<bool> ShipUnderSail; internal static ConfigEntry<float> OarModelScale; internal static ConfigEntry<float> StrokesPerSecond; internal static ConfigEntry<bool> Splashes; internal static ConfigEntry<string> EntryEffects; internal static ConfigEntry<string> ExitEffects; internal static ConfigEntry<float> HudOutboard; internal static ConfigEntry<float> HudHeight; internal static ConfigEntry<string> RowingSeatAnimations; internal const float PlaceholderOarLength = 3.2f; internal const float DefaultOutboard = 1.1f; internal const float DefaultHeight = 0.5f; internal static GameObject OarPrefab; internal static Mesh OarMesh; internal static Texture2D OarTexture; private static readonly Dictionary<string, OarModel> shipOars = new Dictionary<string, OarModel>(); private static string assetDir = ""; private static readonly Dictionary<string, OarModel> shipFittings = new Dictionary<string, OarModel>(); private Harmony harmony; internal static ServerSettings Synced; internal const string PlacementFixed = "Fixed"; internal const string PlacementPerSeat = "Per seat"; private static readonly Dictionary<string, ShipOarSettings> shipSettings = new Dictionary<string, ShipOarSettings>(); private static readonly Dictionary<string, ConfigEntry<string>> seatFilters = new Dictionary<string, ConfigEntry<string>>(); private static string cachedBonusText; private static float[] cachedBonuses = new float[0]; internal static OarModel OarFor(string ship) { if (shipOars.TryGetValue(ship, out var value)) { return value; } OarModel oarModel = LoadShipModel("oar_", ship) ?? (((Object)(object)OarMesh != (Object)null) ? new OarModel { Mesh = OarMesh, Texture = OarTexture } : null); shipOars[ship] = oarModel; return oarModel; } internal static OarModel FittingFor(string ship) { if (shipFittings.TryGetValue(ship, out var value)) { return value; } OarModel oarModel = LoadShipModel("oarlock_", ship); shipFittings[ship] = oarModel; return oarModel; } private static OarModel LoadShipModel(string prefix, string ship) { //IL_0083: 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) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(assetDir, prefix + DisplayName(ship).ToLowerInvariant().Replace(' ', '_') + ".obj"); if (!File.Exists(text)) { return null; } try { Mesh val = ObjLoader.Load(text); ((Object)val).name = "HeaveHo_" + prefix + ship; Texture2D val2 = ObjLoader.LoadTexture(text); ManualLogSource log = Log; object[] obj = new object[4] { ship, Path.GetFileName(text), val.vertexCount, null }; Bounds bounds = val.bounds; obj[3] = ((Bounds)(ref bounds)).min.x; string text2 = string.Format("[{0}] Loaded {1} ({2} verts, x {3:F2} to ", obj); bounds = val.bounds; log.LogInfo((object)(text2 + $"{((Bounds)(ref bounds)).max.x:F2}), " + (((Object)(object)val2 != (Object)null) ? ("texture " + ((Object)val2).name) : "drawn with the hull's texture"))); return new OarModel { Mesh = val, Texture = val2, HullTexture = ((Object)(object)val2 == (Object)null) }; } catch (Exception ex) { Log.LogWarning((object)("[" + ship + "] Couldn't load " + Path.GetFileName(text) + ": " + ex.Message)); return null; } } private void Awake() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; Translations.Load((BaseUnityPlugin)(object)this); Synced = new ServerSettings("nick.valheim.heaveho", ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); Cfg.Init(((BaseUnityPlugin)this).Config, Synced); BindSettings(); assetDir = AssetDir(); if (!LoadOarObj()) { LoadOarBundle(); } RowerHud.LoadIcon(assetDir); harmony = new Harmony("nick.valheim.heaveho"); harmony.PatchAll(); RowerHud.Install(harmony); Ship[] array = Object.FindObjectsByType<Ship>((FindObjectsSortMode)0); foreach (Ship val in array) { if (!Object.op_Implicit((Object)(object)((Component)val).GetComponent<RowingShip>())) { ((Component)val).gameObject.AddComponent<RowingShip>(); } } } private static void BindSettings() { RowerStrength = Cfg.Bind("1. Rowing", "Rower strength", 2f, "How hard rowers push. Acceleration (m/s²) at Pull hard when the crew's combined bonus is 1.0.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), advanced: false, null, "1. Rowing/Rower strength"); EasyMultiplier = Cfg.Bind("1. Rowing", "Easy stroke thrust", 0.5f, "Thrust of Easy stroke relative to Pull hard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), advanced: false, null, "1. Rowing/Easy stroke thrust"); BackMultiplier = Cfg.Bind("1. Rowing", "Back water thrust", 0.6f, "Reverse thrust of Back water relative to Pull hard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), advanced: false, null, "1. Rowing/Back water thrust"); TurnStrength = Cfg.Bind("1. Rowing", "Turning strength", 0.5f, "How much rowing on one side turns the ship. 0 = straight line only, 1 = full physics (a lone rower pulls the ship in circles).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), advanced: false, null, "1. Rowing/Turning strength"); MaxRowingSpeed = Cfg.Bind("1. Rowing", "Max rowing speed", 12f, "Rowers stop adding thrust above this speed (m/s). Safety cap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), advanced: true, null, "1. Rowing/Max rowing speed"); RowerBonuses = Cfg.Bind("1. Rowing", "Rower bonuses", "0.25,0.15,0.10,0.08,0.06,0.05", "Share of thrust each extra rower adds (diminishing returns). The last value repeats for larger crews.", null, advanced: true, null, "1. Rowing/Rower bonuses"); StaminaPerSecond = Cfg.Bind("2. Stamina", "Stamina per second", 3f, "Stamina spent per second at Pull hard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), advanced: false, null, "2. Stamina/Stamina per second"); EasyStaminaMultiplier = Cfg.Bind("2. Stamina", "Easy / back water stamina", 0.4f, "Stamina cost of Easy stroke and Back water relative to Pull hard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), advanced: false, null, "2. Stamina/Easy / back water stamina"); RecoverStamina = Cfg.Bind("2. Stamina", "Resume rowing at", 30f, "After running out, a rower rests until stamina recovers to this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), advanced: false, null, "2. Stamina/Resume rowing at"); ShowOars = Cfg.Bind("3. Oars and splashes", "Show oars", def: true, "Master switch for oar visuals on every ship (each ship also has its own switch).", null, advanced: false, null, "3. Oars (all ships)/Show oars;3. Oars/Show oars", local: true); OarModelScale = Cfg.Bind("3. Oars and splashes", "Oar size", 1f, "Size multiplier for the oar model on every ship (each ship also has its own size).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), advanced: false, null, "3. Oars (all ships)/Oar size;3. Oars/Oar model scale", local: true); StrokesPerSecond = Cfg.Bind("3. Oars and splashes", "Stroke rate", 0.55f, "Strokes per second at Easy stroke. Pull hard strokes faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), advanced: false, null, "3. Oars (all ships)/Stroke rate;3. Oars/Strokes per second", local: true); ShipUnderSail = Cfg.Bind("3. Oars and splashes", "Ship the oars under sail", def: false, "While the sail is up and the ship is moving, oars nobody is pulling slide in out of sight; they come back out the moment someone rows or the ship slows. Off: they stay where each ship's \"Oars at rest\" puts them. Live.", null, advanced: false, null, null, local: true); Splashes = Cfg.Bind("3. Oars and splashes", "Oar splashes", def: true, "Splash where each blade enters the water, and a smaller one as it lifts out.", null, advanced: false, null, "4. Splashes/Oar splashes;5. Splashes/Oar splashes", local: true); EntryEffects = Cfg.Bind("3. Oars and splashes", "Splash effects: blade enters", "vfx_water_surface_fish", "Comma-separated Valheim effects (visuals or sfx_ sounds) spawned where the blade bites. Water-related names are listed in LogOutput.log under [Splash]. Live.", null, advanced: false, null, "4. Splashes/Effects on blade entry;5. Splashes/Extra effects on blade entry", local: true); ExitEffects = Cfg.Bind("3. Oars and splashes", "Splash effects: blade lifts out", "vfx_watersplash_bathtub", "Same, for when the blade lifts out. Live.", null, advanced: false, null, "4. Splashes/Effects on blade exit;5. Splashes/Extra effects on blade exit", local: true); HudOutboard = Cfg.Bind("4. Rower HUD", "Arrows: distance outboard", 0.2f, "How far past your oarlock (m) the rowing arrows float.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-3f, 3f), advanced: false, null, null, local: true); HudHeight = Cfg.Bind("4. Rower HUD", "Arrows: height", 0.2f, "Height (m) of the rowing arrows above your oarlock.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 4f), advanced: false, null, null, local: true); RowingSeatAnimations = Cfg.Bind("9. Advanced", "Rowing seat animations", "attach_sitship", "Seat animations that count as rowing seats (helm and mast holds use others). Seats are listed in LogOutput.log per ship. Applies to newly loaded ships; all players should match.", null, advanced: true, null, "0. Seats/Rowing seat animations"); Cfg.PurgeOldSections(); } private string AssetDir() { string text = (string.IsNullOrEmpty(((BaseUnityPlugin)this).Info.Location) ? "" : (Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? "")); List<string> list = new List<string>(); if (text != "") { list.Add(text); } list.Add(Path.Combine(Paths.BepInExRootPath, "scripts")); list.Add(Path.Combine(Paths.PluginPath, "HeaveHo")); foreach (string item in list) { string[] array = new string[3] { "oar.obj", "oarIcon.png", "heaveho" }; foreach (string path in array) { if (File.Exists(Path.Combine(item, path))) { return item; } } } return text; } private bool LoadOarObj() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0085: 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) string text = Path.Combine(AssetDir(), "oar.obj"); if (!File.Exists(text)) { return false; } try { OarMesh = ObjLoader.Load(text); ((Object)OarMesh).name = "HeaveHo_Oar"; OarTexture = ObjLoader.LoadTexture(text); ManualLogSource log = Log; string text2 = $"Loaded custom oar from oar.obj ({OarMesh.vertexCount} verts, "; Bounds bounds = OarMesh.bounds; object arg = ((Bounds)(ref bounds)).min; bounds = OarMesh.bounds; log.LogInfo((object)(text2 + $"bounds {arg} to {((Bounds)(ref bounds)).max}), texture: " + (((Object)(object)OarTexture != (Object)null) ? ((Object)OarTexture).name : "none found"))); return true; } catch (Exception ex) { Log.LogWarning((object)("Couldn't load oar.obj: " + ex.Message)); OarMesh = null; return false; } } private void LoadOarBundle() { string text = Path.Combine(AssetDir(), "heaveho"); if (!File.Exists(text)) { Log.LogInfo((object)"No 'heaveho' asset bundle next to the dll; using placeholder oars."); return; } AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { Log.LogWarning((object)"Couldn't load 'heaveho' asset bundle (built with a different Unity version?). Using placeholder oars."); return; } OarPrefab = val.LoadAsset<GameObject>("HeaveHo_Oar"); if ((Object)(object)OarPrefab == (Object)null) { Log.LogWarning((object)("Bundle has no prefab named 'HeaveHo_Oar'. Assets in bundle: " + string.Join(", ", val.GetAllAssetNames()) + ". Using placeholder oars.")); } else { Log.LogInfo((object)"Loaded custom oar model from 'heaveho'."); } } private static bool IsDrakkar(string ship) { return ship == "VikingShip_Ashlands"; } internal static string DisplayName(string ship) { return ship switch { "Karve" => "Karve", "VikingShip" => "Longship", "VikingShip_Ashlands" => "Drakkar", "Raft" => "Raft", _ => ship, }; } private static string ShipSection(string ship) { return "5. Ship: " + DisplayName(ship); } private static string ShipFrom(string ship, string key, string oldestKey = null) { return "6. Ship: " + DisplayName(ship) + "/" + key + ((oldestKey != null) ? (";3. Oars - " + ship + "/" + oldestKey) : ""); } internal static ConfigEntry<string> GetSeatNameFilter(string ship) { if (!seatFilters.TryGetValue(ship, out var value)) { value = Cfg.Bind(ShipSection(ship), "Rowing seat names", IsDrakkar(ship) ? "sit_box" : "", "Seat name prefixes that row on the " + DisplayName(ship) + " (e.g. sit_box). Empty = all seats with a rowing animation. Applies to newly loaded ships; all players should match.", null, advanced: true, 10, ShipFrom(ship, "Rowing seat names", "Rowing seat names")); seatFilters[ship] = value; Cfg.Save(); } return value; } internal static ShipOarSettings GetShipSettings(string ship, OarlockMeasure measure) { if (shipSettings.TryGetValue(ship, out var value)) { return value; } ShipProfile shipProfile = ShipProfile.For(ship); bool flag = shipProfile != null; float num = 1.1f; float num2 = 0.5f; string text = "hand-tuned defaults"; if (!flag && ship != "Karve" && measure(out var distance, out var height)) { num = distance; num2 = height; text = "measured from the hull"; } string text2 = ShipSection(ship); string text3 = DisplayName(ship); value = new ShipOarSettings { Visuals = Cfg.Bind(text2, "Oar visuals", def: true, "Show oars and splashes on the " + text3 + ". Live.", null, advanced: false, 100, ShipFrom(ship, "Oar visuals", "Oar visuals"), local: true), Physics = Cfg.Bind(text2, "Oar physics", def: true, "Seated rowers push and turn the " + text3 + " and spend stamina. Off = cosmetic rowing. Both off = ordinary seats. Live.", null, advanced: false, 99, ShipFrom(ship, "Oar physics", "Oar physics")), Scale = Cfg.Bind(text2, "Oar size", 1f, "Oar size on the " + text3 + ", on top of the all-ships size. Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), advanced: false, 98, ShipFrom(ship, "Oar size", "Oar size"), local: true), ShowAtRest = Cfg.Bind(text2, "Oars at rest", def: true, (shipProfile != null && shipProfile.StowAlongHull) ? ("Leave the " + text3 + "'s oars out, stowed along the hull, when nobody is rowing them. Off: an oar slides out when its rower starts and back in when they stop. Live.") : ("Leave the " + text3 + "'s oars out, level through their ports, when nobody is rowing them. Off: an oar slides out of its port when its rower starts and back in when they stop, so the ship keeps its own shape. Live."), null, advanced: false, 97, null, local: true) }; if (flag) { value.PerSeatOutboard = Cfg.Bind(text2, "Oar position: out", 0f, "Move every oar outward (+) or inward (-) from where the hull puts it (m). Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), advanced: false, 80, ShipFrom(ship, "Oar position: out")); value.PerSeatHeight = Cfg.Bind(text2, "Oar position: up", 0f, "Move every oar up (+) or down (-) from where the hull puts it (m). Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), advanced: false, 79, ShipFrom(ship, "Oar position: up")); } else { value.Placement = Cfg.Bind(text2, "Oarlock placement", "Fixed", "Fixed: one distance and height for every bench. Per seat: the rail is measured beside each seat (for hulls whose benches span the beam). Applies to newly loaded ships.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Fixed", "Per seat" }), advanced: false, 90, ShipFrom(ship, "Oarlock placement", "Oarlock placement")); value.Outboard = Cfg.Bind(text2, "Fixed: distance from centerline", (float)Math.Round(num, 2), "Fixed placement: oar pivot distance (m) from the centerline. Default " + text + ". Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), advanced: false, 89, ShipFrom(ship, "Fixed: distance from centerline", "Oarlock distance from centerline")); value.Height = Cfg.Bind(text2, "Fixed: height above seat", (float)Math.Round(num2, 2), "Fixed placement: oar pivot height (m) above the benches. Default " + text + ". Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 4f), advanced: false, 88, ShipFrom(ship, "Fixed: height above seat", "Oarlock height above seat")); value.PerSeatOutboard = Cfg.Bind(text2, "Per seat: distance from rail", 0f, "Per seat placement: move every oarlock outward (+) or inward (-) from the measured rail (m). Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-3f, 3f), advanced: false, 80, ShipFrom(ship, "Per seat: distance from rail", "Per seat: distance from rail")); value.PerSeatHeight = Cfg.Bind(text2, "Per seat: height above rail", 0.05f, "Per seat placement: height (m) above the measured rail. Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), advanced: false, 79, ShipFrom(ship, "Per seat: height above rail", "Per seat: height above rail")); value.Spacing = Cfg.Bind(text2, "Per seat: oar spacing", 1f, "Per seat placement: fore-aft gap (m) between oars of rowers sharing one bench. Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), advanced: false, 78, ShipFrom(ship, "Per seat: oar spacing", "Per seat: oar spacing")); } shipSettings[ship] = value; Cfg.PurgeSection("6. Ship: " + text3); Cfg.PurgeSection("3. Oars - " + ship); Cfg.Save(); Log.LogInfo((object)("[" + ship + "] '" + text2 + "': visuals " + (value.Visuals.Value ? "on" : "off") + ", physics " + (value.Physics.Value ? "on" : "off") + ", " + ((!flag) ? $"placement {value.Placement.Value}, {value.Outboard.Value:F2} m out, {value.Height.Value:F2} m up" : (shipProfile.Fitting ? "oars in oarlocks on the gunwale" : "oars through the hull's oar ports")) + $", size {value.Scale.Value:F2}" + (flag ? "" : $" (fixed default {text}: {num:F2} / {num2:F2})"))); return value; } private void Update() { Synced?.Update(); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Synced?.Dispose(); Translations.Unload(); RowerHud.Unload(); RowingShip.DestroyAll(); foreach (OarModel value in shipOars.Values) { if (value != null && (Object)(object)value.Mesh != (Object)(object)OarMesh) { Object.Destroy((Object)(object)value.Mesh); if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } } } shipOars.Clear(); foreach (OarModel value2 in shipFittings.Values) { if (value2 != null) { Object.Destroy((Object)(object)value2.Mesh); if ((Object)(object)value2.Texture != (Object)null) { Object.Destroy((Object)(object)value2.Texture); } } } shipFittings.Clear(); if ((Object)(object)OarMesh != (Object)null) { Object.Destroy((Object)(object)OarMesh); } if ((Object)(object)OarTexture != (Object)null) { Object.Destroy((Object)(object)OarTexture); } OarMesh = null; OarTexture = null; OarPrefab = null; } internal static float RowerBonus(int index) { string value = RowerBonuses.Value; if (value != cachedBonusText) { List<float> list = new List<float>(); string[] array = value.Split(','); foreach (string text in array) { if (float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { list.Add(Mathf.Max(0f, result)); } } cachedBonuses = list.ToArray(); cachedBonusText = value; } if (cachedBonuses.Length == 0) { return 0f; } return cachedBonuses[Mathf.Min(index, cachedBonuses.Length - 1)]; } } [HarmonyPatch(typeof(Ship), "Awake")] internal static class ShipAwakePatch { private static void Postfix(Ship __instance) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<RowingShip>())) { ((Component)__instance).gameObject.AddComponent<RowingShip>(); } } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class PlayerSetControlsPatch { private static void Prefix(Player __instance, ref Vector3 __0) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { RowingShip localRowingShip = RowingShip.LocalRowingShip; if (!((Object)(object)localRowingShip == (Object)null)) { localRowingShip.HandleGearInput(__0.z); __0 = Vector3.zero; } } } } internal static class RowerHud { internal static bool Working; private static bool initialized; private static Hud hudInstance; private static GameObject root; private static GameObject controlsRoot; private static GameObject arrowBack; private static GameObject arrowSlow; private static GameObject arrowForward; private static GameObject arrowFast; private static readonly List<GameObject> hideWhileRowing = new List<GameObject>(); private static Sprite oarIcon; private static Image wheelIcon; private static readonly Color GameWheelColor = new Color(1f, 93f / 145f, 0f, 1f); private static Image powerIcon; private static Sprite powerSprite; private static Color powerColor; private static Vector3 powerScale; private static bool powerPreserveAspect; private static bool powerSwapped; private static Sprite wheelSprite; private static Color wheelColor; private static Quaternion wheelRotation; private static bool wheelPreserveAspect; private static bool iconSwapped; private static Sprite rowingIcon; private const string RootField = "m_shipHudRoot"; private const string ControlsRootField = "m_shipControlsRoot"; private const string BackField = "m_rudderBackward"; private const string SlowField = "m_rudderSlow"; private const string ForwardField = "m_rudderForward"; private const string FastField = "m_rudderFastForward"; private static readonly string[] HideFields = new string[5] { "m_rudderLeft", "m_rudderRight", "m_fullSail", "m_halfSail", "m_shipRudderIndicator" }; private static readonly string[] WindFields = new string[2] { "m_shipWindIndicatorRoot", "m_shipWindIconRoot" }; private static readonly List<GameObject> windParts = new List<GameObject>(); private static bool windHidden; internal static void LoadIcon(string pluginDir) { oarIcon = LoadSprite(Path.Combine(pluginDir, "oarIcon.png"), "HeaveHo_oarIcon"); rowingIcon = LoadSprite(Path.Combine(pluginDir, "rowingPowerIcon.png"), "HeaveHo_rowingPowerIcon"); } private static Sprite LoadSprite(string path, string name) { //IL_000e: 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_001b: Expected O, but got Unknown //IL_0077: 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) if (!File.Exists(path)) { return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = name }; if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path))) { Plugin.Log.LogWarning((object)("Couldn't read " + Path.GetFileName(path) + ".")); Object.Destroy((Object)(object)val); return null; } ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)val2).name = name; Plugin.Log.LogInfo((object)$"Loaded {Path.GetFileName(path)} ({((Texture)val).width}x{((Texture)val).height})."); return val2; } internal static void Unload() { RestoreWheel(); RestorePowerIcon(); SetWind(visible: true); Sprite[] array = (Sprite[])(object)new Sprite[2] { oarIcon, rowingIcon }; foreach (Sprite val in array) { if (!((Object)(object)val == (Object)null)) { Object.Destroy((Object)(object)val.texture); Object.Destroy((Object)(object)val); } } oarIcon = null; rowingIcon = null; Working = false; initialized = false; } internal static void Install(Harmony harmony) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Hud), "UpdateShipHud", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)"Hud.UpdateShipHud not found; rowing gears will show as text instead."); } else { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(RowerHud), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void Postfix(Hud __instance, Player __0) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0146: 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) if (!initialized || (Object)(object)__instance != (Object)(object)hudInstance || (Working && (Object)(object)root == (Object)null)) { Init(__instance); } if (!Working || (Object)(object)__0 == (Object)null || (Object)(object)__0 != (Object)(object)Player.m_localPlayer) { return; } RowingShip localRowingShip = RowingShip.LocalRowingShip; if ((Object)(object)localRowingShip == (Object)null) { RestoreWheel(); RestorePowerIcon(); SetWind(visible: true); return; } SwapWheelForOar(); ShowPowerIcon(localRowingShip); SetWind(visible: false); int localGear = localRowingShip.LocalGear; root.SetActive(true); if ((Object)(object)controlsRoot != (Object)null) { controlsRoot.SetActive(true); } arrowBack.SetActive(localGear < 0); arrowSlow.SetActive(localGear == 1); arrowForward.SetActive(localGear == 2); arrowFast.SetActive(false); foreach (GameObject item in hideWhileRowing) { if (item.activeSelf) { item.SetActive(false); } } if (!((Object)(object)controlsRoot != (Object)null)) { return; } Camera mainCamera = Utils.GetMainCamera(); if ((Object)(object)mainCamera != (Object)null) { Vector3 val = mainCamera.WorldToScreenPoint(localRowingShip.LocalHudAnchor); if (val.z > 0f) { controlsRoot.transform.position = val; } } } private static void SetWind(bool visible) { if (windHidden == !visible) { return; } foreach (GameObject windPart in windParts) { if ((Object)(object)windPart != (Object)null) { windPart.SetActive(visible); } } windHidden = !visible; } private static void SwapWheelForOar() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)oarIcon == (Object)null) && !((Object)(object)wheelIcon == (Object)null)) { iconSwapped = true; wheelIcon.sprite = oarIcon; ((Graphic)wheelIcon).color = Color.white; wheelIcon.preserveAspect = true; ((Transform)((Graphic)wheelIcon).rectTransform).localRotation = Quaternion.identity; if (!((Component)wheelIcon).gameObject.activeSelf) { ((Component)wheelIcon).gameObject.SetActive(true); } } } private static void ShowPowerIcon(RowingShip ship) { //IL_0077: 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_00ec: Unknown result type (might be due to invalid IL or missing references) Sprite val = (((Object)(object)rowingIcon != (Object)null) ? rowingIcon : oarIcon); if (!((Object)(object)val == (Object)null) && !((Object)(object)powerIcon == (Object)null)) { powerSwapped = true; int localGear = ship.LocalGear; powerIcon.sprite = val; powerIcon.preserveAspect = true; ((Graphic)powerIcon).color = (Color)((localGear == 0) ? new Color(1f, 1f, 1f, 0.45f) : Color.white); float num = ((localGear == 2) ? 1.3f : 1f) * (1f + ((localGear != 0) ? (0.1f * ship.LocalStrokeLift) : 0f)); ((Transform)((Graphic)powerIcon).rectTransform).localScale = new Vector3(powerScale.x * num * ((localGear < 0) ? (-1f) : 1f), powerScale.y * num, powerScale.z); if (!((Component)powerIcon).gameObject.activeSelf) { ((Component)powerIcon).gameObject.SetActive(true); } } } private static void RestorePowerIcon() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (powerSwapped && !((Object)(object)powerIcon == (Object)null)) { powerIcon.sprite = powerSprite; ((Graphic)powerIcon).color = powerColor; powerIcon.preserveAspect = powerPreserveAspect; ((Transform)((Graphic)powerIcon).rectTransform).localScale = powerScale; powerSwapped = false; } } private static void RememberGameIcon(Image img, string spriteName, out Sprite sprite, out Color color, out Vector3 scale, out bool preserve) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) sprite = null; color = Color.white; scale = Vector3.one; preserve = false; if ((Object)(object)img == (Object)null) { return; } if ((!((Object)(object)img.sprite != (Object)null) || !((Object)img.sprite).name.StartsWith("HeaveHo_")) && (Object)(object)img.sprite != (Object)null) { sprite = img.sprite; color = ((Graphic)img).color; scale = ((Transform)((Graphic)img).rectTransform).localScale; preserve = img.preserveAspect; return; } Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>(); foreach (Sprite val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == spriteName) { sprite = val; break; } } if ((Object)(object)sprite != (Object)null) { img.sprite = sprite; ((Graphic)img).color = Color.white; ((Transform)((Graphic)img).rectTransform).localScale = Vector3.one; img.preserveAspect = false; Plugin.Log.LogInfo((object)("[HUD] put the game's '" + spriteName + "' icon back (an earlier copy of this mod had replaced it).")); } } private static void RestoreWheel() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (iconSwapped && !((Object)(object)wheelIcon == (Object)null)) { wheelIcon.sprite = wheelSprite; ((Graphic)wheelIcon).color = wheelColor; wheelIcon.preserveAspect = wheelPreserveAspect; ((Transform)((Graphic)wheelIcon).rectTransform).localRotation = wheelRotation; iconSwapped = false; } } private static void Init(Hud hud) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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) bool flag = !initialized; initialized = true; hudInstance = hud; iconSwapped = false; hideWhileRowing.Clear(); if (flag) { LogShipHud(hud); } root = GetObject(hud, "m_shipHudRoot"); controlsRoot = GetObject(hud, "m_shipControlsRoot"); arrowBack = GetObject(hud, "m_rudderBackward"); arrowSlow = GetObject(hud, "m_rudderSlow"); arrowForward = GetObject(hud, "m_rudderForward"); arrowFast = GetObject(hud, "m_rudderFastForward"); object? obj = AccessTools.Field(typeof(Hud), "m_shipRudderIcon")?.GetValue(hud); wheelIcon = (Image)((obj is Image) ? obj : null); GameObject obj2 = GetObject(hud, "m_rudder"); powerIcon = ((obj2 != null) ? obj2.GetComponent<Image>() : null); iconSwapped = false; powerSwapped = false; RememberGameIcon(wheelIcon, "rudder", out wheelSprite, out wheelColor, out var _, out wheelPreserveAspect); wheelRotation = (((Object)(object)wheelIcon != (Object)null) ? ((Transform)((Graphic)wheelIcon).rectTransform).localRotation : Quaternion.identity); wheelColor = GameWheelColor; if ((Object)(object)wheelIcon != (Object)null) { ((Graphic)wheelIcon).color = wheelColor; } RememberGameIcon(powerIcon, "ship_rudder_icon", out powerSprite, out powerColor, out powerScale, out powerPreserveAspect); if ((Object)(object)wheelIcon == (Object)null && (Object)(object)oarIcon != (Object)null) { Plugin.Log.LogWarning((object)"[HUD] m_shipRudderIcon not found; oar icon won't replace the wheel."); } windParts.Clear(); windHidden = false; string[] windFields = WindFields; foreach (string fieldName in windFields) { GameObject val = GetObject(hud, fieldName); if (!((Object)(object)val == (Object)null)) { windParts.Add(val); if (!val.activeSelf) { val.SetActive(true); } } } string[] hideFields = HideFields; foreach (string fieldName2 in hideFields) { GameObject val2 = GetObject(hud, fieldName2); if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)controlsRoot && (Object)(object)val2 != (Object)(object)root && ((Object)(object)wheelIcon == (Object)null || (Object)(object)val2 != (Object)(object)((Component)wheelIcon).gameObject)) { hideWhileRowing.Add(val2); } } Working = (Object)(object)root != (Object)null && (Object)(object)arrowBack != (Object)null && (Object)(object)arrowSlow != (Object)null && (Object)(object)arrowForward != (Object)null && (Object)(object)arrowFast != (Object)null; if (Working) { Plugin.Log.LogInfo((object)"Rower HUD hooked into the ship HUD."); } else { Plugin.Log.LogWarning((object)"Rower HUD: core ship HUD fields missing; using text messages instead. See the [HUD] lines above for the real names."); } } private static GameObject GetObject(Hud hud, string fieldName) { FieldInfo fieldInfo = AccessTools.Field(typeof(Hud), fieldName); if (fieldInfo == null) { Plugin.Log.LogWarning((object)("[HUD] field '" + fieldName + "' not found")); return null; } object value = fieldInfo.GetValue(hud); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { return val; } Component val2 = (Component)((value is Component) ? value : null); if (val2 != null) { return val2.gameObject; } return null; } private static void LogShipHud(Hud hud) { StringBuilder stringBuilder = new StringBuilder("[HUD] ship-related Hud fields:\n"); foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(typeof(Hud))) { string text = declaredField.Name.ToLowerInvariant(); if (text.Contains("ship") || text.Contains("rudder") || text.Contains("sail") || text.Contains("wind")) { object value = declaredField.GetValue(declaredField.IsStatic ? null : hud); GameObject val = (GameObject)((value is GameObject) ? value : null); object obj; if (val == null) { Component val2 = (Component)((value is Component) ? value : null); obj = ((val2 != null) ? val2.gameObject : null); } else { obj = val; } GameObject val3 = (GameObject)obj; stringBuilder.AppendLine(((Object)(object)val3 != (Object)null) ? (" " + declaredField.Name + " (" + declaredField.FieldType.Name + ") -> '" + HierarchyPath(val3.transform) + "'") : (" " + declaredField.Name + " (" + declaredField.FieldType.Name + ")")); } } object? obj2 = AccessTools.Field(typeof(Hud), "m_shipHudRoot")?.GetValue(hud); GameObject val4 = (GameObject)((obj2 is GameObject) ? obj2 : null); if ((Object)(object)val4 != (Object)null) { stringBuilder.AppendLine("[HUD] ship HUD hierarchy:"); DumpTree(val4.transform, 1, stringBuilder); } Plugin.Log.LogDebug((object)stringBuilder.ToString()); } private static void DumpTree(Transform t, int depth, StringBuilder sb) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown if (depth > 5) { return; } Image component = ((Component)t).GetComponent<Image>(); string text = (((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) ? (" sprite=" + ((Object)component.sprite).name) : ""); sb.AppendLine(new string(' ', depth * 2) + ((Object)t).name + text); foreach (Transform item in t) { Transform t2 = item; DumpTree(t2, depth + 1, sb); } } private static string HierarchyPath(Transform t) { string text = ((Object)t).name; while ((Object)(object)t.parent != (Object)null) { t = t.parent; text = ((Object)t).name + "/" + text; } return text; } } public class RowingShip : MonoBehaviour { private struct SeatPing { public float Time; public int Gear; } private class Oar { public Transform Pivot; public Transform Shaft; public Transform Blade; public Transform Model; public int Seat; public float Blend; public Transform Tip; public bool InWater; public float Out; public bool OutKnown; public bool WaterKnown; public float Water; public float WaterSmooth; } internal static RowingShip LocalRowingShip; private const int GearBack = -1; private const int GearNone = 0; private const int GearEasy = 1; private const int GearHard = 2; private const string RpcRow = "HeaveHo_Row"; private const float PingInterval = 1f; private const float PingTimeout = 2.5f; private const int MaxSeats = 32; private const float OarInboard = 0.6f; private static readonly int ZdoRowMask = StringExtensionMethods.GetStableHashCode("HeaveHo_RowerMask"); private static readonly int ZdoHardMask = StringExtensionMethods.GetStableHashCode("HeaveHo_HardMask"); private static readonly int ZdoBackMask = StringExtensionMethods.GetStableHashCode("HeaveHo_BackMask"); private static readonly FieldRef<Player, Transform> PlayerAttachPoint = AccessTools.FieldRefAccess<Player, Transform>("m_attachPoint"); private static readonly List<RowingShip> All = new List<RowingShip>(); private static readonly HashSet<string> loggedShips = new HashSet<string>(); private static readonly HashSet<string> loggedHulls = new HashSet<string>(); private static readonly FieldInfo ForceDisableInit = AccessTools.Field(typeof(ZNetView), "m_forceDisableInit"); private bool preview; private ZNetView nview; private Rigidbody body; private Ship shipComponent; private bool sailing; private Vector3 lastPosition; private float speedSmoothed; private Chair[] seats = Array.Empty<Chair>(); private Vector3[] seatLocal = Array.Empty<Vector3>(); private float[] seatSide = Array.Empty<float>(); private readonly Dictionary<int, SeatPing> seatPings = new Dictionary<int, SeatPing>(); private readonly List<int> staleSeats = new List<int>(); private int localSeat = -1; private int localGear; private bool localRowing; private int lastSentCode = int.MinValue; private bool exhausted; private float pingTimer; private bool upHeld; private bool downHeld; private Oar[] oars = Array.Empty<Oar>(); private readonly float[] gearPhase = new float[3]; private Plugin.ShipOarSettings shipCfg; private ShipProfile profile; private float[] portYaw = Array.Empty<float>(); private Plugin.OarModel oarModel; private Transform[] fittings = Array.Empty<Transform>(); private Vector3[] railPoint = Array.Empty<Vector3>(); private bool[] railFound = Array.Empty<bool>(); private int[] railGroupIndex = Array.Empty<int>(); private int[] railGroupCount = Array.Empty<int>(); private Material fittingMaterial; private Material objMaterial; private static bool Headless => (int)SystemInfo.graphicsDeviceType == 4; private float Outboard { get { if (shipCfg == null || shipCfg.Outboard == null) { return 1.1f; } return shipCfg.Outboard.Value; } } private float OarlockHeight { get { if (shipCfg == null || shipCfg.Height == null) { return 0.5f; } return shipCfg.Height.Value; } } private bool PerSeat { get { if (profile == null) { if (shipCfg != null) { return shipCfg.Placement.Value == "Per seat"; } return false; } return true; } } private bool VisualsOn { get { if (shipCfg != null) { return shipCfg.Visuals.Value; } return true; } } private bool PhysicsOn { get { if (shipCfg != null) { return shipCfg.Physics.Value; } return true; } } private float OarScale => ((shipCfg != null) ? shipCfg.Scale.Value : 1f) * Plugin.OarModelScale.Value; private bool Valid { get { if ((Object)(object)nview != (Object)null && nview.IsValid()) { return nview.GetZDO() != null; } return false; } } internal int LocalGear => localGear; internal float LocalStrokeLift { get { if (localGear != 0) { return 0.5f + 0.5f * Mathf.Cos(gearPhase[(localGear != -1) ? ((localGear == 1) ? 1 : 2) : 0]); } return 0f; } } internal Vector3 LocalHudAnchor { get { //IL_001f: 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) //IL_002e: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if (localSeat < 0 || localSeat >= seats.Length) { return ((Component)this).transform.position + Vector3.up * 2f; } Vector3 val = OarlockLocal(localSeat); float num = Mathf.Min(Mathf.Abs(val.x - seatLocal[localSeat].x), 1.2f); val.x = seatLocal[localSeat].x + seatSide[localSeat] * num; val.x += seatSide[localSeat] * Plugin.HudOutboard.Value; return ((Component)this).transform.TransformPoint(val) + Vector3.up * Plugin.HudHeight.Value; } } private void Awake() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) nview = ((Component)this).GetComponent<ZNetView>(); preview = ForceDisableInit != null && (bool)ForceDisableInit.GetValue(null); body = ((Component)this).GetComponent<Rigidbody>(); shipComponent = ((Component)this).GetComponent<Ship>(); lastPosition = ((Component)this).transform.position; FindRowingSeats(); All.Add(this); if ((Object)(object)nview != (Object)null && nview.IsValid()) { nview.Register<int>("HeaveHo_Row", (Action<long, int>)RPC_Row); } } internal static void DestroyAll() { RowingShip[] array = All.ToArray(); foreach (RowingShip rowingShip in array) { if ((Object)(object)rowingShip != (Object)null) { Object.Destroy((Object)(object)rowingShip); } } All.Clear(); LocalRowingShip = null; } private void Start() { if (seats.Length != 0) { ResolveOarlock(); } if (seats.Length != 0 && !Headless && (preview || ((Object)(object)nview != (Object)null && nview.IsValid()))) { BuildOars(); } } private void ResolveOarlock() { string prefabName = Utils.GetPrefabName(((Component)this).gameObject); shipCfg = Plugin.GetShipSettings(prefabName, MeasureOarlock); profile = ShipProfile.For(prefabName); if (PerSeat) { MeasurePerSeat(); } } private Vector3 OarlockLocal(int s) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_00f2: Unknown result type (might be due to invalid IL or missing references) if (PerSeat && s < railPoint.Length) { Vector3 result = (Vector3)(railFound[s] ? railPoint[s] : new Vector3(seatSide[s] * Outboard, seatLocal[s].y + OarlockHeight, seatLocal[s].z)); result.x += seatSide[s] * shipCfg.PerSeatOutboard.Value; result.y += shipCfg.PerSeatHeight.Value; if (shipCfg.Spacing != null) { result.z += ((float)railGroupIndex[s] - (float)(railGroupCount[s] - 1) * 0.5f) * shipCfg.Spacing.Value; } return result; } return new Vector3(seatSide[s] * Outboard, seatLocal[s].y + OarlockHeight, seatLocal[s].z); } private bool MeasureOarlock(out float distance, out float height) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) int num = seats.Length / 2; if (MeasureRail(seatLocal[num], seatSide[num], out distance, out var railY)) { height = railY - seatLocal[num].y; return true; } height = 0f; return false; } private bool MeasureRail(Vector3 seat, float side, out float distance, out float railY) { //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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) distance = 0f; railY = 0f; Ship component = ((Component)this).GetComponent<Ship>(); Vector3 val = ((Component)this).transform.TransformDirection(new Vector3(0f - side, 0f, 0f)); for (float num = 2.5f; num >= -0.3f; num -= 0.05f) { Vector3 val2 = ((Component)this).transform.TransformPoint(new Vector3(side * 20f, seat.y + num, seat.z)); float num2 = -1f; RaycastHit[] array = Physics.RaycastAll(val2, val, 20f, -1, (QueryTriggerInteraction)1); for (int i = 0; i < array.Length; i++) { RaycastHit val3 = array[i]; if (!((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null) && !((Object)(object)((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<Ship>() != (Object)(object)component)) { float num3 = Mathf.Abs(((Component)this).transform.InverseTransformPoint(((RaycastHit)(ref val3)).point).x); if (num3 > num2) { num2 = num3; } } } if (num2 > 0.2f) { distance = num2; railY = seat.y + num + 0.05f; return true; } } return false; } private void MeasurePerSeat() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: 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) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) int num = seats.Length; railPoint = (Vector3[])(object)new Vector3[num]; railFound = new bool[num]; railGroupIndex = new int[num]; railGroupCount = new int[num]; int num2 = 0; portYaw = new float[num]; for (int i = 0; i < num; i++) { float distance; float railY; if (profile != null && profile.NearestPort(seatLocal[i].z, out var port)) { railPoint[i] = new Vector3(seatSide[i] * port.X, profile.PortY, port.Z); railFound[i] = true; portYaw[i] = (0f - seatSide[i]) * port.Yaw; num2++; } else if (MeasureRail(seatLocal[i], seatSide[i], out distance, out railY)) { railPoint[i] = new Vector3(seatSide[i] * distance, railY, seatLocal[i].z); railFound[i] = true; num2++; } } Dictionary<string, List<int>> dictionary = new Dictionary<string, List<int>>(); for (int j = 0; j < num; j++) { string key = $"{seatSide[j]}:{Mathf.Round(seatLocal[j].z * 2f)}"; if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<int>()); } value.Add(j); } foreach (List<int> value2 in dictionary.Values) { value2.Sort((int a, int b) => Mathf.Abs(seatLocal[a].x).CompareTo(Mathf.Abs(seatLocal[b].x))); for (int num3 = 0; num3 < value2.Count; num3++) { railGroupIndex[value2[num3]] = num3; railGroupCount[value2[num3]] = value2.Count; } } Plugin.Log.LogInfo((object)(string.Format("[{0}] per-seat oarlocks: {1} found for {2}/{3} seats, ", Utils.GetPrefabName(((Component)this).gameObject), (profile != null) ? "oar port" : "rail", num2, num) + $"{dictionary.Count} rail groups")); } private void OnDestroy() { All.Remove(this); if ((Object)(object)LocalRowingShip == (Object)(object)this) { LocalRowingShip = null; } if ((Object)(object)nview != (Object)null && nview.IsValid()) { nview.Unregister("HeaveHo_Row"); } Oar[] array = oars; foreach (Oar oar in array) { if (oar != null && (Object)(object)oar.Pivot != (Object)null) { Object.Destroy((Object)(object)((Component)oar.Pivot).gameObject); } } Transform[] array2 = fittings; foreach (Transform val in array2) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } if ((Object)(object)objMaterial != (Object)null) { Object.Destroy((Object)(object)objMaterial); } if ((Object)(object)fittingMaterial != (Object)null) { Object.Destroy((Object)(object)fittingMaterial); } } private static Transform SeatPoint(Chair c) { if (!((Object)(object)c.m_attachPoint != (Object)null)) { return ((Component)c).transform; } return c.m_attachPoint; } private void FindRowingSeats() { //IL_00f7: 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_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(); string[] array = Plugin.RowingSeatAnimations.Value.Split(','); foreach (string text in array) { if (text.Trim().Length > 0) { hashSet.Add(text.Trim()); } } List<string> list = new List<string>(); string[] array2 = Plugin.GetSeatNameFilter(Utils.GetPrefabName(((Component)this).gameObject)).Value.Split(','); foreach (string text2 in array2) { if (text2.Trim().Length > 0) { list.Add(text2.Trim().ToLowerInvariant()); } } List<Vector3> list2 = new List<Vector3>(); ShipControlls[] componentsInChildren = ((Component)this).GetComponentsInChildren<ShipControlls>(true); foreach (ShipControlls val in componentsInChildren) { list2.Add(((Component)this).transform.InverseTransformPoint(((Object)(object)val.m_attachPoint != (Object)null) ? val.m_attachPoint.position : ((Component)val).transform.position)); } Chair[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Chair>(true); List<Chair> list3 = new List<Chair>(); HashSet<Chair> hashSet2 = new HashSet<Chair>(); Chair[] array3 = componentsInChildren2; foreach (Chair val2 in array3) { if (!hashSet.Contains(val2.m_attachAnimation) || list3.Count >= 32) { continue; } string cn = ((Object)val2).name.ToLowerInvariant(); if (list.Count <= 0 || list.Exists((string n) => cn.StartsWith(n))) { Vector3 at = ((Component)this).transform.InverseTransformPoint(SeatPoint(val2).position); if (list2.Exists((Vector3 h) => Vector3.Distance(h, at) < 2f)) { hashSet2.Add(val2); } else { list3.Add(val2); } } } seats = list3.ToArray(); seatLocal = (Vector3[])(object)new Vector3[seats.Length]; seatSide = new float[seats.Length]; for (int num = 0; num < seats.Length; num++) { seatLocal[num] = ((Component)this).transform.InverseTransformPoint(SeatPoint(seats[num]).position); seatSide[num] = ((Mathf.Abs(seatLocal[num].x) > 0.05f) ? Mathf.Sign(seatLocal[num].x) : ((num % 2 == 0) ? 1f : (-1f))); } string prefabName = Utils.GetPrefabName(((Component)this).gameObject); if (loggedShips.Add(prefabName)) { Chair[] array4 = componentsInChildren2; foreach (Chair val3 in array4) { Vector3 val4 = ((Component)this).transform.InverseTransformPoint(SeatPoint(val3).position); string text3 = (list3.Contains(val3) ? "ROWS" : (hashSet2.Contains(val3) ? "excluded (helm)" : "excluded")); Plugin.Log.LogInfo((object)("[" + prefabName + "] seat '" + ((Object)val3).name + "' anim='" + val3.m_attachAnimation + "' " + $"sit=({val4.x:F2},{val4.y:F2},{val4.z:F2}) {text3}")); } } } private void Update() { if (!Valid) { if (preview) { UpdateOars(0, 0, 0, Mathf.Max(Time.deltaTime, 0.016f)); } return; } float deltaTime = Time.deltaTime; if (!(deltaTime <= 0f)) { UpdateLocalRower(deltaTime); ZDO zDO = nview.GetZDO(); UpdateOars(zDO.GetInt(ZdoRowMask, 0), zDO.GetInt(ZdoHardMask, 0), zDO.GetInt(ZdoBackMask, 0), deltaTime); } } internal void HandleGearInput(float forward) { bool flag = forward > 0.5f; bool flag2 = forward < -0.5f; if (flag && !upHeld) { ShiftGear(1); } if (flag2 && !downHeld) { ShiftGear(-1); } upHeld = flag; downHeld = flag2; } private void ShiftGear(int delta) { int num = Mathf.Clamp(localGear + delta, -1, 2); if (num == localGear) { return; } localGear = num; if (!RowerHud.Working) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, GearName(localGear), 0, (Sprite)null, false); } } } private static string KeyToken(string keyboard, string gamepad) { return "<color=yellow><b>$KEY_" + (ZInput.IsGamepadActive() ? gamepad : keyboard) + "</b></color>"; } private static string GearName(int gear) { return gear switch { -1 => "< " + Translations.T("heaveho_gear_back"), 1 => "> " + Translations.T("heaveho_gear_easy"), 2 => ">> " + Translations.T("heaveho_gear_hard"), _ => Translations.T("heaveho_gear_none"), }; } private void UpdateLocalRower(float dt) { Player localPlayer = Player.m_localPlayer; int num = FindLocalSeat(localPlayer); if (num < 0) { if ((Object)(object)LocalRowingShip == (Object)(object)this) { LocalRowingShip = null; } localGear = 0; upHeld = (downHeld = false); } else if ((Object)(object)LocalRowingShip != (Object)(object)this) { LocalRowingShip = this; localGear = 0; upHeld = (downHeld = false); ((Character)localPlayer).Message((MessageType)1, Translations.T("heaveho_hint", KeyToken("Forward", "LStickUp"), KeyToken("Backward", "LStickDown"), KeyToken("Jump", "Jump")), 0, (Sprite)null, false); } if (num >= 0 && localGear != 0) { float stamina = localPlayer.GetStamina(); if (exhausted && stamina >= Plugin.RecoverStamina.Value) { exhausted = false; } else if (!exhausted && stamina <= 0.5f) { exhausted = true; ((Character)localPlayer).Message((MessageType)2, Translations.T("heaveho_tired"), 0, (Sprite)null, false); } } bool flag = num >= 0 && localGear != 0 && !exhausted; if (flag) { float num2 = ((localGear == 2) ? 1f : Plugin.EasyStaminaMultiplier.Value); if (PhysicsOn) { ((Character)localPlayer).UseStamina(Plugin.StaminaPerSecond.Value * num2 * dt); } } if (localRowing && (!flag || num != localSeat)) { SendRow(-localSeat - 1); lastSentCode = int.MinValue; } if (flag) { int num3 = num * 4 + (localGear + 1); pingTimer -= dt; if (num3 != lastSentCode || pingTimer <= 0f) { SendRow(num3); lastSentCode = num3; pingTimer = 1f; } } localRowing = flag; localSeat = num; } private int FindLocalSeat(Player player) { if ((Object)(object)player == (Object)null || seats.Length == 0 || !((Character)player).IsAttached()) { return -1; } if (!VisualsOn && !PhysicsOn) { return -1; } Transform val = PlayerAttachPoint.Invoke(player); if ((Object)(object)val == (Object)null) { return -1; } Chair componentInParent = ((Component)val).GetComponentInParent<Chair>(); if ((Object)(object)componentInParent == (Object)null) { return -1; } return Array.IndexOf(seats, componentInParent); } private void SendRow(int code) { if (Valid) { nview.InvokeRPC("HeaveHo_Row", new object[1] { code }); } } private void RPC_Row(long sender, int code) { if (nview.IsOwner() && code < 128 && code >= -32) { if (code >= 0) { seatPings[code / 4] = new SeatPing { Time = Time.time, Gear = code % 4 - 1 }; } else { seatPings.Remove(-code - 1); } } } private void FixedUpdate() { if (!Valid || !nview.IsOwner()) { return; } float time = Time.time; int num = 0; int num2 = 0; int num3 = 0; staleSeats.Clear(); foreach (KeyValuePair<int, SeatPing> seatPing in seatPings) { int key = seatPing.Key; if (time - seatPing.Value.Time > 2.5f || key < 0 || key >= seats.Length) { staleSeats.Add(key); continue; } num |= 1 << key; if (seatPing.Value.Gear == 2) { num2 |= 1 << key; } if (seatPing.Value.Gear == -1) { num3 |= 1 << key; } } foreach (int staleSeat in staleSeats) { seatPings.Remove(staleSeat); } ZDO zDO = nview.GetZDO(); if (zDO.GetInt(ZdoRowMask, 0) != num) { zDO.Set(ZdoRowMask, num, false); } if (zDO.GetInt(ZdoHardMask, 0) != num2) { zDO.Set(ZdoHardMask, num2, false); } if (zDO.GetInt(ZdoBackMask, 0) != num3) { zDO.Set(ZdoBackMask, num3, false); } ApplyRowingForces(num, num2, num3); } private void ApplyRowingForces(int rowMask, int hardMask, int backMask) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //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_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_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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) if (rowMask == 0 || (Object)(object)body == (Object)null || !PhysicsOn) { return; } Vector3 val = Vector3.ProjectOnPlane(((Component)this).transform.forward, Vector3.up); Vector3 normalized = ((Vector3)(ref val)).normalized; float num = Vector3.Dot(body.linearVelocity, normalized); Vector3 worldCenterOfMass = body.worldCenterOfMass; Vector3 val2 = Vector3.zero; Vector3 val3 = Vector3.zero; int num2 = 0; for (int i = 0; i < seats.Length; i++) { if ((rowMask & (1 << i)) != 0) { bool flag = (backMask & (1 << i)) != 0; bool flag2 = (hardMask & (1 << i)) != 0; float num3 = (flag ? (-1f) : 1f); float num4 = (flag2 ? 1f : (flag ? Plugin.BackMultiplier.Value : Plugin.EasyMultiplier.Value)); float num5 = Plugin.RowerBonus(num2++); if (!(num * num3 >= Plugin.MaxRowingSpeed.Value)) { Vector3 val4 = normalized * (num3 * Plugin.RowerStrength.Value * num5 * num4 * body.mass); Vector3 val5 = ((Component)this).transform.TransformPoint(OarlockLocal(i)); val2 += val4; val3 += Vector3.Cross(val5 - worldCenterOfMass, val4); } } } body.AddForce(val2, (ForceMode)0); Vector3 val6 = Vector3.Project(val3, ((Component)this).transform.up) * Plugin.TurnStrength.Value; body.AddTorque(val6, (ForceMode)0); } private void BuildOars() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) MeshRenderer val = FindHullRenderer(); Material val2 = (((Object)(object)val != (Object)null) ? ((Renderer)val).sharedMaterial : null); int layer = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject.layer : ((Component)this).gameObject.layer); oarModel = Plugin.OarFor(Utils.GetPrefabName(((Component)this).gameObject)); BuildFittings(val2, layer); oars = new Oar[seats.Length]; for (int i = 0; i < seats.Length; i++) { Transform transform = new GameObject("HeaveHo_Oar_" + i).transform; transform.SetParent(((Component)this).transform, false); if (oarModel != null) { oars[i] = new Oar { Pivot = transform, Model = SpawnObjModel(transform, val2, layer), Seat = i }; } else if ((Object)(object)Plugin.OarPrefab != (Object)null) { oars[i] = new Oar { Pivot = transform, Model = SpawnModel(transform, val2, layer), Seat = i }; } else { oars[i] = new Oar { Pivot = transform, Shaft = MakePart((PrimitiveType)2, transform, val2, layer), Blade = MakePart((PrimitiveType)3, transform, val2, layer), Seat = i }; } Transform transform2 = new GameObject("HeaveHo_BladeTip").transform; transform2.SetParent(transform, false); oars[i].Tip = transform2; } } private void BuildFittings(Material hullMaterial, int layer) { //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_0048: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown Plugin.OarModel oarModel = ((profile != null && profile.Fitting) ? Plugin.FittingFor(Utils.GetPrefabName(((Component)this).gameObject)) : null); if (oarModel != null && !((Object)(object)hullMaterial == (Object)null)) { Material val = new Material(hullMaterial) { name = "HeaveHo_Oarlock" }; if (val.HasProperty("_BumpMap")) { val.SetTexture("_BumpMap", (Texture)null); } val.DisableKeyword("_NORMALMAP"); fittingMaterial = val; fittings = (Transform[])(object)new Transform[seats.Length]; for (int i = 0; i < seats.Length; i++) { GameObject val2 = new GameObject("HeaveHo_Oarlock_" + i) { layer = layer }; val2.transform.SetParent(((Component)this).transform, false); val2.AddComponent<MeshFilter>().sharedMesh = oarModel.Mesh; ((Renderer)val2.AddComponent<MeshRenderer>()).sharedMaterial = val; fittings[i] = val2.transform; } } } internal static float WaterLevelAt(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) float liquidLevel = Floating.GetLiquidLevel(p, 1f, (LiquidType)10); if (liquidLevel > -1000f) { return liquidLevel; } if (!((Object)(object)ZoneSystem.instance != (Object)null)) { return 30f; } return ZoneSystem.instance.m_waterLevel; } private void UpdateSplash(Oar oar, bool rowing, bool hard) { //IL_0039: 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_0047: 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_005e: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)oar.Tip == (Object)null) { return; } if (!rowing || oar.Blend < 0.95f || !Plugin.Splashes.Value) { oar.InWater = false; return; } Vector3 position = oar.Tip.position; float num = (oar.WaterKnown ? oar.Water : WaterLevelAt(position)); bool flag = (oar.InWater ? (position.y < num + 0.08f) : (position.y < num - 0.02f)); if (flag != oar.InWater) { oar.InWater = flag; SplashEffects.Spawn(new Vector3(position.x, num + 0.02f, position.z), ((Component)this).transform.rotation, flag, hard); } } private static bool IsWaterMask(Renderer r, Material m) { string text = ((Object)r).name.ToLowerInvariant(); string text2 = (((Object)(object)m.shader != (Object)null) ? ((Object)m.shader).name.ToLowerInvariant() : ""); if (!text.Contains("watermask") && !text.Contains("water_mask") && !text2.Contains("watermask")) { return text2.Contains("water mask"); } return true; } private Transform SpawnObjModel(Transform pivot, Material hullMaterial, int layer) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0054: 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_0069: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("HeaveHo_OarModel"); val.layer = layer; val.transform.SetParent(pivot, false); val.AddComponent<MeshFilter>().sharedMesh = oarModel.Mesh; if ((Object)(object)objMaterial == (Object)null && (Object)(object)hullMaterial != (Object)null) { objMaterial = new Material(hullMaterial) { name = "HeaveHo_OarObj" }; if (objMaterial.HasProperty("_MainTex") && !oarModel.HullTexture) { objMaterial.SetTexture("_MainTex", (Texture)(object)oarModel.Texture); objMaterial.SetTextureScale("_MainTex", Vector2.one); objMaterial.SetTextureOffset("_MainTex", Vector2.zero); } if (objMaterial.HasProperty("_Color") && !oarModel.HullTexture) { objMaterial.SetColor("_Color", Color.white); } if (objMaterial.HasProperty("_BumpMap")) { objMaterial.SetTexture("_BumpMap", (Texture)null); } objMaterial.DisableKeyword("_NORMALMAP"); } MeshRenderer val2 = val.AddComponent<MeshRenderer>(); if ((Object)(object)objMaterial != (Object)null) { ((Renderer)val2).sharedMaterial = objMaterial; } return val.transform; } private static Transform SpawnModel(Transform pivot, Material hullMaterial, int layer) { //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) //IL_00d4: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(Plugin.OarPrefab, pivot, false); ((Object)val).name = "HeaveHo_OarModel"; Collider[] componentsInChildren = val.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren) { Object.DestroyImmediate((Object)(object)val2); } Transform[] componentsInChildren2 = val.GetComponentsInChildren<Transform>(true); foreach (Transform val3 in componentsInChildren2) { ((Component)val3).gameObject.layer = layer; } if ((Object)(object)hullMaterial != (Object)null) { Renderer[] componentsInChildren3 = val.GetComponentsInChildren<Renderer>(true); foreach (Renderer val4 in componentsInChildren3) { Material[] sharedMaterials = val4.sharedMaterials; for (int l = 0; l < sharedMaterials.Length; l++) { Material val5 = sharedMaterials[l]; Material val6 = new Material(hullMaterial) { name = "HeaveHo_Oar_" + (((Object)(object)val5 != (Object)null) ? ((Object)val5).name : "mat") }; if ((Object)(object)val5 != (Object)null) { if (val5.HasProperty("_MainTex") && val6.HasProperty("_MainTex")) { val6.SetTexture("_MainTex", val5.GetTexture("_MainTex")); val6.SetTextureScale("_MainTex", Vector2.one); val6.SetTextureOffset("_MainTex", Vector2.zero); } if (val5.HasProperty("_Color") && val6.HasProperty("_Color")) { val6.SetColor("_Color", val5.GetColor("_Color")); } if (val6.HasProperty("_BumpMap")) { val6.SetTexture("_BumpMap", (Texture)null); } val6.DisableKeyword("_NORMALMAP"); } sharedMaterials[l] = val6; } val4.sharedMaterials = sharedMaterials; } } return val.transform; } private MeshRenderer FindHullRenderer() { //IL_0086: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) MeshRenderer val = null; float num = float.MinValue; MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val2 in componentsInChildren) { Material sharedMaterial = ((Renderer)val2).sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null) && !IsWaterMask((Renderer)(object)val2, sharedMaterial) && ((Component)val2).gameObject.activeInHierarchy && !((Object)val2).name.StartsWith("HeaveHo_") && sharedMaterial.renderQueue < 2500) { Bounds bounds = ((Renderer)val2).bounds; Vector3 size = ((Bounds)(ref bounds)).size; float num2 = ((Vector3)(ref size)).sqrMagnitude; if (((Object)sharedMaterial.shader).name == "Custom/Piece") { num2 += 10000f; } if (((Object)val2).name.Equals("hull", StringComparison.OrdinalIgnoreCase)) { num2 += 100000f; } if (num2 > num) { val = val2; num = num2; } } } if ((Object)(object)val != (Object)null && loggedHulls.Add(Utils.GetPrefabName(((Component)this).gameObject))) { Plugin.Log.LogInfo((object)("[" + Utils.GetPrefabName(((Component)this).gameObject) + "] oar material from '" + ((Object)val).name + "' (shader '" + ((Object)((Renderer)val).sharedMaterial.shader).name + "')")); } return val; } private static Transform MakePart(PrimitiveType type, Transform parent, Material mat, int layer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.CreatePrimitive(type); Object.DestroyImmediate((Object)(object)val.GetComponent<Collider>()); val.layer = layer; val.transform.SetParent(parent, false); if ((Object)(object)mat != (Object)null) { ((Renderer)val.GetComponent<MeshRenderer>()).sharedMaterial = mat; } return val.transform; } private void UpdateOars(int rowMask, int hardMask, int backMask, float dt) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_06cb: Unknown result type (might be due to invalid IL or missing references) //IL_06e8: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_08e1: Unknown result type (might be due to invalid IL or missing references) //IL_0904: Unknown result type (might be due to invalid IL or missing references) //IL_092c: Unknown result type (might be due to invalid IL or missing references) //IL_094c: Unknown result type (might be due to invalid IL or missing references) //IL_096e: Unknown result type (might be due to invalid IL or missing references) //IL_0776: Unknown result type (might be due to invalid IL or missing references) //IL_072c: Unknown result type (might be due to invalid IL or missing references) //IL_0731: Unknown result type (might be due to invalid IL or missing references) //IL_0735: Unknown result type (might be due to invalid IL or missing references) //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_087d: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_051e: Unknown result type (might be due to invalid IL or missing references) //IL_08b3: Unknown result type (might be due to invalid IL or missing references) //IL_0892: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Unknown result type (might be due to invalid IL or missing references) //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05cf: Unknown result type (might be due to invalid IL or missing references) //IL_05ea: Unknown result type (might be due to invalid IL or missing references) //IL_061b: Unknown result type (might be due to invalid IL or missing references) //IL_061d: Unknown result type (might be due to invalid IL or missing references) if (oars.Length == 0) { return; } float num = Plugin.StrokesPerSecond.Value * MathF.PI * 2f; gearPhase[0] = (gearPhase[0] + dt * num * 0.75f) % (MathF.PI * 2f); gearPhase[1] = (gearPhase[1] + dt * num * 0.75f) % (MathF.PI * 2f); gearPhase[2] = (gearPhase[2] + dt * num * 1.2f) % (MathF.PI * 2f); bool flag = Plugin.ShowOars.Value && VisualsOn; float num2 = 3.2f * ((shipCfg != null) ? shipCfg.Scale.Value : 1f); Vector3 val = Vector3.ProjectOnPlane(((Component)this).transform.position - lastPosition, Vector3.up); float num3 = ((Vector3)(ref val)).magnitude / dt; lastPosition = ((Component)this).transform.position; speedSmoothed = Mathf.Lerp(speedSmoothed, num3, 1f - Mathf.Exp(-2f * dt)); bool flag2 = (Object)(object)shipComponent != (Object)null && shipComponent.IsSailUp(); sailing = flag2 && speedSmoothed > (sailing ? 1f : 2f); bool flag3 = sailing && Plugin.ShipUnderSail.Value; for (int i = 0; i < fittings.Length; i++) { if (((Component)fittings[i]).gameObject.activeSelf != flag) { ((Component)fittings[i]).gameObject.SetActive(flag); } if (flag) { fittings[i].localPosition = OarlockLocal(i); fittings[i].localRotation = Quaternion.Euler(profile.FittingPitch, profile.FittingYaw * seatSide[i], profile.FittingRoll * seatSide[i]); } } Oar[] array = oars; foreach (Oar oar in array) { if (((Component)oar.Pivot).gameObject.activeSelf != flag) { ((Component)oar.Pivot).gameObject.SetActive(flag); } if (!flag) { continue; } int num4 = 1 << oar.Seat; bool flag4 = (rowMask & num4) != 0; bool flag5 = (backMask & num4) != 0; bool flag6 = (hardMask & num4) != 0; float num5 = (flag5 ? gearPhase[0] : (flag6 ? gearPhase[2] : gearPhase[1])); float num6 = seatSide[oar.Seat]; oar.Blend = Mathf.MoveTowards(oar.Blend, flag4 ? 1f : 0f, dt * 1.5f); float num7 = Mathf.Sin(num5); float num8 = Mathf.Cos(num5); float num9 = (flag6 ? 32f : 24f); float num10 = num9 * num6 * num7 * (flag5 ? (-1f) : 1f); float num11 = -18f - 10f * num8; float num12 = 75f * num6; float num13 = 12f; float num14 = ((num6 > 0f) ? 0f : 180f); bool flag7 = profile != null && oar.Seat < portYaw.Length; float num15 = 0f; float num16 = 0f; if (flag4 && oar.Blend > 0.5f && (Object)(object)oar.Tip != (Object)null) { oar.Water = WaterLevelAt(oar.Tip.position); oar.WaterSmooth = (oar.WaterKnown ? Mathf.Lerp(oar.WaterSmooth, oar.Water, 1f - Mathf.Exp(-5f * dt)) : oar.Water); oar.WaterKnown = true; } else { oar.WaterKnown = false; } Bounds bounds; if (flag7) { num14 += portYaw[oar.Seat]; float num17 = (flag6 ? profile.SweepHard : profile.SweepEasy); num10 = num17 * num6 * num7 * (flag5 ? (-1f) : 1f); num12 = (profile.StowAlongHull ? (profile.StowYaw * num6) : 0f); num13 = profile.StowDip; num16 = (profile.StowAlongHull ? 0f : 90f); num15 = 90f * (1f - Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(-0.25f, 0.15f, num8))); Vector3 val2 = OarlockLocal(oar.Seat); float num18; if (oarModel == null) { num18 = num2; } else { bounds = oarModel.Mesh.bounds; num18 = (((Bounds)(ref bounds)).max.x - profile.TipInset) * OarScale; } float num19 = num18; float num20 = (0f - Mathf.Asin(Mathf.Clamp((Mathf.Max(0.1f, val2.y - profile.Waterline) + 0.05f) / num19, 0f, 0.95f))) * 57.29578f; float num21 = num20; if (oar.WaterKnown) { float num22 = (num14 - num17 * num6 * (flag5 ? (-1f) : 1f)) * (MathF.PI / 180f); float num23 = ((Component)this).transform.right.y * Mathf.Cos(num22) - ((Component)this).transform.forward.y * Mathf.Sin(num22); float y = ((Component)this).transform.up.y; float num24 = Mathf.Sqrt(num23 * num23 + y * y); float num25 = (oar.WaterSmooth - 0.05f - ((Component)this).transform.TransformPoint(val2).y) / num19; float num26 = (Mathf.Asin(Mathf.Clamp(num25 / num24, -1f, 1f)) - Mathf.Atan2(num23, y)) * 57.29578f; num21 = Mathf.Clamp(num26, Mathf.Max(num20 - 14f, -60f), Mathf.Min(num20 + 8f, -1f)); } num11 = num21 - profile.DipAmplitude * num8; } float num27 = Mathf.Lerp(num12, num10, oar.Blend); float num28 = Mathf.Lerp(num13, num11, oar.Blend); oar.Pivot.localPosition = OarlockLocal(oar.Seat); oar.Pivot.localRotation = Quaternion.Euler(0f, num14 + num27, num28); if ((Object)(object)oar.Tip != (Object)null) { float num29; if (!((Object)(object)oar.Model != (Object)null) || oarModel == null) { num29 = num2 - 0.35f; } else { bounds = oarModel.Mesh.bounds; num29 = (((Bounds)(ref bounds)).max.x - ((profile != null) ? profile.TipInset : 0.3f)) * OarScale; } float num30 = num29; oar.Tip.localPosition = new Vector3(num30, 0f, 0f); } UpdateSplash(oar, flag4, flag6); if ((Object)(object)oar.Model != (Object)null) { bool flag8 = (shipCfg == null || shipCfg.ShowAtRest.Value) && !flag3; float num31 = ((flag4 || flag8) ? 1f : 0f); oar.Out = (oar.OutKnown ? Mathf.MoveTowards(oar.Out, num31, dt * 1.5f) : num31); oar.OutKnown = true; float num32 = Mathf.SmoothStep(0f, 1f, oar.Out); if (((Component)oar.Model).gameObject.activeSelf != num32 > 0.001f) { ((Component)oar.Model).gameObject.SetActive(num32 > 0.001f); } oar.Model.localScale = new Vector3(OarScale * Mathf.Max(num32, 0.001f), OarScale, OarScale); oar.Model.localRotation = (flag7 ? Quaternion.Euler(Mathf.Lerp(num16, num15, oar.Blend), 0f, 0f) : Quaternion.identity); } else { float num33 = num2 + 0.6f; oar.Shaft.localRotation = Quaternion.Euler(0f, 0f, 90f); oar.Shaft.localScale = new Vector3(0.07f, num33 * 0.5f, 0.07f); oar.Shaft.localPosition = new Vector3((num2 - 0.6f) * 0.5f, 0f, 0f); oar.Blade.localScale = new Vector3(0.7f, 0.22f, 0.04f); oar.Blade.localPosition = new Vector3(num2 - 0.35f, 0f, 0f); } } } } internal class ShipProfile { internal struct Port { public float X; public float Z; public float Yaw; public Port(float x, float z, float yaw) { X = x; Z = z; Yaw = yaw; } } internal Port[] Ports; internal float PortY; internal float Waterline; internal float SweepEasy; internal float SweepHard; internal float DipAmplitude; internal float StowDip; internal float StowYaw; internal float TipInset; internal bool StowAlongHull; internal bool OarsOutAtRest; internal bool Fitting; internal float FittingPitch; internal float FittingYaw; internal float FittingRoll; private static readonly ShipProfile Karve = new ShipProfile { Ports = new Port[1] { new Port(1f, 2.23f, 0f) }, PortY = 0.98f, Waterline = 0.15f, SweepEasy = 24f, SweepHard = 32f, DipAmplitude = 9f, StowDip = 2f, StowYaw = 80f, TipInset = 0.1f, StowAlongHull = true, OarsOutAtRest = true, Fitting = true, FittingPitch = -6f, FittingYaw = -14f, FittingRoll = 18