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 PortableShips v0.12.8
BepInEx/plugins/PortableShips/PortableShips.dll
Decompiled 5 hours 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.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using PortableShips.Compatibility; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PortableShips")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.12.8.0")] [assembly: AssemblyInformationalVersion("0.12.8")] [assembly: AssemblyProduct("PortableShips")] [assembly: AssemblyTitle("PortableShips")] [assembly: AssemblyVersion("0.12.8.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 PortableShips { internal static class AnimalBagPolicy { internal static bool Empty(string encoded) { if (string.IsNullOrEmpty(encoded)) { return true; } if (encoded.Length > 24) { return false; } try { using MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(encoded), writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream); int num = binaryReader.ReadInt32(); if (num < 1 || num > 110) { return false; } return ((num >= 108) ? binaryReader.ReadUInt16() : binaryReader.ReadInt32()) == 0 && memoryStream.Position == memoryStream.Length; } catch (Exception ex) when (ex is FormatException || ex is IOException || ex is ArgumentException) { return false; } } } internal readonly struct AnimalCapsuleShape { internal readonly float Radius; internal readonly float HalfSegment; internal readonly float RootHeight; internal AnimalCapsuleShape(float radius, float halfSegment, float rootHeight) { Radius = radius; HalfSegment = halfSegment; RootHeight = rootHeight; } internal static bool TryCreate(float radius, float height, int direction, float centerY, float sx, float sy, float sz, out AnimalCapsuleShape shape) { shape = default(AnimalCapsuleShape); if (!AnimalRules.Number(radius, 0.001f, 100f) || !AnimalRules.Number(height, 0.001f, 100f) || direction < 0 || direction > 2 || !AnimalRules.Number(centerY, -100f, 100f) || !AnimalRules.Number(sx, 0.05f, 20f) || !AnimalRules.Number(sy, 0.05f, 20f) || !AnimalRules.Number(sz, 0.05f, 20f)) { return false; } float num = direction switch { 1 => sy, 0 => sx, _ => sz, }; float num2 = radius * direction switch { 1 => Math.Max(sx, sz), 0 => Math.Max(sy, sz), _ => Math.Max(sx, sy), }; float num3 = Math.Max(0f, height * num * 0.5f - num2); shape = new AnimalCapsuleShape(num2, num3, num2 + ((direction == 1) ? num3 : 0f) - centerY * sy + 0.12f); return true; } } internal static class AnimalCast { internal static void BeginPacking(Player player, Character actor) { ShipCast.CheckPlayer(player); AnimalPackingService.Validate(player, actor); AnimalPackingAccess access = ((Component)actor).GetComponent<AnimalPackingAccess>(); ZNetView view = ((Component)actor).GetComponent<ZNetView>(); InputAndFeedback.Require(Object.op_Implicit((Object)(object)access), "Animal is still initializing. Try again shortly."); string token = TransactionJournal.NewId(); bool finished = false; ShipCast.Start(Packing(), delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!finished) { ShipCastVisualRelay.Send(player, 0); } if (Object.op_Implicit((Object)(object)access)) { access.Release(token); } }); IEnumerator Packing() { float until = Time.realtimeSinceStartup + 5f; float nextRequest = 0f; while (!AnimalPackingAccess.Owns(view, token)) { ShipCast.CheckPlayer(player); AnimalPackingService.Validate(player, actor); InputAndFeedback.Require(string.IsNullOrEmpty(access.Rejection) || access.Rejection == "retry", access.Rejection); InputAndFeedback.Require(Time.realtimeSinceStartup < until, "Animal did not respond. Try again after its state synchronizes."); if (Time.realtimeSinceStartup >= nextRequest) { nextRequest = Time.realtimeSinceStartup + 0.3f; access.BeginRequest(token); } yield return null; } IEnumerator cast = ShipCast.AnimatedCountdown(player, "Shrinking " + actor.GetHoverName(), delegate { //IL_0049: 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) ShipCast.CheckPlayer(player); AnimalPackingService.Validate(player, actor); InputAndFeedback.Require(AnimalPackingAccess.Owns(view, token), "Animal reservation changed. Try again."); ShipCastVisualRelay.Send(player, 1, ((Component)actor).transform.position, ((Component)actor).transform.rotation); }); while (cast.MoveNext()) { yield return cast.Current; } Vector3 position = ((Component)actor).transform.position; Quaternion rotation = ((Component)actor).transform.rotation; AnimalPackingService.Pack(player, actor, token); finished = true; ShipCastVisualRelay.Send(player, 2, position, rotation); } } internal static void BeginDeployment(Player player, Inventory? source, ItemData item) { //IL_004a: 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) ShipCast.CheckPlayer(player); AnimalType type = AnimalIdentification.FromItem(item); AnimalDeploymentService.Deploy(player, source, item, validateOnly: true); bool finished = false; Vector3 point = default(Vector3); ShipCast.Start(Deploying(), delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!finished) { ShipCastVisualRelay.Send(player, 0); } }); IEnumerator Deploying() { IEnumerator cast = ShipCast.AnimatedCountdown(player, "Releasing " + type.Label, delegate { //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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) ShipCast.CheckPlayer(player); point = AnimalDeploymentService.Deploy(player, source, item, validateOnly: true); ShipCastVisualRelay.Send(player, 1, point, ((Component)player).transform.rotation); }); while (cast.MoveNext()) { yield return cast.Current; } point = AnimalDeploymentService.Deploy(player, source, item); finished = true; ShipCastVisualRelay.Send(player, 2, point, ((Component)player).transform.rotation); } } } internal static class AnimalDeploymentService { internal static Vector3 Deploy(Player player, Inventory? source, ItemData item, bool validateOnly = false) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: 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_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_047c: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Unknown result type (might be due to invalid IL or missing references) //IL_0548: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0604: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); InputAndFeedback.Require((source == null || source == inventory) && inventory.ContainsItem(item), "Move the packed animal to your own hotbar first."); AnimalType animalType = AnimalIdentification.FromItem(item); PackedAnimalState packedAnimalState = PackedAnimalItem.Validate(item, animalType); GameObject val = AnimalIdentification.Resolve(animalType); AnimalSnapshot.Preflight(packedAnimalState, val); InputAndFeedback.Require(!ZNetView.m_useInitZDO && ZNetView.m_initZDO == null, "Another network spawn is initializing. Try again."); Camera mainCamera = Utils.GetMainCamera(); InputAndFeedback.Require(Object.op_Implicit((Object)(object)mainCamera), "Game camera unavailable."); Ray ray = mainCamera.ScreenPointToRay(new Vector3((float)Screen.width / 2f, (float)Screen.height / 2f)); int mask = LayerMask.GetMask(new string[11] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "vehicle", "character", "character_net", "character_noenv", "item" }); InputAndFeedback.Require(InputAndFeedback.AimHit(ray, 60f, mask, player, out var hit) && Vector3.Distance(((Component)player).transform.position, ((RaycastHit)(ref hit)).point) <= PortableShipsPlugin.Instance.DeployDistance.Value && ((RaycastHit)(ref hit)).normal.y >= 0.6f, "Aim at nearby, reasonably level ground or a clear ship deck."); InputAndFeedback.Require(!Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Character>()) && !Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<ItemDrop>()), "Aim at solid ground, not an animal or dropped item."); Rigidbody attachedRigidbody = ((RaycastHit)(ref hit)).collider.attachedRigidbody; InputAndFeedback.Require(!Object.op_Implicit((Object)(object)attachedRigidbody) || attachedRigidbody.isKinematic || Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Ship>()), "Aim at stable ground or a ship deck."); float liquidLevel = Floating.GetLiquidLevel(((RaycastHit)(ref hit)).point, 0f, (LiquidType)0); InputAndFeedback.Require(liquidLevel < -9999f || ((RaycastHit)(ref hit)).point.y >= liquidLevel + 0.05f, "Choose dry ground or a deck above the water."); Vector3 val2 = Vector3.ProjectOnPlane(((Character)player).GetLookDir(), Vector3.up); Quaternion val3 = ((((Vector3)(ref val2)).sqrMagnitude > 0.001f) ? Quaternion.LookRotation(((Vector3)(ref val2)).normalized, Vector3.up) : ((Component)player).transform.rotation); CapsuleCollider component = val.GetComponent<CapsuleCollider>(); Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(packedAnimalState.ScaleX, packedAnimalState.ScaleY, packedAnimalState.ScaleZ); InputAndFeedback.Require(AnimalCapsuleShape.TryCreate(component.radius, component.height, component.direction, component.center.y, val4.x, val4.y, val4.z, out var shape), "Animal collision shape is unsupported; packed item unchanged."); float radius = shape.Radius; float halfSegment = shape.HalfSegment; Vector3 val5 = val3 * ((component.direction == 0) ? Vector3.right : ((component.direction == 1) ? Vector3.up : Vector3.forward)); Vector3 val6 = Vector3.Scale(component.center, val4); Vector3 val7 = ((RaycastHit)(ref hit)).point + Vector3.up * shape.RootHeight; Vector3 val8 = val7 + val3 * val6; InputAndFeedback.Require(!Physics.CheckCapsule(val8 - val5 * halfSegment, val8 + val5 * halfSegment, radius * 0.98f, mask, (QueryTriggerInteraction)1), "Not enough space for this animal. Aim at a clear area."); InputAndFeedback.Require(PrivateArea.CheckAccess(val7, radius, true, false), "A ward prevents releasing the animal here."); if (validateOnly) { return val7; } string text = TransactionJournal.NewId(); TransactionJournal.Write(text, "animal-deploy-intent", string.Format("player={0} animal={1} identity={2} position={3} prior={4}", player.GetPlayerID(), animalType.Prefab, packedAnimalState.Identity, val7, item.m_customData["PortableShips.AnimalTransaction"])); item.m_customData["PortableShips.AnimalState"] = "deploying"; item.m_customData["PortableShips.AnimalTransaction"] = text; PortableShipsPlugin.Instance.Busy = true; try { inventory.m_onChanged?.Invoke(); ZDO val9 = ZDOMan.instance.CreateNewZDO(val7, StringExtensionMethods.GetStableHashCode(animalType.Prefab)); val9.SetPrefab(StringExtensionMethods.GetStableHashCode(animalType.Prefab)); val9.SetRotation(val3); ZNetView component2 = val.GetComponent<ZNetView>(); val9.Persistent = true; val9.SetType(component2.m_type); val9.SetDistant(component2.m_distant); val9.Set("PortableShips.Transaction", "animal-deploy:" + text); AnimalSnapshot.Restore(val9, packedAnimalState, val); ZNetView.m_useInitZDO = true; ZNetView.m_initZDO = val9; GameObject val10; try { val10 = Object.Instantiate<GameObject>(val, val7, val3); } finally { ZNetView.m_useInitZDO = false; ZNetView.m_initZDO = null; } ZNetView component3 = val10.GetComponent<ZNetView>(); Character component4 = val10.GetComponent<Character>(); InputAndFeedback.Require(Object.op_Implicit((Object)(object)component3) && component3.IsValid() && component3.IsOwner() && component3.GetZDO() == val9 && Object.op_Implicit((Object)(object)component4), "Animal spawn could not be verified. Keep the pending item and check the journal."); component4.SetLevel(packedAnimalState.Level); if (Object.op_Implicit((Object)(object)attachedRigidbody)) { val10.GetComponent<Rigidbody>().linearVelocity = attachedRigidbody.GetPointVelocity(val7); } val9.Set(ZDOVars.s_maxHealth, packedAnimalState.MaxHealth); val9.Set(ZDOVars.s_health, packedAnimalState.Health); MonsterAI component5 = ((Component)component4).GetComponent<MonsterAI>(); if (Object.op_Implicit((Object)(object)component5)) { component5.MakeTame(); component5.SetDespawnInDay(false); component5.SetEventCreature(false); } TransactionJournal.Write(text, "animal-created", ((object)Unsafe.As<ZDOID, ZDOID>(ref val9.m_uid)/*cast due to .constrained prefix*/).ToString()); ((MonoBehaviour)PortableShipsPlugin.Instance).StartCoroutine(Complete(player, inventory, item, component4, component3, packedAnimalState, text)); return val7; } catch { PortableShipsPlugin.Instance.Busy = false; PortableShipsPlugin.Instance.Error("Animal deployment " + text + " interrupted. Keep pending item/animal lock; inspect the journal before recovery."); throw; } } private static IEnumerator Complete(Player player, Inventory inventory, ItemData item, Character actor, ZNetView view, PackedAnimalState state, string tx) { yield return null; yield return (object)new WaitForFixedUpdate(); yield return null; try { InputAndFeedback.Require(Object.op_Implicit((Object)(object)player) && (Object)(object)Player.m_localPlayer == (Object)(object)player && Object.op_Implicit((Object)(object)ZNetScene.instance) && Object.op_Implicit((Object)(object)actor) && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.IsOwner(), "Animal release interrupted. Keep the pending item and check the journal."); AnimalSnapshot.Verify(actor, state); InputAndFeedback.Require(inventory.ContainsItem(item) && inventory.RemoveItem(item), "Could not consume the reserved animal item. Consult the journal before recovery."); view.GetZDO().Set("PortableShips.Transaction", ""); TransactionJournal.Write(tx, "animal-deploy-commit", ((object)Unsafe.As<ZDOID, ZDOID>(ref view.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString()); InputAndFeedback.Say("Animal released, still tamed."); } catch (Exception ex) { PortableShipsPlugin.Instance.Error("Animal release " + tx + ": " + ex); InputAndFeedback.Say(ex.Message); } finally { PortableShipsPlugin.Instance.Busy = false; } } } internal static class AnimalIcons { private static readonly Dictionary<string, Sprite> Icons = new Dictionary<string, Sprite>(); internal static Sprite For(AnimalType type) { string key = ((type.Prefab == "Hen" || type.Prefab == "Chicken") ? type.Prefab : type.Trophy); if (Icons.TryGetValue(key, out Sprite value) && Object.op_Implicit((Object)(object)value)) { return value; } GameObject prefab = PrefabManager.Instance.GetPrefab(type.Trophy); if (!Object.op_Implicit((Object)(object)prefab)) { prefab = PrefabManager.Instance.GetPrefab("TrophyBoar"); } if (!Object.op_Implicit((Object)(object)prefab)) { return null; } return prefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_icons[0]; } internal static void Refresh() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4 || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return; } StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Cold")); if (!Object.op_Implicit((Object)(object)statusEffect) || !Object.op_Implicit((Object)(object)statusEffect.m_icon)) { PortableShipsPlugin.Instance.LogInfo("Cold status icon is not ready for packed animals."); return; } AnimalType[] types = AnimalIdentification.Types; foreach (AnimalType animalType in types) { string key = ((animalType.Prefab == "Hen" || animalType.Prefab == "Chicken") ? animalType.Prefab : animalType.Trophy); if (!Icons.ContainsKey(key)) { try { Sprite foreground = For(animalType); if (animalType.Prefab == "Hen" || animalType.Prefab == "Chicken") { GameObject prefab = PrefabManager.Instance.GetPrefab(animalType.Prefab); Sprite val = RenderManager.Instance.Render(new RenderRequest(prefab) { Width = 128, Height = 128, Rotation = Quaternion.Euler(10f, 135f, 0f), UseCache = true, TargetPlugin = ((BaseUnityPlugin)PortableShipsPlugin.Instance).Info.Metadata, ParticleSimulationTime = -1f }); if (Object.op_Implicit((Object)(object)val)) { Rect rect = val.rect; if (((Rect)(ref rect)).width > 0f) { foreground = val; } } } Icons[key] = Compose(statusEffect.m_icon, foreground); } catch (Exception ex) { PortableShipsPlugin.Instance.Error("Packed animal icon " + animalType.Prefab + ": " + ex.Message); } } GameObject prefab2 = PrefabManager.Instance.GetPrefab(animalType.Item); if (Object.op_Implicit((Object)(object)prefab2)) { prefab2.GetComponent<ItemDrop>().m_itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { For(animalType) }; } } } private static Color[] Pixels(Sprite sprite, int size) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) RenderTexture active = RenderTexture.active; RenderTexture temporary = RenderTexture.GetTemporary(size, size, 0, (RenderTextureFormat)0); Texture2D val = null; try { Rect textureRect = sprite.textureRect; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(((Rect)(ref textureRect)).width / (float)((Texture)sprite.texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)sprite.texture).height); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)sprite.texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)sprite.texture).height); Graphics.Blit((Texture)(object)sprite.texture, temporary, val2, val3); RenderTexture.active = temporary; val = new Texture2D(size, size, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)size, (float)size), 0, 0); val.Apply(); return val.GetPixels(); } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)val); } } } private static Sprite Compose(Sprite background, Sprite foreground) { //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) Color[] array = Pixels(background, 128); Color[] array2 = Pixels(foreground, 96); for (int i = 0; i < array.Length; i++) { array[i].a *= 0.9f; } for (int j = 0; j < 96; j++) { for (int k = 0; k < 96; k++) { int num = (j + 16) * 128 + k + 16; Color val = array2[j * 96 + k]; Color val2 = array[num]; float num2 = val.a + val2.a * (1f - val.a); array[num] = (Color)((num2 <= 0f) ? Color.clear : new Color((val.r * val.a + val2.r * val2.a * (1f - val.a)) / num2, (val.g * val.a + val2.g * val2.a * (1f - val.a)) / num2, (val.b * val.a + val2.b * val2.a * (1f - val.a)) / num2, num2)); } } Texture2D val3 = new Texture2D(128, 128, (TextureFormat)4, false) { name = "PortableShips.FrozenAnimalIcon", filterMode = (FilterMode)1 }; val3.SetPixels(array); val3.Apply(false, true); return Sprite.Create(val3, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f)); } } internal sealed class AnimalType { internal readonly string Prefab; internal readonly string Label; internal readonly string Trophy; internal readonly string Item; internal AnimalType(string prefab, string label, string trophy) { Prefab = prefab; Label = label; Trophy = trophy; Item = "PortableShips_PackedAnimal_" + prefab; } } internal static class AnimalIdentification { internal static readonly AnimalType[] Types = new AnimalType[27] { new AnimalType("Boar", "Boar", "TrophyBoar"), new AnimalType("Boar_piggy", "Piglet", "TrophyBoar"), new AnimalType("Wolf", "Wolf", "TrophyWolf"), new AnimalType("Wolf_cub", "Wolf cub", "TrophyWolf"), new AnimalType("Lox", "Lox", "TrophyLox"), new AnimalType("Lox_Calf", "Lox calf", "TrophyLox"), new AnimalType("Hen", "Hen", "ChickenEgg"), new AnimalType("Chicken", "Chick", "ChickenEgg"), new AnimalType("Asksvin", "Asksvin", "TrophyAsksvin"), new AnimalType("Asksvin_hatchling", "Asksvin hatchling", "TrophyAsksvin"), new AnimalType("Moose", "Moose", "TrophyMoose"), new AnimalType("Moose_calf", "Moose calf", "TrophyMoose"), new AnimalType("rae_OdinHorse", "Horse", "rae_OdinHorse_Trophy"), new AnimalType("rae_Offspring_Normal", "Foal", "rae_OdinHorse_Trophy"), new AnimalType("Fox_TW", "Fox", "TrophyFox_TW"), new AnimalType("FoxCub_TW", "Fox cub", "TrophyFox_TW"), new AnimalType("Razorback_TW", "Razorback", "TrophyRazorback_TW"), new AnimalType("RazorbackPiggy_TW", "Razorback piglet", "TrophyRazorback_TW"), new AnimalType("BlackBear_TW", "Black bear", "TrophyBlackBear_TW"), new AnimalType("BlackBearCub_TW", "Black bear cub", "TrophyBlackBear_TW"), new AnimalType("GrizzlyBear_TW", "Grizzly bear", "TrophyGrizzlyBear_TW"), new AnimalType("GrizzlyBearCub_TW", "Grizzly cub", "TrophyGrizzlyBear_TW"), new AnimalType("Prowler_TW", "Prowler", "TrophyProwler_TW"), new AnimalType("ProwlerCub_TW", "Prowler cub", "TrophyProwler_TW"), new AnimalType("ArcticBear_TW", "Arctic bear", "TrophyArcticBear_TW"), new AnimalType("ArcticBearCub_TW", "Arctic bear cub", "TrophyArcticBear_TW"), new AnimalType("BearCubBase_TW", "Bear cub", "TrophyArcticBear_TW") }; internal static AnimalType? FromCharacter(Character actor) { ZNetView view = (Object.op_Implicit((Object)(object)actor) ? ((Component)actor).GetComponent<ZNetView>() : null); if (!Object.op_Implicit((Object)(object)view) || !view.IsValid()) { return null; } return Types.FirstOrDefault((AnimalType t) => StringExtensionMethods.GetStableHashCode(t.Prefab) == view.GetZDO().GetPrefab()); } internal static AnimalType? FromItem(ItemData item) { if (item == null || !Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return null; } return Types.FirstOrDefault((AnimalType t) => t.Item == ((Object)item.m_dropPrefab).name); } internal static GameObject Resolve(AnimalType type) { GameObject prefab = ZNetScene.instance.GetPrefab(type.Prefab); InputAndFeedback.Require(Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<Character>()) && Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>()) && Object.op_Implicit((Object)(object)prefab.GetComponent<CapsuleCollider>()), "Animal prefab unavailable: " + type.Prefab); return prefab; } } internal static class AnimalModCompatibility { [HarmonyPatch] private static class HorseMutations { private static bool Prepare() { if (Chainloader.PluginInfos.TryGetValue("Raelaziel.OdinHorse", out var value) && value.Metadata.Version.ToString() == "1.9.1") { return AccessTools.TypeByName("OdinHorse.TameableExtensions") != null; } return false; } private static IEnumerable<MethodBase> TargetMethods() { Type type = AccessTools.TypeByName("OdinHorse.TameableExtensions"); string[] array = new string[3] { "RPC_AddArmor", "RPC_RemoveArmor", "RPC_ToggleWait" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } } MethodInfo methodInfo2 = AccessTools.Method(AccessTools.TypeByName("OdinHorse.HorseSaddlebags"), "Open", (Type[])null, (Type[])null); if (methodInfo2 != null) { yield return methodInfo2; } } [HarmonyPriority(800)] private static bool Prefix(Tameable __0) { return !AnimalPackingAccess.Reserved(((Component)__0).GetComponent<ZNetView>()); } } [HarmonyPatch] private static class HorseCart { private static bool Prepare() { if (!Chainloader.PluginInfos.TryGetValue("Raelaziel.OdinHorse", out var value) || value.Metadata.Version.ToString() != "1.9.1") { return false; } Type type = AccessTools.TypeByName("OdinHorse.OdinHorse"); if (type != null) { return AccessTools.Method(type, "AttachCartTo", (Type[])null, (Type[])null) != null; } return false; } private static MethodBase TargetMethod() { return AccessTools.Method(AccessTools.TypeByName("OdinHorse.OdinHorse"), "AttachCartTo", (Type[])null, (Type[])null); } [HarmonyPriority(800)] private static bool Prefix(Character __0) { return !AnimalPackingAccess.Reserved(((Component)__0).GetComponent<ZNetView>()); } } internal static void RequireVersion(string prefab) { string text = (AnimalRules.Horse(prefab) ? "Raelaziel.OdinHorse" : (prefab.EndsWith("_TW", StringComparison.Ordinal) ? "Therzie.Monstrum" : "")); if (text.Length != 0) { string text2 = ((text == "Raelaziel.OdinHorse") ? "1.9.1" : "1.6.1"); InputAndFeedback.Require(Chainloader.PluginInfos.TryGetValue(text, out var value) && value.Metadata.Version.ToString() == text2, "This animal requires the inspected " + text + " " + text2 + " build to preserve its data."); } } internal static void RequireHorseCoat(int coat) { Type type = AccessTools.TypeByName("OdinHorse.OdinHorse"); IList list = ((type == null) ? null : (AccessTools.Field(type, "horseMaterials")?.GetValue(null) as IList)); InputAndFeedback.Require(list != null && coat >= 0 && coat < list.Count, "Horse coat is still initializing or the OdinHorse palette changed. Animal/item unchanged."); } internal static void ValidateHorseStorage(Character actor) { InputAndFeedback.Require(AnimalBagPolicy.Empty(((Component)actor).GetComponent<ZNetView>().GetZDO().GetString("rae_saddlebags", "")), "Empty and close the horse's saddlebags before shrinking it."); } } internal sealed class AnimalPackingAccess : MonoBehaviour { [HarmonyPatch(typeof(Character), "Awake")] private static class Register { private static void Postfix(Character __instance) { if (AnimalIdentification.FromCharacter(__instance) != null && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<AnimalPackingAccess>())) { ((Component)__instance).gameObject.AddComponent<AnimalPackingAccess>().Initialize(__instance); } } } [HarmonyPatch(typeof(Character), "GetHoverText")] private static class Hover { private static void Postfix(Character __instance, ref string __result) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer) && Beastbinding.Learned(localPlayer) && __instance.IsTamed() && AnimalIdentification.FromCharacter(__instance) != null && InputAndFeedback.CanAct(localPlayer) && (Object)(object)AnimalPackingService.AimedAnimal(localPlayer) == (Object)(object)__instance) { __result = __result + "\n[<color=yellow><b>" + ((object)PortableShipsPlugin.Instance.PackKey.Value/*cast due to .constrained prefix*/).ToString() + "</b></color>] Shrink"; } } } [HarmonyPatch(typeof(BaseAI), "UpdateAI")] private static class PauseAI { private static bool Prefix(BaseAI __instance, ref bool __result) { if (!Reserved(((Component)__instance).GetComponent<ZNetView>())) { return true; } __instance.StopMoving(); __result = false; return false; } } [HarmonyPatch] private static class PauseLifeCycle { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Growup), "GrowUpdate", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Procreation), "Procreate", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Tameable), "RPC_AddSaddle", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Tameable), "RPC_SetName", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Sadle), "RPC_RemoveSaddle", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Sadle), "UpdateStamina", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Sadle), "UpdateDrown", (Type[])null, (Type[])null); } private static bool Prefix(Component __instance) { return !Reserved(__instance.GetComponentInParent<ZNetView>()); } } [HarmonyPatch(typeof(Sadle), "RPC_RequestControl")] private static class RiderGuard { private static bool Prefix(Sadle __instance, long sender) { ZNetView componentInParent = ((Component)__instance).GetComponentInParent<ZNetView>(); if (!Reserved(componentInParent)) { return true; } if (componentInParent.IsOwner()) { componentInParent.InvokeRPC(sender, "RequestRespons", new object[1] { false }); } return false; } } [HarmonyPatch] private static class InteractionGuard { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Tameable), "Interact", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Tameable), "UseItem", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Tameable), "DropSaddle", (Type[])null, (Type[])null); } [HarmonyPriority(800)] private static bool Prefix(Tameable __instance, ref bool __result) { if (!Reserved(((Component)__instance).GetComponent<ZNetView>())) { return true; } __result = false; return false; } } [HarmonyPatch] private static class CommitDamageGuard { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Character), "RPC_Heal", (Type[])null, (Type[])null); } private static bool Prefix(Character __instance) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { return !component.GetZDO().GetString("PortableShips.Transaction", "").StartsWith("animal-", StringComparison.Ordinal); } return true; } } private const string RequestRpc = "PortableShips.RequestAnimal"; private const string ReplyRpc = "PortableShips.AnimalReply"; private Character actor; private ZNetView view; internal string Rejection = ""; private string requested = ""; private PackedAnimalState? appearance; private float appearanceUntil; private float nextAppearance; internal void Initialize(Character target) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) actor = target; view = ((Component)target).GetComponent<ZNetView>(); view.Register<string>("PortableShips.RequestAnimal", (Action<long, string>)Request); view.Register<string, string>("PortableShips.AnimalReply", (Action<long, string, string>)Reply); ZDO zDO = view.GetZDO(); Vector3 vec = zDO.GetVec3("PortableShips.AnimalScale", Vector3.zero); if (AnimalRules.Number(vec.x, 0.05f, 20f) && AnimalRules.Number(vec.y, 0.05f, 20f) && AnimalRules.Number(vec.z, 0.05f, 20f)) { ((Component)actor).transform.localScale = vec; } if (PackedAnimalState.TryDecode(zDO.GetString("PortableShips.AnimalAppearance", ""), AnimalIdentification.FromCharacter(target).Prefab, out PackedAnimalState state)) { appearance = state; appearanceUntil = Time.unscaledTime + 2f; } } private void LateUpdate() { if (appearance == null || Time.unscaledTime > appearanceUntil || Time.unscaledTime < nextAppearance) { return; } nextAppearance = Time.unscaledTime + 0.15f; try { AnimalAppearance.Apply(actor, appearance); } catch (Exception ex) { appearance = null; PortableShipsPlugin.Instance.Error("Animal appearance: " + ex.Message); } } internal static bool Reserved(ZNetView view) { if (Object.op_Implicit((Object)(object)view) && view.IsValid()) { if (!view.GetZDO().GetString("PortableShips.Transaction", "").StartsWith("animal-", StringComparison.Ordinal)) { if (!string.IsNullOrEmpty(view.GetZDO().GetString("PortableShips.AnimalLease", "")) && Object.op_Implicit((Object)(object)ZNet.instance)) { return view.GetZDO().GetLong("PortableShips.AnimalLeaseExpires", 0L) > ZNet.instance.GetTime().Ticks; } return false; } return true; } return false; } internal static bool Owns(ZNetView view, string token) { if (Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.IsOwner() && view.GetZDO().GetString("PortableShips.AnimalLease", "") == token) { return view.GetZDO().GetLong("PortableShips.AnimalLeaseExpires", 0L) > ZNet.instance.GetTime().Ticks; } return false; } internal void BeginRequest(string token) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) requested = token; Rejection = ""; if (!view.HasOwner()) { view.ClaimOwnership(); } Beastbinding.Publish(Player.m_localPlayer); ZNetView component = ((Component)Player.m_localPlayer).GetComponent<ZNetView>(); if (view.GetZDO().GetOwner() != ZNet.GetUID()) { ZDOMan.instance.ForceSendZDO(view.GetZDO().GetOwner(), component.GetZDO().m_uid); } view.InvokeRPC("PortableShips.RequestAnimal", new object[1] { token }); } private void Request(long sender, string token) { //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) if (!view.IsValid() || !view.IsOwner()) { return; } try { InputAndFeedback.Require(Guid.TryParseExact(token, "N", out var _), "Invalid animal request."); if (view.GetZDO().GetString("PortableShips.AnimalLease", "") == token && Owns(view, token)) { view.InvokeRPC(sender, "PortableShips.AnimalReply", new object[2] { token, "" }); return; } InputAndFeedback.Require(!Reserved(view) && string.IsNullOrEmpty(view.GetZDO().GetString("PortableShips.Transaction", "")), "This animal is busy or has an unfinished transaction."); Player val = ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)((Component)p).GetComponent<ZNetView>()) && ((Component)p).GetComponent<ZNetView>().IsValid() && ((Component)p).GetComponent<ZNetView>().GetZDO().GetOwner() == sender)); InputAndFeedback.Require(Object.op_Implicit((Object)(object)val) && !((Character)val).IsDead() && Vector3.Distance(((Component)val).transform.position, ((Component)actor).transform.position) <= 10f, "Move closer to the animal."); InputAndFeedback.Require(((Component)val).GetComponent<ZNetView>().GetZDO().GetBool("PortableShips.BeastbindingLearned", false), "retry"); AnimalPackingService.ValidateActor(actor); view.GetZDO().Set("PortableShips.AnimalLease", token); view.GetZDO().Set("PortableShips.AnimalLeaseExpires", ZNet.instance.GetTime().AddSeconds(10.0).Ticks); ZDOMan.instance.ForceSendZDO(sender, view.GetZDO().m_uid); view.GetZDO().SetOwner(sender); view.InvokeRPC(sender, "PortableShips.AnimalReply", new object[2] { token, "" }); } catch (Exception ex) { string text = ((ex is InvalidOperationException) ? ex.Message : "Animal reservation failed. Check the PortableShips log."); if (!(ex is InvalidOperationException)) { PortableShipsPlugin.Instance.Error(ex.ToString()); } view.InvokeRPC(sender, "PortableShips.AnimalReply", new object[2] { token, text }); } } private void Reply(long sender, string token, string error) { if (token == requested) { Rejection = error; } } internal void Release(string token) { if (Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)ZNetScene.instance) && ZDOMan.instance != null && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.IsOwner() && view.GetZDO().GetString("PortableShips.AnimalLease", "") == token && string.IsNullOrEmpty(view.GetZDO().GetString("PortableShips.Transaction", ""))) { view.GetZDO().Set("PortableShips.AnimalLease", ""); view.GetZDO().Set("PortableShips.AnimalLeaseExpires", 0L); } requested = ""; } } internal static class AnimalPackingService { internal static Character? AimedAnimal(Player player) { //IL_0029: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) Camera mainCamera = Utils.GetMainCamera(); if (!Object.op_Implicit((Object)(object)mainCamera)) { return null; } Ray ray = mainCamera.ScreenPointToRay(new Vector3((float)Screen.width / 2f, (float)Screen.height / 2f)); int mask = LayerMask.GetMask(new string[11] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "vehicle", "character", "character_net", "character_noenv", "character_ghost" }); if (!InputAndFeedback.AimHit(ray, 50f, mask, player, out var hit) || Vector3.Distance(((Character)player).GetEyePoint(), ((RaycastHit)(ref hit)).point) > PortableShipsPlugin.Instance.PackDistance.Value) { return null; } Character componentInParent = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Character>(); if (!Object.op_Implicit((Object)(object)componentInParent) || componentInParent.IsPlayer()) { return null; } return componentInParent; } internal static void ValidateActor(Character actor) { InputAndFeedback.Require(Object.op_Implicit((Object)(object)actor) && AnimalIdentification.FromCharacter(actor) != null, "This creature is not a supported permanent tameable animal."); AnimalModCompatibility.RequireVersion(AnimalIdentification.FromCharacter(actor).Prefab); InputAndFeedback.Require(actor.IsTamed(), "Only fully tamed animals can be shrunk."); InputAndFeedback.Require(!actor.IsDead() && actor.GetHealth() > 0f, "This animal is no longer alive."); ZNetView view = ((Component)actor).GetComponent<ZNetView>(); InputAndFeedback.Require(Object.op_Implicit((Object)(object)view) && view.IsValid(), "Animal is no longer available."); Tameable component = ((Component)actor).GetComponent<Tameable>(); InputAndFeedback.Require(!Object.op_Implicit((Object)(object)component) || (component.m_unsummonDistance <= 0f && component.m_unsummonOnOwnerLogoutSeconds <= 0f), "Temporary summons cannot be packed."); InputAndFeedback.Require(!Object.op_Implicit((Object)(object)component) || !component.HaveRider(), "Dismount before shrinking the animal."); foreach (Player allPlayer in Player.GetAllPlayers()) { Transform attachPoint = allPlayer.GetAttachPoint(); InputAndFeedback.Require(!Object.op_Implicit((Object)(object)attachPoint) || (Object)(object)((Component)attachPoint).GetComponentInParent<Character>() != (Object)(object)actor, "A player is still mounted on this animal."); } InputAndFeedback.Require(((Component)actor).GetComponentsInChildren<ZNetView>(true).All((ZNetView v) => (Object)(object)v == (Object)(object)view), "Animal has independently networked attachments. Packing blocked to preserve them."); Container[] componentsInChildren = ((Component)actor).GetComponentsInChildren<Container>(true); foreach (Container val in componentsInChildren) { InputAndFeedback.Require(AnimalRules.Horse(AnimalIdentification.FromCharacter(actor).Prefab) && ((Object)val).name == "SaddlebagContainer" && !val.IsInUse() && val.GetInventory().GetAllItems().Count == 0, "Empty and close the animal's storage before shrinking; unsupported storage attachments must be removed."); } if (AnimalRules.Horse(AnimalIdentification.FromCharacter(actor).Prefab)) { AnimalModCompatibility.ValidateHorseStorage(actor); } Rigidbody body = ((Component)actor).GetComponent<Rigidbody>(); InputAndFeedback.Require(!Object.FindObjectsByType<Joint>((FindObjectsSortMode)0).Any((Joint j) => (Object)(object)j.connectedBody == (Object)(object)body), "Detach the cart or physical tether before shrinking this animal."); if (view.GetZDO().GetBool(ZDOVars.s_haveSaddleHash, false)) { InputAndFeedback.Require(Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_saddle) && Object.op_Implicit((Object)(object)component.m_saddleItem) && AnimalRules.CanSaddle(AnimalIdentification.FromCharacter(actor).Prefab), "Animal's saddle cannot be preserved safely."); } } internal static void Validate(Player player, Character actor) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) InputAndFeedback.Require(Beastbinding.Learned(player), "Buy Beastbinding from the Bog Witch for 1,000 coins to shrink tamed animals."); InputAndFeedback.Require(Object.op_Implicit((Object)(object)actor) && (Object)(object)AimedAnimal(player) == (Object)(object)actor, "Keep aiming at the same nearby animal."); ValidateActor(actor); InputAndFeedback.Require(string.IsNullOrEmpty(((Component)actor).GetComponent<ZNetView>().GetZDO().GetString("PortableShips.Transaction", "")), "Animal has an unfinished transaction. Check the journal."); InputAndFeedback.Require(PrivateArea.CheckAccess(((Component)actor).transform.position, 0f, true, false), "A ward prevents shrinking this animal."); InputAndFeedback.Require(((Humanoid)player).GetInventory().HaveEmptySlot(), "Free an inventory slot before shrinking the animal."); } internal unsafe static void Pack(Player player, Character actor, string token) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_013c: 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_0174: Unknown result type (might be due to invalid IL or missing references) Validate(player, actor); AnimalType animalType = AnimalIdentification.FromCharacter(actor); ZNetView component = ((Component)actor).GetComponent<ZNetView>(); InputAndFeedback.Require(AnimalPackingAccess.Owns(component, token), "Animal reservation changed. Try again."); PackedAnimalState packedAnimalState = AnimalSnapshot.Capture(actor, animalType); string text = TransactionJournal.NewId(); ItemData val = PackedAnimalItem.Create(animalType, packedAnimalState, text); Inventory inventory = ((Humanoid)player).GetInventory(); InputAndFeedback.Require(inventory.CanAddItem(val, -1), "No room for the packed animal."); TransactionJournal.Write(text, "animal-pack-intent", $"player={player.GetPlayerID()} animal={animalType.Prefab} identity={packedAnimalState.Identity} zdo={component.GetZDO().m_uid} hp={packedAnimalState.Health}/{packedAnimalState.MaxHealth} saddle={packedAnimalState.Saddle}"); bool flag = false; PortableShipsPlugin.Instance.Busy = true; try { InputAndFeedback.Require(inventory.AddItem(val) && inventory.ContainsItem(val), "Unable to reserve an inventory slot."); InputAndFeedback.Require(AnimalPackingAccess.Owns(component, token), "Animal authority changed before packing."); ValidateActor(actor); component.GetZDO().Set("PortableShips.Transaction", "animal-pack:" + text); flag = true; ZDOID uid = component.GetZDO().m_uid; ZDOMan instance = ZDOMan.instance; TransactionJournal.Write(text, "animal-delete-intent", ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString()); component.Destroy(); ((MonoBehaviour)PortableShipsPlugin.Instance).StartCoroutine(Complete(player, inventory, val, instance, uid, text)); } catch { PortableShipsPlugin.Instance.Busy = false; if (!flag) { inventory.RemoveItem(val); } else { PortableShipsPlugin.Instance.Error("Animal packing " + text + " interrupted; pending item/lock retained. Check the journal before recovery."); } throw; } } private unsafe static IEnumerator Complete(Player player, Inventory inventory, ItemData item, ZDOMan manager, ZDOID id, string tx) { //IL_0023: 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) float until = Time.realtimeSinceStartup + 15f; while (ZDOMan.instance == manager && Object.op_Implicit((Object)(object)ZNetScene.instance) && Object.op_Implicit((Object)(object)player) && (Object)(object)Player.m_localPlayer == (Object)(object)player && manager.GetZDO(id) != null && Time.realtimeSinceStartup < until) { yield return null; } try { if (ZDOMan.instance != manager || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)player) || (Object)(object)Player.m_localPlayer != (Object)(object)player || manager.GetZDO(id) != null || !inventory.ContainsItem(item)) { PortableShipsPlugin.Instance.Error("Animal pack " + tx + " acknowledgement uncertain. Keep the pending item and inspect the journal."); InputAndFeedback.Say("Animal pack confirmation interrupted. Keep the pending item and check the journal."); yield break; } TransactionJournal.Write(tx, "animal-pack-commit", ((object)(*(ZDOID*)(&id))/*cast due to .constrained prefix*/).ToString()); item.m_customData["PortableShips.AnimalState"] = "ready"; inventory.m_onChanged?.Invoke(); InputAndFeedback.Say("Animal safely packed."); } finally { PortableShipsPlugin.Instance.Busy = false; } } } internal static class AnimalAppearance { private static readonly int[] Floats = new int[3] { Shader.PropertyToID("_Hue"), Shader.PropertyToID("_Saturation"), Shader.PropertyToID("_Value") }; private static readonly int ColorId = Shader.PropertyToID("_Color"); private static readonly int SkinId = Shader.PropertyToID("_SkinColor"); internal static AnimalTint[] Capture(Character actor) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown List<AnimalTint> list = new List<AnimalTint>(); SkinnedMeshRenderer[] componentsInChildren = ((Component)actor).GetComponentsInChildren<SkinnedMeshRenderer>(true); InputAndFeedback.Require(componentsInChildren.Length <= 128, "Animal has an unsupported appearance layout."); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] sharedMaterials = ((Renderer)componentsInChildren[i]).sharedMaterials; InputAndFeedback.Require(sharedMaterials.Length <= 16, "Animal has too many appearance materials."); for (int j = 0; j < sharedMaterials.Length; j++) { Material material = sharedMaterials[j]; if (!Object.op_Implicit((Object)(object)material)) { continue; } MaterialPropertyBlock block = new MaterialPropertyBlock(); ((Renderer)componentsInChildren[i]).GetPropertyBlock(block, j); AnimalTint tint = new AnimalTint { Renderer = i, Material = j }; for (int k = 0; k < Floats.Length; k++) { if (material.HasProperty(Floats[k])) { tint.Flags |= 1 << k; tint.Values[k] = (block.HasFloat(Floats[k]) ? block.GetFloat(Floats[k]) : material.GetFloat(Floats[k])); } } Color(ColorId, 8, 3); Color(SkinId, 16, 7); if (tint.Flags != 0) { list.Add(tint); } void Color(int id, int bit, int offset) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_006c: 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_0096: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(id)) { tint.Flags |= bit; Color val = (block.HasColor(id) ? block.GetColor(id) : material.GetColor(id)); tint.Values[offset] = val.r; tint.Values[offset + 1] = val.g; tint.Values[offset + 2] = val.b; tint.Values[offset + 3] = val.a; } } } } InputAndFeedback.Require(list.Count <= 64, "Animal appearance is too complex to preserve safely."); return list.ToArray(); } internal static void Apply(Character actor, PackedAnimalState state) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown if (actor.GetLevel() != state.Level) { return; } SkinnedMeshRenderer[] componentsInChildren = ((Component)actor).GetComponentsInChildren<SkinnedMeshRenderer>(true); AnimalTint[] tints = state.Tints; foreach (AnimalTint tint in tints) { InputAndFeedback.Require(tint.Renderer < componentsInChildren.Length && tint.Material < ((Renderer)componentsInChildren[tint.Renderer]).sharedMaterials.Length, "Animal appearance layout changed. Check the journal."); SkinnedMeshRenderer val = componentsInChildren[tint.Renderer]; MaterialPropertyBlock block = new MaterialPropertyBlock(); ((Renderer)val).GetPropertyBlock(block, tint.Material); for (int j = 0; j < Floats.Length; j++) { if ((tint.Flags & (1 << j)) != 0) { block.SetFloat(Floats[j], tint.Values[j]); } } Color(ColorId, 8, 3); Color(SkinId, 16, 7); ((Renderer)val).SetPropertyBlock(block, tint.Material); void Color(int id, int flag, int offset) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) if ((tint.Flags & flag) != 0) { block.SetColor(id, new Color(tint.Values[offset], tint.Values[offset + 1], tint.Values[offset + 2], tint.Values[offset + 3])); } } } } } internal static class AnimalSnapshot { private static double Seconds(long now, long before) { if (before <= 0) { return 0.0; } return Math.Max(0.0, (double)(now - before) / 10000000.0); } internal static PackedAnimalState Capture(Character actor, AnimalType type) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0162: 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) ZDO zDO = ((Component)actor).GetComponent<ZNetView>().GetZDO(); long ticks = ZNet.instance.GetTime().Ticks; Tameable component = ((Component)actor).GetComponent<Tameable>(); Procreation component2 = ((Component)actor).GetComponent<Procreation>(); long num = zDO.GetLong(ZDOVars.s_pregnant, 0L); Vector3 localScale = ((Component)actor).transform.localScale; string text = zDO.GetString("PortableShips.AnimalIdentity", ""); Guid result; PackedAnimalState packedAnimalState = new PackedAnimalState { Prefab = type.Prefab, Identity = (Guid.TryParseExact(text, "N", out result) ? text : TransactionJournal.NewId()), Name = zDO.GetString(ZDOVars.s_tamedName, ""), Author = zDO.GetString(ZDOVars.s_tamedNameAuthor, ""), Health = actor.GetHealth(), MaxHealth = actor.GetMaxHealth(), Level = actor.GetLevel(), Saddle = zDO.GetBool(ZDOVars.s_haveSaddleHash, false), HorseMaterial = (AnimalRules.Horse(type.Prefab) ? zDO.GetInt("HorseMaterial", -1) : (-1)), HorseArmor = (AnimalRules.Horse(type.Prefab) && zDO.GetBool("HaveArmor", false)), HorseWaiting = (AnimalRules.Horse(type.Prefab) && zDO.GetBool("IsWaiting", false)), ScaleX = localScale.x, ScaleY = localScale.y, ScaleZ = localScale.z, RandomSeed = zDO.GetInt("RandMatSeed", -1), AgeSeconds = Seconds(ticks, zDO.GetLong(ZDOVars.s_spawnTime, 0L)), FedSeconds = ((Object.op_Implicit((Object)(object)component) && zDO.GetLong(ZDOVars.s_tameLastFeeding, 0L) > 0) ? Mathf.Clamp(component.m_fedDuration - (float)Seconds(ticks, zDO.GetLong(ZDOVars.s_tameLastFeeding, 0L)), 0f, component.m_fedDuration) : 0f), PregnancySeconds = ((Object.op_Implicit((Object)(object)component2) && num > 0) ? Mathf.Clamp(component2.m_pregnancyDuration - (float)Seconds(ticks, num), 0f, component2.m_pregnancyDuration) : (-1f)), LovePoints = zDO.GetInt(ZDOVars.s_lovePoints, 0), Stamina = ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_saddle)) ? zDO.GetFloat(ZDOVars.s_stamina, component.m_saddle.GetMaxStamina()) : 0f), Tints = AnimalAppearance.Capture(actor) }; if (AnimalRules.Horse(type.Prefab)) { AnimalModCompatibility.RequireHorseCoat(packedAnimalState.HorseMaterial); } InputAndFeedback.Require(packedAnimalState.RandomSeed >= 0 || ((Component)actor).GetComponentsInChildren<RandomMaterialValues>(true).Length == 0, "Animal appearance is still initializing. Wait a moment and try again."); InputAndFeedback.Require(packedAnimalState.Valid, "Animal has unsupported health, name or appearance data. Animal unchanged."); return packedAnimalState; } private static long Before(long now, double seconds) { return Math.Max(1L, now - (long)(seconds * 10000000.0)); } internal static void Preflight(PackedAnimalState state, GameObject prefab) { AnimalModCompatibility.RequireVersion(state.Prefab); if (AnimalRules.Horse(state.Prefab)) { AnimalModCompatibility.RequireHorseCoat(state.HorseMaterial); } Tameable component = prefab.GetComponent<Tameable>(); InputAndFeedback.Require(!state.Saddle || (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_saddle) && Object.op_Implicit((Object)(object)component.m_saddleItem)), "Saved saddle is unavailable on this animal prefab."); InputAndFeedback.Require(state.PregnancySeconds < 0f || Object.op_Implicit((Object)(object)prefab.GetComponent<Procreation>()), "Saved breeding state is unavailable on this animal prefab."); SkinnedMeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true); AnimalTint[] tints = state.Tints; foreach (AnimalTint animalTint in tints) { InputAndFeedback.Require(animalTint.Renderer < componentsInChildren.Length && animalTint.Material < ((Renderer)componentsInChildren[animalTint.Renderer]).sharedMaterials.Length, "Animal appearance layout changed. Keep the packed item and restore compatible animal assets."); } } internal static void Restore(ZDO zdo, PackedAnimalState state, GameObject prefab) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) long ticks = ZNet.instance.GetTime().Ticks; zdo.Set(ZDOVars.s_tamed, true); zdo.Set(ZDOVars.s_level, state.Level, false); zdo.Set(ZDOVars.s_health, state.Health); zdo.Set(ZDOVars.s_maxHealth, state.MaxHealth); zdo.Set(ZDOVars.s_tamedName, state.Name); zdo.Set(ZDOVars.s_tamedNameAuthor, state.Author); if (AnimalRules.Horse(state.Prefab)) { zdo.Set("HorseMaterial", state.HorseMaterial); zdo.Set("HaveArmor", state.HorseArmor); zdo.Set("IsWaiting", state.HorseWaiting); } zdo.Set(ZDOVars.s_haveSaddleHash, state.Saddle); zdo.Set(ZDOVars.s_stamina, state.Stamina); zdo.Set("PortableShips.AnimalIdentity", state.Identity); zdo.Set("PortableShips.AnimalScale", new Vector3(state.ScaleX, state.ScaleY, state.ScaleZ)); zdo.Set("PortableShips.AnimalAppearance", state.Encode()); if (state.RandomSeed >= 0) { zdo.Set("RandMatSeed", state.RandomSeed); } zdo.Set(ZDOVars.s_spawnTime, Before(ticks, state.AgeSeconds)); zdo.Set(ZDOVars.s_follow, ""); zdo.Set(ZDOVars.s_patrol, false); Tameable component = prefab.GetComponent<Tameable>(); Procreation component2 = prefab.GetComponent<Procreation>(); if (Object.op_Implicit((Object)(object)component)) { zdo.Set(ZDOVars.s_tameLastFeeding, Before(ticks, Math.Max(0f, component.m_fedDuration - state.FedSeconds))); } zdo.Set(ZDOVars.s_lovePoints, state.LovePoints, false); if (state.PregnancySeconds >= 0f) { InputAndFeedback.Require(Object.op_Implicit((Object)(object)component2), "This animal no longer supports its saved breeding state."); zdo.Set(ZDOVars.s_pregnant, Before(ticks, Math.Max(0f, component2.m_pregnancyDuration - state.PregnancySeconds))); } } internal static void Verify(Character actor, PackedAnimalState state) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = ((Component)actor).GetComponent<ZNetView>().GetZDO(); InputAndFeedback.Require(actor.IsTamed() && actor.GetLevel() == state.Level && Mathf.Abs(actor.GetHealth() - state.Health) < 0.001f && Mathf.Abs(actor.GetMaxHealth() - state.MaxHealth) < 0.001f && zDO.GetBool(ZDOVars.s_haveSaddleHash, false) == state.Saddle && zDO.GetString(ZDOVars.s_tamedName, "") == state.Name && zDO.GetString("PortableShips.AnimalIdentity", "") == state.Identity && zDO.GetInt("RandMatSeed", -1) == state.RandomSeed && Vector3.Distance(((Component)actor).transform.localScale, new Vector3(state.ScaleX, state.ScaleY, state.ScaleZ)) < 0.001f, "Animal restoration could not be verified. Keep the pending item and consult the journal."); if (AnimalRules.Horse(state.Prefab)) { InputAndFeedback.Require(zDO.GetInt("HorseMaterial", -1) == state.HorseMaterial && zDO.GetBool("HaveArmor", false) == state.HorseArmor && zDO.GetBool("IsWaiting", false) == state.HorseWaiting, "Horse coat/armor/wait restoration failed. Consult the journal."); } AnimalAppearance.Apply(actor, state); } } internal static class Beastbinding { [HarmonyPatch(typeof(Trader), "GetAvailableItems")] private static class WitchOffer { private static void Postfix(Trader __instance, List<TradeItem> __result) { //IL_004c: 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_005c: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; ZNetView componentInParent = ((Component)__instance).GetComponentInParent<ZNetView>(); if (Object.op_Implicit((Object)(object)localPlayer) && !Learned(localPlayer) && Object.op_Implicit((Object)(object)componentInParent) && componentInParent.IsValid() && componentInParent.GetZDO().GetPrefab() == StringExtensionMethods.GetStableHashCode("BogWitch")) { if (offer == null) { offer = new TradeItem { m_price = 1000, m_stack = 1, m_buyKey = "PortableShips.Beastbinding", m_name = "Beastbinding", m_requiredGlobalKey = "", m_incrementKey = "PortableShips.BeastbindingRank", m_incrementAmount = 1, m_tooltip = "Permanent character ability. Approach a tamed animal, aim at it and press P to shrink it. Health, stars, saddle, appearance and name are preserved. Use the packed animal from your hotbar to release it.", m_buyPlayerEffects = new EffectList { m_effectPrefabs = Array.Empty<EffectData>() }, m_levelUpEffect = true }; } offer.m_icon = AnimalIcons.For(AnimalIdentification.Types.First((AnimalType t) => t.Prefab == "Wolf")); if (!__result.Any((TradeItem i) => i.m_buyKey == "PortableShips.Beastbinding")) { __result.Add(offer); } } } } [HarmonyPatch(typeof(StoreGui), "BuySelectedItem")] private static class NativePurchase { private static bool Prefix(TradeItem ___m_selectedItem) { if (___m_selectedItem != null && !(___m_selectedItem.m_buyKey != "PortableShips.Beastbinding")) { return !Learned(Player.m_localPlayer); } return true; } private static void Postfix() { if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Publish(Player.m_localPlayer); } } } internal const string Key = "PortableShips.Beastbinding"; internal const string Proof = "PortableShips.BeastbindingLearned"; internal const int Price = 1000; private static TradeItem? offer; internal static bool Learned(Player player) { if (Object.op_Implicit((Object)(object)player)) { return ((Humanoid)player).HaveUniqueKey("PortableShips.Beastbinding"); } return false; } internal static void Publish(Player player) { ZNetView component = ((Component)player).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner() && (Object)(object)player == (Object)(object)Player.m_localPlayer) { component.GetZDO().Set("PortableShips.BeastbindingLearned", Learned(player)); } } } internal static class BoonCostPlan { internal static readonly string[] Prefabs = new string[3] { "Eitr", "YggdrasilWood", "JuteBlue" }; internal static readonly int[] Amounts = new int[3] { 20, 20, 10 }; internal static bool TryCreate(IReadOnlyList<string> prefabs, IReadOnlyList<int> stacks, out Dictionary<int, int> spend) { return TryCreate(prefabs, stacks, Prefabs, Amounts, out spend); } internal static bool TryCreate(IReadOnlyList<string> prefabs, IReadOnlyList<int> stacks, IReadOnlyList<string> costs, IReadOnlyList<int> amounts, out Dictionary<int, int> spend) { spend = new Dictionary<int, int>(); if (prefabs.Count != stacks.Count || costs.Count != amounts.Count) { return false; } for (int i = 0; i < costs.Count; i++) { int num = amounts[i]; if (num <= 0) { spend.Clear(); return false; } for (int j = 0; j < prefabs.Count; j++) { if (num <= 0) { break; } if (!(prefabs[j] != costs[i]) && stacks[j] > 0) { int num2 = Math.Min(num, stacks[j]); spend.Add(j, num2); num -= num2; } } if (num != 0) { spend.Clear(); return false; } } return true; } } internal sealed class BoonRuneVisual : MonoBehaviour { internal sealed class HullSurface : IDisposable { private readonly GameObject helper; internal readonly Collider[] Colliders; internal HullSurface(Ship ship) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) MeshRenderer val = VisibleHull(ship); MeshFilter val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MeshFilter>() : null); InputAndFeedback.Require(Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.sharedMesh), "Visible wooden hull mesh unavailable for rune projection."); helper = new GameObject("PortableShips.RuneProjectionQuery") { hideFlags = (HideFlags)61, layer = 2 }; try { helper.transform.SetPositionAndRotation(((Component)val2).transform.position, ((Component)val2).transform.rotation); helper.transform.localScale = ((Component)val2).transform.lossyScale; MeshCollider val3 = helper.AddComponent<MeshCollider>(); val3.sharedMesh = val2.sharedMesh; Colliders = (Collider[])(object)new Collider[1] { (Collider)val3 }; Physics.SyncTransforms(); } catch { helper.SetActive(false); Object.Destroy((Object)(object)helper); throw; } } public void Dispose() { helper.SetActive(false); Object.Destroy((Object)(object)helper); } } internal const float SurfaceClearance = 0.022f; private static Material? engraving; private static Texture2D? mask; private readonly List<Mesh> meshes = new List<Mesh>(); private readonly List<GameObject> visuals = new List<GameObject>(); private MeshRenderer? boundHull; private bool built; private float retryAt; internal static void EnsureAvailable(Ship ship) { Material(); int[] array = new int[2] { -1, 1 }; foreach (int side in array) { if (FindAnchor(ship, side, out var _)) { return; } } InputAndFeedback.Require(condition: false, "No suitable hull surface beside the helm for the rune engraving."); } private static Material Material() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: 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_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Expected O, but got Unknown //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)engraving)) { return engraving; } GameObject prefab = PrefabManager.Instance.GetPrefab("dvergrtown_secretdoor"); object obj; if (!Object.op_Implicit((Object)(object)prefab)) { obj = null; } else { Transform obj2 = prefab.transform.Find("secretdoor/hgih"); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<MeshRenderer>() : null); } MeshRenderer val = (MeshRenderer)obj; Texture2D val2 = (Texture2D)(Object.op_Implicit((Object)(object)val) ? /*isinst with value type is only supported in some contexts*/: null); InputAndFeedback.Require(Object.op_Implicit((Object)(object)val2), "Infested-mine secret-door rune asset unavailable; no upgrade materials were spent."); Shader val3 = Shader.Find("Unlit/Transparent") ?? Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default"); InputAndFeedback.Require(Object.op_Implicit((Object)(object)val3), "Rune engraving shader unavailable; no upgrade materials were spent."); Texture2D val4 = AssetUtils.DuplicateTexture(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height)); try { Color[] pixels = val4.GetPixels(); int num = ((Texture)val4).width; int num2 = ((Texture)val4).height; int num3 = -1; int num4 = -1; for (int i = 0; i < ((Texture)val4).height; i++) { for (int j = 0; j < ((Texture)val4).width; j++) { if (((Color)(ref pixels[i * ((Texture)val4).width + j])).maxColorComponent > 0.03f) { num = Math.Min(num, j); num3 = Math.Max(num3, j); num2 = Math.Min(num2, i); num4 = Math.Max(num4, i); } } } InputAndFeedback.Require(num3 >= num && num4 >= num2, "Secret-door rune mask is empty; upgrade cancelled."); int num5 = num3 - num + 5; int num6 = num4 - num2 + 5; mask = new Texture2D(num5, num6, (TextureFormat)4, true) { name = "PortableShips.BoonRuneMask", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)2, anisoLevel = 8, hideFlags = (HideFlags)32 }; Color[] array = Enumerable.Repeat<Color>(new Color(0.58f, 0.42f, 0.24f, 0f), num5 * num6).ToArray(); for (int k = 2; k < num6 - 2; k++) { for (int l = 2; l < num5 - 2; l++) { float maxColorComponent = ((Color)(ref pixels[(num2 + k - 2) * ((Texture)val4).width + num + l - 2])).maxColorComponent; array[k * num5 + l] = new Color(0.58f, 0.42f, 0.24f, maxColorComponent); } } mask.SetPixels(array); mask.Apply(true, true); engraving = new Material(val3) { name = "PortableShips.BoonEngraving", mainTexture = (Texture)(object)mask, hideFlags = (HideFlags)32 }; engraving.renderQueue = 3000; if (engraving.HasProperty("_Color")) { engraving.SetColor("_Color", Color.white); } return engraving; } finally { Object.Destroy((Object)(object)val4); } } internal static MeshRenderer? VisibleHull(Ship ship) { return ((IEnumerable<MeshRenderer>)((Component)ship).GetComponentsInChildren<MeshRenderer>()).FirstOrDefault((Func<MeshRenderer, bool>)((MeshRenderer r) => ((Renderer)r).enabled && ((Object)r).name == "hull" && Object.op_Implicit((Object)(object)((Component)r).transform.parent) && (((Object)((Component)r).transform.parent).name.StartsWith("hull_", StringComparison.Ordinal) || ((Object)((Component)r).transform.parent).name.StartsWith("visual_", StringComparison.Ordinal)))); } internal static bool RayHull(Collider[] hull, Ray ray, float distance, out RaycastHit nearest) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) nearest = default(RaycastHit); bool flag = false; RaycastHit val = default(RaycastHit); for (int i = 0; i < hull.Length; i++) { if (hull[i].Raycast(ray, ref val, distance) && (!flag || ((RaycastHit)(ref val)).distance < ((RaycastHit)(ref nearest)).distance)) { nearest = val; flag = true; } } return flag; } private static bool FindAnchor(Ship ship, int side, out RaycastHit hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) hit = default(RaycastHit); if (!Object.op_Implicit((Object)(object)ship.m_shipControlls) || !Object.op_Implicit((Object)(object)ship.m_shipControlls.m_attachPoint)) { return false; } Vector3 val = ((Component)ship).transform.InverseTransformPoint(ship.m_shipControlls.m_attachPoint.position); using HullSurface hullSurface = new HullSurface(ship); Collider[] colliders = hullSurface.Colliders; for (float num = val.y + 2.2f; num >= val.y - 0.25f; num -= 0.08f) { float[] array = new float[5] { -0.35f, 0f, 0.5f, 1f, 1.6f }; foreach (float num2 in array) { Vector3 val2 = ((Component)ship).transform.TransformPoint(new Vector3((float)side * 30f, num, val.z + num2)); if (RayHull(colliders, new Ray(val2, ((Component)ship).transform.right * (float)(-side)), 30f, out hit) && Vector3.Dot(((RaycastHit)(ref hit)).normal, ((Component)ship).transform.right * (float)side) > 0.3f && SkidbladnirBoon.NearHelm(ship, ((RaycastHit)(ref hit)).point) && Project(ship, hit, colliders, out Vector3[] _, out Vector2[] _, out int[] _)) { return true; } } } return false; } private void Update() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (Time.time < retryAt || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return; } retryAt = Time.time + 3f; Ship component = ((Component)this).GetComponent<Ship>(); if (!SkidbladnirBoon.IsInstalled(((Component)this).GetComponent<ZNetView>())) { return; } MeshRenderer val = VisibleHull(component); if (built && (Object)(object)val == (Object)(object)boundHull) { return; } Clear(); try { Material val2 = Material(); int[] array = new int[2] { -1, 1 }; foreach (int side in array) { if (FindAnchor(component, side, out var hit)) { Build(component, hit, val2); } } built = meshes.Count > 0; if (!built) { throw new InvalidOperationException("No rune engraving anchor found beside the helm."); } boundHull = val; PortableShipsPlugin.Instance.LogInfo($"Boon runes created: ship={((Object)component).name} sides={meshes.Count} hull={((Object)((Component)val).transform.parent).name} shader={((Object)val2.shader).name}"); } catch (Exception ex) { PortableShipsPlugin.Instance.Error("Boon rune display: " + ex.Message); } } private void Build(Ship ship, RaycastHit anchor, Material material) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown using HullSurface hullSurface = new HullSurface(ship); if (Project(ship, anchor, hullSurface.Colliders, out Vector3[] vertices, out Vector2[] uv, out int[] indices)) { Mesh val = new Mesh { name = "PortableShips.SkidbladnirRunes" }; val.vertices = vertices; val.uv = uv; val.triangles = indices; val.RecalculateNormals(); val.RecalculateBounds(); meshes.Add(val); GameObject val2 = new GameObject("SkidbladnirBoon_Engraving"); val2.transform.SetParent(((Component)ship).transform, false); val2.AddComponent<MeshFilter>().sharedMesh = val; MeshRenderer obj = val2.AddComponent<MeshRenderer>(); ((Renderer)obj).sharedMaterial = material; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; visuals.Add(val2); } } private static bool Project(Ship ship, RaycastHit anchor, Collider[] hull, out Vector3[] vertices, out Vector2[] uv, out int[] indices) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_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_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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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) Vector3 normal = ((RaycastHit)(ref anchor)).normal; Vector3 val = Vector3.Cross(((Component)ship).transform.up, normal); Vector3 normalized = ((Vector3)(ref val)).normalized; val = Vector3.Cross(normal, normalized); Vector3 normalized2 = ((Vector3)(ref val)).normalized; vertices = (Vector3[])(object)new Vector3[429]; uv = (Vector2[])(object)new Vector2[vertices.Length]; indices = Array.Empty<int>(); List<int> list = new List<int>(); for (int i = 0; i <= 12; i++) { for (int j = 0; j <= 32; j++) { int num = i * 33 + j; uv[num] = new Vector2((float)j / 32f, (float)i / 12f); Vector3 val2 = ((RaycastHit)(ref anchor)).point + normalized * ((uv[num].x - 0.5f) * 1.1f) + normalized2 * ((uv[num].y - 0.5f) * 0.44f); if (!RayHull(hull, new Ray(val2 + normal, -normal), 2f, out var nearest)) { return false; } vertices[num] = ((Component)ship).transform.InverseTransformPoint(((RaycastHit)(ref nearest)).point + ((RaycastHit)(ref nearest)).normal * 0.022f); if (j < 32 && i < 12) { list.AddRange(new int[6] { num, num + 1, num + 32 + 2, num, num + 32 + 2, num + 32 + 1 }); } } } indices = list.ToArray(); return true; } private void Clear() { foreach (GameObject visual in visuals) { if (Object.op_Implicit((Object)(object)visual)) { visual.SetActive(false); Object.Destroy((Object)(object)visual); } } foreach (Mesh mesh in meshes) { if (Object.op_Implicit((Object)(object)mesh)) { Object.Destroy((Object)(object)mesh); } } visuals.Clear(); meshes.Clear(); built = false; boundHull = null; } private void OnDestroy() { Clear(); } } internal static class DeploymentRecovery { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void <Register>b__0_0(ConsoleEventArgs _) { PortableShipsPlugin.Instance.Run(Begin); } } internal static void Register() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate { PortableShipsPlugin.Instance.Run(Begin); }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("portableships_recover", "Aim at an interrupted summoned ship with its pending item in your inventory. Verifies it and completes deployment; never spawns another ship.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Begin() { //IL_0146: Unknown result type (might be due to invalid IL or missing references) Player player = Player.m_localPlayer; InputAndFeedback.Require(Object.op_Implicit((Object)(object)player) && Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)ZNetScene.instance), "Enter the world containing the interrupted ship first."); CheckPlayer(player); Ship ship = ShipPackingService.AimedShip(player); InputAndFeedback.Require(Object.op_Implicit((Object)(object)ship), "Stand outside and aim at the interrupted ship's hull."); ZNetView view = ((Component)ship).GetComponent<ZNetView>(); ShipType type = ShipIdentification.FromShip(ship); ItemData[] array = (from i in ((Humanoid)player).GetInventory().GetAllItems() where ShipIdentification.FromItem(i) == type && DeploymentRecoveryPolicy.Matches(i.m_customData, i.m_stack, type.Prefab, type.Prefab, view.GetZDO().GetString("PortableShips.Transaction", "")) select i).ToArray(); InputAndFeedback.Require(array.Length == 1, "Need exactly one matching pending item and its locked ship in this world. Nothing changed."); ItemData item = array[0]; InputAndFeedback.Require(PackedShipPayload.TryRead(new Dictionary<string, string>(item.m_customData) { ["PortableShips.State"] = "ready" }, type.Prefab, item.m_stack, out float _, out string error), error); ShipAddonAPI.Validate(item); UpgradePayload upgrades = LongshipUpgradesCompatibility.Read(item, type); InputAndFeedback.Require(PrivateArea.CheckAccess(((Component)ship).transform.position, 0f, true, false), "A ward prevents recovery here."); ShipPackingAccess access = ((Component)ship).GetComponent<ShipPackingAccess>(); InputAndFeedback.Require(Object.op_Implicit((Object)(object)access), "Ship still initializing."); string tx = item.m_customData["PortableShips.Transaction"]; ShipCast.Start(Resume(), delegate { try { if (Object.op_Implicit((Object)(object)access)) { access.Release(tx); } } finally { PortableShipsPlugin.Instance.Busy = false; } }); IEnumerator Resume() { TransactionJournal.Write(tx, "deploy-recovery-request", ((object)Unsafe.As<ZDOID, ZDOID>(ref view.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString()); access.BeginRecoveryRequest(tx); float deadline = Time.realtimeSinceStartup + 5f; while (!ShipPackingAccess.OwnsReservation(view, tx)) { CheckPlayer(player); InputAndFeedback.Require(Object.op_Implicit((Object)(object)ship) && (Object)(object)ShipPackingService.AimedShip(player) == (Object)(object)ship, "Keep aiming at the same hull during recovery."); InputAndFeedback.Require(string.IsNullOrEmpty(access.Rejection), access.Rejection); InputAndFeedback.Require(Time.realtimeSinceStartup < deadline, "Recovery authority timed out. Nothing consumed."); yield return null; } IEnumerator completion = ShipDeploymentService.Complete(player, ((Humanoid)player).GetInventory(), item, view, upgrades, type, tx); while (completion.MoveNext()) { yield return completion.Current; } } } private static void CheckPlayer(Player player) { InputAndFeedback.Require(Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InAttack() && !((Character)player).InDodge() && player.GetActionQueueCount() == 0, "Finish your current action before recovering the ship."); } } internal static class DeploymentRecoveryPolicy { internal static bool Matches(IDictionary<string, string> data, int stack, string prefab, string actualPrefab, string shipLock) { if (stack == 1 && data.TryGetValue("PortableShips.State", out string value) && value == "deploying" && data.TryGetValue("PortableShips.Transaction", out string value2) && Guid.TryParseExact(value2, "N", out var _) && data.TryGetValue("PortableShips.Ship", out string value3) && value3 == prefab && actualPrefab == prefab) { return shipLock == "deploy:" + value2; } return false; } } internal sealed class HullInscription : MonoBehaviour { internal TMP_FontAsset Font; internal MeshFilter BoardSource; private TextMeshPro? generator; private readonly List<GameObject> parts = new List<GameObject>(); private readonly List<Mesh> meshes = new List<Mesh>(); private readonly List<Material> materials = new List<Material>(); private string? lastName; private MeshRenderer? lastHull;