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 PetManeater v1.0.1
plugins/PetManeater.dll
Decompiled 3 days 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; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalLib.Modules; using Microsoft.CodeAnalysis; using PetManeater.Compat; using PetManeater.Content; using PetManeater.Core; using PetManeater.Patches; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations.Rigging; using UnityEngine.Rendering.HighDefinition; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PetManeater")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+8c66be17c3e0ede4ed166b464865f019e78bf511")] [assembly: AssemblyProduct("PetManeater")] [assembly: AssemblyTitle("PetManeater")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PetManeater { [BepInPlugin("Solon.PetManeater", "PetManeater", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static PluginConfig Cfg { get; private set; } private void Awake() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { Cfg = new PluginConfig(((BaseUnityPlugin)this).Config); int num = Cfg.VerifyAllBound(); PetRules.UseLocal(); _harmony = new Harmony("Solon.PetManeater"); _harmony.PatchAll(typeof(CorePatches)); _harmony.PatchAll(typeof(TamingPatches)); _harmony.PatchAll(typeof(SightPatches)); _harmony.PatchAll(typeof(SteeringPatches)); _harmony.PatchAll(typeof(ContentPatch)); _harmony.PatchAll(typeof(WhistlePatches)); _harmony.PatchAll(typeof(CarrierPatches)); _harmony.PatchAll(typeof(CarrierWornPatches)); _harmony.PatchAll(typeof(CarrierUiPatches)); _harmony.PatchAll(typeof(CarryPatches)); _harmony.PatchAll(typeof(RiderPatches)); _harmony.PatchAll(typeof(KennelPatches)); _harmony.PatchAll(typeof(PersistencePatches)); _harmony.PatchAll(typeof(SprayPatches)); _harmony.PatchAll(typeof(PlayPatches)); _harmony.PatchAll(typeof(StormPatches)); _harmony.PatchAll(typeof(ShopPatches)); _harmony.PatchAll(typeof(ImperiumNeutral)); _harmony.PatchAll(typeof(PathingCompat)); _harmony.PatchAll(typeof(QuicksandCompat)); _harmony.PatchAll(typeof(DeviantCompat)); ImperiumCompat.Initialize(_harmony); GordionCompat.Initialize(_harmony); ImperiumNeutral.Initialize(); PathingCompat.Initialize(_harmony); QuicksandCompat.Initialize(_harmony); DeviantCompat.Initialize(_harmony); RemnantsCompat.Initialize(_harmony); RuntimeIconsCompat.Detect(); VerifyPatched(); Log.LogInfo((object)("[Pet] PetManeater v1.0.1 loaded. Taming: " + (Cfg.Enabled.Value ? "on" : "off") + ", " + $"hold {Cfg.HoldSecondsRequired.Value:0.#}s, " + $"limit {Cfg.MaxTamedPerLobby.Value} per lobby" + ((num > 0) ? $" -- WARNING: {num} CONFIG ENTRIES UNBOUND, see errors above." : "."))); } catch (Exception arg) { Log.LogError((object)$"[Pet] Failed to initialize; the mod is inactive: {arg}"); } } private void VerifyPatched() { Check(AccessTools.Method(typeof(EnemyAI), "PlayerIsTargetable", (Type[])null, (Type[])null), "EnemyAI.PlayerIsTargetable", "pets would still hunt their owner"); Check(AccessTools.Method(typeof(CaveDwellerAI), "Update", (Type[])null, (Type[])null), "CaveDwellerAI.Update", "taming progress would never be measured"); Check(AccessTools.Method(typeof(TetraChemicalItem), "ItemActivate", (Type[])null, (Type[])null), "TetraChemicalItem.ItemActivate", "the whistle would emit gas instead of commands"); Check(AccessTools.Method(typeof(SprayPaintItem), "AddSprayPaintLocal", (Type[])null, (Type[])null), "SprayPaintItem.AddSprayPaintLocal", "spraying a pet would dye it instead of marking it"); } private void Check(MethodInfo target, string name, string consequence) { if (target == null) { Log.LogError((object)("[Pet] " + name + " was not found - the game version may have changed it. INACTIVE: " + consequence + ".")); return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)target); if (patchInfo?.Prefixes?.Any((Patch p) => p.owner == "Solon.PetManeater") != true && patchInfo?.Postfixes?.Any((Patch p) => p.owner == "Solon.PetManeater") != true) { Log.LogWarning((object)("[Pet] WARNING: no patch attached to " + name + " - " + consequence + ".")); } else { DebugLog("Patch active on " + name + "."); } } internal static void DebugLog(string message) { if (Cfg != null && Cfg.DebugMode.Value) { Log.LogInfo((object)("[Pet][Debug] " + message)); } } private void OnDestroy() { PetNetwork.Unregister(); PetMarker.Clear(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal sealed class PluginConfig { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> DebugMode; public readonly ConfigEntry<float> HoldSecondsRequired; public readonly ConfigEntry<int> MaxTamedPerLobby; public readonly ConfigEntry<bool> RequireFeeding; public readonly ConfigEntry<bool> NoEatingOnShip; public readonly ConfigEntry<bool> SnareFleaProvokes; public readonly ConfigEntry<bool> SprayPaintRecolours; public readonly ConfigEntry<bool> SprayPaintMarks; public readonly ConfigEntry<string> WhistleName; public readonly ConfigEntry<int> WhistlePrice; public readonly ConfigEntry<float> WhistleWarmUpSeconds; public readonly ConfigEntry<float> LongCommandSeconds; public readonly ConfigEntry<bool> WhistleAttractsDogs; public readonly ConfigEntry<float> WhistleVolume; public readonly ConfigEntry<float> HornVolume; public readonly ConfigEntry<float> CloseMin; public readonly ConfigEntry<float> CloseMax; public readonly ConfigEntry<string> CarrierName; public readonly ConfigEntry<int> CarrierPrice; public readonly ConfigEntry<int> PackCapacity; public readonly ConfigEntry<bool> KennelEnabled; public readonly ConfigEntry<string> KennelUnlockableName; public readonly ConfigEntry<int> KennelCapacity; public readonly ConfigEntry<float> KennelEnterDistance; public readonly ConfigEntry<float> KennelAngerRecovery; public readonly ConfigEntry<bool> StockAllFurniture; public readonly ConfigEntry<bool> PetOrderForSale; public readonly ConfigEntry<int> PetOrderPrice; public readonly ConfigEntry<bool> ShipDoorRule; public readonly ConfigEntry<float> CatchUpSpeed; public readonly ConfigEntry<float> CatchUpSeconds; public readonly ConfigEntry<float> WhistleLoopGain; public readonly ConfigEntry<bool> ImperiumSpawnEntry; public readonly ConfigEntry<bool> LaserPointerPlay; public readonly ConfigEntry<string> LaserItemName; public readonly ConfigEntry<bool> SoccerBallFetch; public readonly ConfigEntry<float> FollowMinDistance; public readonly ConfigEntry<float> FollowMaxDistance; public readonly ConfigEntry<bool> ResetAngerEachMoon; public readonly ConfigEntry<bool> LightningNeedsMetal; public readonly ConfigEntry<bool> CarrierExportTextures; public readonly ConfigEntry<float> DaylightSightHour; public readonly ConfigEntry<float> EclipseSightHour; public readonly ConfigEntry<float> RingMin; public readonly ConfigEntry<float> RingMax; public readonly ConfigEntry<float> ClosingSpeedMultiplier; public readonly ConfigEntry<bool> CombatEnabled; public readonly ConfigEntry<float> CombatRange; public readonly ConfigEntry<int> AttackForce; public readonly ConfigEntry<float> AttackCooldown; public readonly ConfigEntry<float> CombatLeash; public readonly ConfigEntry<bool> DiagnosticsEnabled; public readonly ConfigEntry<float> SecondsCryingToLosePet; internal int VerifyAllBound() { int num = 0; FieldInfo[] fields = typeof(PluginConfig).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (typeof(ConfigEntryBase).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(this) == null) { num++; Plugin.Log.LogError((object)("[Pet] Config entry '" + fieldInfo.Name + "' was never bound. Anything reading it will throw; that feature is broken until this is fixed.")); } } return num; } public PluginConfig(ConfigFile cfg) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Expected O, but got Unknown //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Expected O, but got Unknown //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Expected O, but got Unknown //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Expected O, but got Unknown //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Expected O, but got Unknown //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_039b: Expected O, but got Unknown //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Expected O, but got Unknown //IL_041f: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Expected O, but got Unknown //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_049a: Expected O, but got Unknown //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_04d3: Expected O, but got Unknown //IL_051e: Unknown result type (might be due to invalid IL or missing references) //IL_0528: Expected O, but got Unknown //IL_054c: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Expected O, but got Unknown //IL_0585: Unknown result type (might be due to invalid IL or missing references) //IL_058f: Expected O, but got Unknown //IL_05f3: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Expected O, but got Unknown //IL_062c: Unknown result type (might be due to invalid IL or missing references) //IL_0636: Expected O, but got Unknown //IL_0665: Unknown result type (might be due to invalid IL or missing references) //IL_066f: Expected O, but got Unknown //IL_06eb: Unknown result type (might be due to invalid IL or missing references) //IL_06f5: Expected O, but got Unknown //IL_0724: Unknown result type (might be due to invalid IL or missing references) //IL_072e: Expected O, but got Unknown //IL_075d: Unknown result type (might be due to invalid IL or missing references) //IL_0767: Expected O, but got Unknown //IL_07ca: Unknown result type (might be due to invalid IL or missing references) //IL_07d4: Expected O, but got Unknown //IL_081f: Unknown result type (might be due to invalid IL or missing references) //IL_0829: Expected O, but got Unknown //IL_08b0: Unknown result type (might be due to invalid IL or missing references) //IL_08ba: Expected O, but got Unknown //IL_08e9: Unknown result type (might be due to invalid IL or missing references) //IL_08f3: Expected O, but got Unknown //IL_0917: Unknown result type (might be due to invalid IL or missing references) //IL_0921: Expected O, but got Unknown Enabled = cfg.Bind<bool>("1. General", "Enabled", true, "Master switch. When off the Maneater behaves exactly as it does in vanilla."); DebugMode = cfg.Bind<bool>("1. General", "DebugMode", false, "Verbose logging: which conditions were met, who owns what, network traffic."); HoldSecondsRequired = cfg.Bind<float>("2. Taming", "Hold Seconds Required", 60f, new ConfigDescription("Total time the baby must spend in its future owner's arms. Cumulative - it does not have to be one unbroken stretch.\nIn multiplayer the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>())); MaxTamedPerLobby = cfg.Bind<int>("2. Taming", "Max Tamed Per Lobby", 4, new ConfigDescription("How many Maneaters the whole crew can have tamed at once. Matches the dog house's capacity.\nIn multiplayer the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>())); RequireFeeding = cfg.Bind<bool>("2. Taming", "Require Feeding", true, "Whether the baby must have eaten before it can be tamed.\nIn multiplayer the host's value is used."); NoEatingOnShip = cfg.Bind<bool>("2. Taming", "No Eating On Ship", true, "Section 8: a Maneater aboard the ship takes no interest in scrap at all, so it cannot eat the day's takings while nobody is looking.\nApplies to every Maneater and not only a tamed one - a wild baby carried aboard is the same problem."); SnareFleaProvokes = cfg.Bind<bool>("6. Combat", "Snare Flea Provokes", true, "Section 5.5: a Snare Flea clamped onto somebody makes a monster-form pet attack it - through the person it is sitting on.\nThis is the one case where a pet can hurt its own owner, and it is meant to: the creature is a predator with a grudge and a mouthful of teeth, not a trained dog, and somebody has to decide whether being freed is worth the risk. Turn it off and a pet ignores a flea riding a player exactly as it ignores every other threat to them."); SprayPaintRecolours = cfg.Bind<bool>("3. Behaviour", "Spray Paint Recolours Pets", true, "Section 9.4: spraying a tamed Maneater with the vanilla paint can colours the creature instead of leaving a mark on it, so four identical pets can be told apart."); SprayPaintMarks = cfg.Bind<bool>("3. Behaviour", "Spray Paint Marks Instead Of Dyeing", false, "Off (the default): a squirt from the paint can turns the WHOLE creature that colour, instantly, on every machine, and it stays that colour through a change of form and a flight to the next moon.\nOn: the can leaves individual marks where you aimed instead, hung off the creature's bones the same way the game marks a player. That is the more faithful answer and it was the default for a while - but the reason to paint a pet at all is to tell four of them apart across a room, and a smudge on a shoulder does not do that."); LightningNeedsMetal = cfg.Bind<bool>("3. Behaviour", "Lightning Only After Eating Metal", true, "Whether a storm ignores a tamed pet until it has actually swallowed something conductive.\nIt ignores nothing by default in the base game, and not because anyone decided a pet should be struck: the baby is a carryable object, its item is flagged as conductive metal, and the weather targets every such object in the level. So an animal following its owner across a field was, to the storm, the nearest piece of scrap. Eating a length of pipe puts it back on the list, which is the version of this rule worth having. Wild Maneaters are not affected either way."); FollowMinDistance = cfg.Bind<float>("3. Behaviour", "Follow Min Distance", 5f, new ConfigDescription("A tamed pet closer than this to its owner stops advancing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); FollowMaxDistance = cfg.Bind<float>("3. Behaviour", "Follow Max Distance", 8f, new ConfigDescription("A tamed pet further than this from its owner starts closing the gap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 60f), Array.Empty<object>())); DaylightSightHour = cfg.Bind<float>("3. Behaviour", "Daylight Sight Hour", 18f, new ConfigDescription("Outdoors, a pet is blind until this hour and cannot find its owner. 18 = 6 PM.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 24f), Array.Empty<object>())); EclipseSightHour = cfg.Bind<float>("3. Behaviour", "Eclipse Sight Hour", 13f, new ConfigDescription("The same, on an eclipsed moon - there is no sun to blind it, so it sees earlier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 24f), Array.Empty<object>())); RingMin = cfg.Bind<float>("3. Behaviour", "Ring Min", 15f, new ConfigDescription("Nearest the pet will settle in its roaming mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 90f), Array.Empty<object>())); RingMax = cfg.Bind<float>("3. Behaviour", "Ring Max", 20f, new ConfigDescription("Furthest it will settle in its roaming mode. Wide enough that it still prowls and investigates on its own.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); SecondsCryingToLosePet = cfg.Bind<float>("3. Behaviour", "Seconds Crying To Lose Pet", 90f, new ConfigDescription("How long a pet must be left crying, unattended and away from the ship, before it stops being yours. Rocking it resets nothing but stops the clock.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), Array.Empty<object>())); WhistleName = cfg.Bind<string>("4. Whistle", "Item Name", "Training whistle", "Name shown in the terminal and the HUD. Needs a game restart."); WhistlePrice = cfg.Bind<int>("4. Whistle", "Price", 30, new ConfigDescription("Terminal price of the training whistle. Needs a game restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); WhistleWarmUpSeconds = cfg.Bind<float>("4. Whistle", "Warm Up Seconds", 0.89f, new ConfigDescription("The item is raised to the mouth before any sound comes out, so nothing is heard - and no command is given - until it has been held this long. Letting go sooner does nothing at all.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); LongCommandSeconds = cfg.Bind<float>("4. Whistle", "Long Command Seconds", 3f, new ConfigDescription("Hold the whistle at least this long to call the pet to you. A shorter blast - but still longer than the warm-up - switches it between its baby and monster forms.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); WhistleAttractsDogs = cfg.Bind<bool>("4. Whistle", "Attracts Dogs", true, "The whistle is loud. Outside, that is a problem - which is the point."); CloseMin = cfg.Bind<float>("4. Whistle", "Close Min", 3f, new ConfigDescription("Nearest the pet will settle in its close mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); CloseMax = cfg.Bind<float>("4. Whistle", "Close Max", 5f, new ConfigDescription("Furthest it will settle in its close mode. A BAND rather than a single distance on purpose: aimed at one exact radius the creature overshoots, decides it is out of position and immediately picks somewhere new, which reads as pacing rather than as following.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 40f), Array.Empty<object>())); ImperiumSpawnEntry = cfg.Bind<bool>("5. Compatibility", "Imperium Spawn Entry", true, "When Imperium is installed, add a \"Maneater (Pet)\" row to its spawn menu that produces an already-tamed pet. A testing convenience - it skips the taming conditions entirely, so leave it off if you would rather not be able to cheat."); ResetAngerEachMoon = cfg.Bind<bool>("3. Behaviour", "Reset Anger Each Moon", true, "Clear accumulated resentment on every landing, as the design calls for."); WhistleVolume = cfg.Bind<float>("4. Whistle", "Whistle Volume", 1f, new ConfigDescription("How loud the whistle itself is. The donor item is a quiet one - it is meant to be heard by the person holding it and nobody else.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); HornVolume = cfg.Bind<float>("4. Whistle", "Horn Volume", 1f, new ConfigDescription("How loud the pet's mode horn is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); CombatEnabled = cfg.Bind<bool>("6. Combat", "Enabled", true, "In its monster form a pet hunts other monsters. Turn off and it will only follow."); CombatRange = cfg.Bind<float>("6. Combat", "Range", 25f, new ConfigDescription("How far a pet looks for something to fight. It still has to SEE the target - a creature that charges whatever is nearest walks through walls to get to it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>())); AttackForce = cfg.Bind<int>("6. Combat", "Attack Force", 2, new ConfigDescription("Damage per connected leap. The wild Maneater's charge takes a player's whole health bar, so the monster equivalent is 10 - which kills most creatures in one, exactly as section 5.3 intends.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>())); AttackCooldown = cfg.Bind<float>("6. Combat", "Attack Cooldown", 1.5f, new ConfigDescription("Seconds between charges, so a fight is a fight rather than an instant deletion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>())); CarrierExportTextures = cfg.Bind<bool>("8. Carrier", "Export Textures", false, "Writes the carrier's own texture maps out as PNG files, so they can be repainted by hand.\nThey land in a PetManeater-Carrier folder beside the plugin, one PNG per map, taken before this mod dyes anything - so what you get is the game's artwork, not its own guess at a colour. Drop a repainted copy into the `custom` folder inside it, keeping the file name and the image size, and it is used exactly as drawn: the automatic recolouring is skipped entirely for any map you have replaced. Needs a game restart either way, since the bag is built once at startup."); CarrierName = cfg.Bind<string>("8. Carrier", "Item Name", "Mother's carrier", "Name shown in the terminal and the HUD. Needs a game restart."); CarrierPrice = cfg.Bind<int>("8. Carrier", "Price", 120, new ConfigDescription("Terminal price of the carrier. Needs a game restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); ClosingSpeedMultiplier = cfg.Bind<float>("3. Behaviour", "Closing Speed Multiplier", 2f, new ConfigDescription("How much faster a pet moves while it is on its way in after being called close. It applies ONLY while closing the gap - once it is inside the near band it moves at its own speed again, and it is never sped up in the roaming mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>())); CombatLeash = cfg.Bind<float>("6. Combat", "Leash", 20f, new ConfigDescription("How far from ITS OWNER a pet will go to pick a fight. Measured from the owner rather than from the pet on purpose: measured from the pet, a hunt leads to the next hunt and the pet fights its way across the level, never returning and never appearing to hear the whistle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); DiagnosticsEnabled = cfg.Bind<bool>("7. Diagnostics", "Enabled", true, "Watch every tamed pet and, when one stops moving while it was supposed to be going somewhere, write a single report naming WHY - with the seconds leading up to it. On by default while the standing-still problem is being tracked down; it only writes anything when a pet actually freezes, so it is quiet on a healthy round."); KennelEnabled = cfg.Bind<bool>("9. Kennel", "Enabled", true, "Whether a pet can be sent to wait in the dog house. Whistle while looking at one.\nIn multiplayer the host's value is used."); KennelUnlockableName = cfg.Bind<string>("9. Kennel", "Unlockable Name", "Dog house", "Which piece of ship furniture is the kennel, matched by name without regard to case or to the rest of the name.\nA name and not a number: the unlockables are a list other mods add to, so a fixed index that is right today quietly addresses somebody else's furniture tomorrow."); KennelCapacity = cfg.Bind<int>("9. Kennel", "Capacity", 4, new ConfigDescription("How many pets fit in one dog house. A pet still walking there has already taken its place.\nIn multiplayer the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>())); KennelEnterDistance = cfg.Bind<float>("9. Kennel", "Enter Distance", 2f, new ConfigDescription("How close a pet must get to the dog house to count as having gone inside.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), Array.Empty<object>())); KennelAngerRecovery = cfg.Bind<float>("9. Kennel", "Recovery Per Second", 0.06f, new ConfigDescription("How fast resentment and loneliness fall while a pet is in the kennel. The default is three times what a calm pet recovers at outside it, so a full meter clears in about twenty seconds of quiet.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); StockAllFurniture = cfg.Bind<bool>("9. Kennel", "Stock All Furniture", false, "List every piece of ship furniture in the terminal, every time, instead of the four to six the game picks at random from the day's seed.\nHere so that testing the dog house does not mean re-rolling days until it turns up. It changes nothing about prices, placement or what the furniture does - only what the shop is willing to show you.\nIn multiplayer the host's value is used, and it is applied on every machine: a list only the host could see would let the host buy furniture nobody else could."); PetOrderForSale = cfg.Bind<bool>("11. Shop", "Sell Tamed Maneaters", true, "Whether the terminal sells a tamed Maneater, delivered by the dropship to whoever ordered it - fed, calm and ready to follow.\nOne per player, and none once the crew is at its pet limit: the terminal refuses the order before any credits are spent. In multiplayer the host's value is used. The item is listed either way, so every machine's shop has the same items in the same order."); PetOrderPrice = cfg.Bind<int>("11. Shop", "Tamed Maneater Price", 900, new ConfigDescription("What a tamed Maneater costs. In multiplayer the host's price is used on every terminal.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>())); ShipDoorRule = cfg.Bind<bool>("3. Behaviour", "Keep Pets Aboard With You", true, "While its owner is on the ship, a pet that walked aboard by itself is held to the follow distance instead of its mode's roaming ring.\nThis is what stops a pet losing itself outdoors: a ring of fifteen to thirty metres reaches well past the hull, so the pet steps out of the open hatch to reach the far side of it, gets distracted on the way, and is gone. It still walks out to follow an owner who has left - anything stricter would be a locked door."); CatchUpSpeed = cfg.Bind<float>("3. Behaviour", "Catch Up Speed", 1.6f, new ConfigDescription("How much faster a pet that has fallen behind may run while it catches up.\nThe one exception to moving exactly like a wild Maneater. A pet that loses its route used to be TELEPORTED to its owner; it is not any more, in any situation, because a creature that appears beside you did not come to you and a player who has seen that once stops believing anything it does. A leg up is the honest version. 1 turns it off entirely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), Array.Empty<object>())); LaserPointerPlay = cfg.Bind<bool>("10. Play", "Laser Pointer", true, "Section 3.1: a baby pet chases the dot from a laser pointer, the way a cat does.\nOnly your own pet, only the baby form, and never while it is in your hands, in the carrier, in the dog house, crying, or parked on the ship. It gets no speed bonus and no priority over anything real - the design is explicit that this must not become a way of steering a pet about, and a torch that outranks a whistled order is exactly that."); LaserItemName = cfg.Bind<string>("10. Play", "Laser Item Name", "laser", "Part of the item's name, matched without regard to case.\nMatched by name because the laser pointer is not its own class in this game - it is a FlashlightItem with a narrow beam - so there is nothing else about it to recognise."); SoccerBallFetch = cfg.Bind<bool>("10. Play", "Fetch The Ball", true, "Section 3.2: kick a football and a baby pet runs it down, picks it up and brings it back to your feet. The same list of things it will not interrupt applies."); CatchUpSeconds = cfg.Bind<float>("3. Behaviour", "Catch Up Seconds", 6f, new ConfigDescription("How long that leg up lasts before the pet is back to its ordinary pace.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), Array.Empty<object>())); WhistleLoopGain = cfg.Bind<float>("4. Whistle", "Loop Gain", 1f, new ConfigDescription("Extra loudness for the whistle's looping body, on top of the automatic normalisation.\nThe clip this is cut from is a quiet recording, and an AudioSource cannot be turned above 1, so the loop is amplified in the samples themselves until it peaks just short of clipping. Raise this only if that is still not enough - past about 1.5 it will start to distort.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 4f), Array.Empty<object>())); PackCapacity = cfg.Bind<int>("8. Carrier", "Pack Capacity", 4, new ConfigDescription("How much the carrier holds. One large item is what the design asks for: the point is a Maneater walking your heaviest piece of scrap home for you, not a second inventory.\nIn multiplayer the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 15), Array.Empty<object>())); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Solon.PetManeater"; public const string PLUGIN_NAME = "PetManeater"; public const string PLUGIN_VERSION = "1.0.1"; } } namespace PetManeater.Patches { [HarmonyPatch] internal static class CarrierPatches { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(BeltBagItem), "ItemInteractLeftRight")] private static bool ItemInteractLeftRight_Prefix(BeltBagItem __instance, bool right) { if (!CarrierContent.IsCarrier((GrabbableObject)(object)__instance)) { return true; } if (right) { CarryPatches.Interact(__instance); } else { CarryPatches.Load(__instance); } return false; } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger")] private static void SetHoverTip_Postfix(PlayerControllerB __instance) { if (!Plugin.Cfg.Enabled.Value || (Object)(object)__instance.cursorTip == (Object)null) { return; } CaveDwellerAI val = CarryPatches.TargetedPet(__instance); if ((Object)(object)val == (Object)null) { return; } if (PetForm.IsAdult(val)) { if (PetMarker.IsOwnedBy((EnemyAI)(object)val, __instance) && (Object)(object)PetPack.PackOn(val) == (Object)null) { ((TMP_Text)__instance.cursorTip).text = "Strap the carrier on : [E]"; } } else { ((TMP_Text)__instance.cursorTip).text = (PetCarry.IsRiding(val) ? "Take out : [E] [E]" : "Put in carrier : [E]"); } } [HarmonyPrefix] [HarmonyPatch(typeof(BeltBagItem), "TryAddObjectToBag")] private static bool TryAddObjectToBag_Prefix(BeltBagItem __instance, GrabbableObject gObject) { if (CarrierContent.IsCarrier((GrabbableObject)(object)__instance)) { return WillTake(__instance, gObject); } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(BeltBagItem), "TryAddObjectToBagServerRpc")] private static bool TryAddObjectToBagServerRpc_Prefix(BeltBagItem __instance, NetworkObjectReference netObjectRef) { if (!CarrierContent.IsCarrier((GrabbableObject)(object)__instance)) { return true; } NetworkObject val = default(NetworkObject); GrabbableObject item = ((((NetworkObjectReference)(ref netObjectRef)).TryGet(ref val, (NetworkManager)null) && (Object)(object)val != (Object)null) ? ((Component)val).GetComponent<GrabbableObject>() : null); return WillTake(__instance, item); } private static bool WillTake(BeltBagItem bag, GrabbableObject item) { if ((Object)(object)PetCarry.RiderOf(bag) != (Object)null) { return false; } if (bag.objectsInBag != null && bag.objectsInBag.Count >= PetPack.Capacity) { return false; } if (!((Object)(object)item == (Object)null) && !((Object)(object)item.itemProperties == (Object)null)) { return item.itemProperties.twoHanded; } return true; } private static bool HasRoom(BeltBagItem bag) { return WillTake(bag, null); } } [HarmonyPatch] internal static class CarrierUiPatches { private static readonly List<GameObject> Slots = new List<GameObject>(); private static readonly Dictionary<int, Vector2> OriginalPositions = new Dictionary<int, Vector2>(); private static readonly List<Behaviour> SuppressedLayout = new List<Behaviour>(); private static bool _captured; private static Vector2 _cell; private static float _xStep; private static float _yStep; private static Vector2 _centre; private static bool _ours; [HarmonyPostfix] [HarmonyPatch(typeof(BeltBagInventoryUI), "FillSlots")] private static void FillSlots_Postfix(BeltBagInventoryUI __instance, BeltBagItem beltBag) { if (!Plugin.Cfg.Enabled.Value) { return; } try { if (!CarrierContent.IsCarrier((GrabbableObject)(object)beltBag)) { Restore(); } else { Arrange(__instance); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Pet] Could not lay out the carrier's panel: " + ex.Message)); } } [HarmonyPostfix] [HarmonyPatch(typeof(BeltBagInventoryUI), "OnDisable")] private static void OnDisable_Postfix() { Restore(); } private static void Arrange(BeltBagInventoryUI ui) { //IL_0105: 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) GameObject[] inventorySlots = ui.inventorySlots; if (inventorySlots == null || inventorySlots.Length == 0) { return; } Capture(inventorySlots); int num = Mathf.Clamp(PetPack.Capacity, 1, inventorySlots.Length); int num2 = Mathf.Max(1, Mathf.CeilToInt(Mathf.Sqrt((float)num))); float num3 = (float)(Mathf.CeilToInt((float)num / (float)num2) - 1) * _yStep; SuppressLayout(inventorySlots[0].transform.parent); for (int i = 0; i < inventorySlots.Length; i++) { if ((Object)(object)inventorySlots[i] == (Object)null) { continue; } if (i >= num) { inventorySlots[i].SetActive(false); continue; } inventorySlots[i].SetActive(true); int num4 = i / num2; int num5 = i % num2; float num6 = (float)(Mathf.Min(num2, num - num4 * num2) - 1) * _xStep; RectTransform component = inventorySlots[i].GetComponent<RectTransform>(); if (!((Object)(object)component == (Object)null)) { component.anchoredPosition = new Vector2(_centre.x - num6 * 0.5f + (float)num5 * _xStep, _centre.y + num3 * 0.5f - (float)num4 * _yStep); if (((Vector2)(ref _cell)).sqrMagnitude > 0.01f) { component.sizeDelta = _cell; } } } _ours = true; } private static void Capture(GameObject[] slots) { //IL_008c: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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) //IL_0108: 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_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) GameObject[] array = slots; foreach (GameObject val in array) { if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); RectTransform component = val.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null && !OriginalPositions.ContainsKey(instanceID)) { OriginalPositions[instanceID] = component.anchoredPosition; Slots.Add(val); } } } if (_captured) { return; } RectTransform component2 = slots[0].GetComponent<RectTransform>(); if ((Object)(object)component2 == (Object)null) { return; } Rect rect = component2.rect; _cell = ((Rect)(ref rect)).size; Vector2 anchoredPosition = component2.anchoredPosition; float num = anchoredPosition.x; float num2 = anchoredPosition.x; float num3 = anchoredPosition.y; float num4 = anchoredPosition.y; RectTransform val2 = null; RectTransform val3 = null; array = slots; foreach (GameObject val4 in array) { RectTransform val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<RectTransform>() : null); if ((Object)(object)val5 == (Object)null) { continue; } Vector2 anchoredPosition2 = val5.anchoredPosition; if (Mathf.Abs(anchoredPosition2.y - anchoredPosition.y) < 1f) { if ((Object)(object)val2 == (Object)null && anchoredPosition2.x > anchoredPosition.x + 1f) { val2 = val5; } } else if ((Object)(object)val3 == (Object)null) { val3 = val5; } num = Mathf.Min(num, anchoredPosition2.x); num2 = Mathf.Max(num2, anchoredPosition2.x); num3 = Mathf.Min(num3, anchoredPosition2.y); num4 = Mathf.Max(num4, anchoredPosition2.y); } _xStep = (((Object)(object)val2 != (Object)null) ? Mathf.Abs(val2.anchoredPosition.x - anchoredPosition.x) : Mathf.Max(1f, _cell.x * 1.15f)); _yStep = (((Object)(object)val3 != (Object)null) ? Mathf.Abs(val3.anchoredPosition.y - anchoredPosition.y) : Mathf.Max(1f, _cell.y * 1.15f)); _centre = new Vector2((num + num2) * 0.5f, (num3 + num4) * 0.5f); _captured = true; } private static void SuppressLayout(Transform container) { if ((Object)(object)container == (Object)null || SuppressedLayout.Count > 0) { return; } LayoutGroup[] components = ((Component)container).GetComponents<LayoutGroup>(); foreach (LayoutGroup val in components) { if (((Behaviour)val).enabled) { ((Behaviour)val).enabled = false; SuppressedLayout.Add((Behaviour)(object)val); } } ContentSizeFitter[] components2 = ((Component)container).GetComponents<ContentSizeFitter>(); foreach (ContentSizeFitter val2 in components2) { if (((Behaviour)val2).enabled) { ((Behaviour)val2).enabled = false; SuppressedLayout.Add((Behaviour)(object)val2); } } } private static void Restore() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!_ours) { return; } _ours = false; foreach (GameObject slot in Slots) { if (!((Object)(object)slot == (Object)null)) { slot.SetActive(true); RectTransform component = slot.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null && OriginalPositions.TryGetValue(((Object)slot).GetInstanceID(), out var value)) { component.anchoredPosition = value; } } } foreach (Behaviour item in SuppressedLayout) { if ((Object)(object)item != (Object)null) { item.enabled = true; } } SuppressedLayout.Clear(); } internal static void Reset() { Slots.Clear(); OriginalPositions.Clear(); SuppressedLayout.Clear(); _captured = false; _ours = false; } } [HarmonyPatch] internal static class CarrierWornPatches { private static readonly Dictionary<ulong, float> CrouchBlend = new Dictionary<ulong, float>(); private static readonly Dictionary<int, float> RestingYaw = new Dictionary<int, float>(); private static readonly Vector3 Standing = new Vector3(-0.03f, 1.8f, 0.22f); private static readonly Vector3 StandingAngles = new Vector3(0f, 50f, 0f); private static readonly Vector3 Crouched = new Vector3(-0.03f, 0.78f, 0.13f); private static readonly Vector3 CrouchedAngles = new Vector3(30f, 50f, 30f); private const float CrouchBlendSeconds = 0.25f; [HarmonyPostfix] [HarmonyPatch(typeof(BeltBagItem), "LateUpdate")] private static void LateUpdate_Postfix(BeltBagItem __instance) { if (!Plugin.Cfg.Enabled.Value || !CarrierContent.IsCarrier((GrabbableObject)(object)__instance)) { return; } CarrierScale.Apply(__instance); PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy; bool num = PetPack.IsPacked(__instance); Intangible(__instance, (Object)(object)playerHeldBy != (Object)null); if (num) { PetPack.Draw(__instance); PetCarry.DrawRiders(); return; } if ((Object)(object)playerHeldBy != (Object)null) { if (((GrabbableObject)__instance).isPocketed) { PlaceWorn(__instance, playerHeldBy); } else { PlaceHeld(__instance); } } else { PlaceResting(__instance); } PetCarry.DrawRiders(); } private static void PlaceWorn(BeltBagItem bag, PlayerControllerB wearer) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_007f: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) float num = Blend(wearer); Vector3 standing = Standing; Vector3 crouched = Crouched; Vector3 val = Vector3.Lerp(standing, crouched, num); Quaternion val2 = Quaternion.Slerp(Quaternion.Euler(StandingAngles), Quaternion.Euler(CrouchedAngles), num); Transform transform = ((Component)wearer).transform; ((Component)bag).transform.position = transform.position + transform.up * val.y + transform.forward * val.z + transform.right * val.x; ((Component)bag).transform.rotation = transform.rotation * Quaternion.Euler(bag.beltBagTorsoOffsetRotation) * val2; } private static float Blend(PlayerControllerB wearer) { ulong playerClientId = wearer.playerClientId; float num = (wearer.isCrouching ? 1f : 0f); float num2 = Mathf.Max(0f, 0.25f); if (num2 <= 0f) { CrouchBlend[playerClientId] = num; return num; } if (!CrouchBlend.TryGetValue(playerClientId, out var value)) { value = num; } value = Mathf.MoveTowards(value, num, Time.deltaTime / num2); CrouchBlend[playerClientId] = value; return value; } private static void PlaceResting(BeltBagItem bag) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)bag).GetInstanceID(); if (((GrabbableObject)bag).isHeld || ((GrabbableObject)bag).isHeldByEnemy || ((GrabbableObject)bag).isPocketed || (Object)(object)((GrabbableObject)bag).parentObject != (Object)null) { RestingYaw.Remove(instanceID); } else if (((GrabbableObject)bag).reachedFloorTarget) { Vector3 val = RestingRotation(bag); if ((Object)(object)((GrabbableObject)bag).itemProperties != (Object)null) { ((GrabbableObject)bag).itemProperties.restingRotation = val; } if (!RestingYaw.TryGetValue(instanceID, out var value)) { value = ((Component)bag).transform.eulerAngles.y; RestingYaw[instanceID] = value; } ((Component)bag).transform.rotation = Quaternion.Euler(val.x, value, val.z); } } private static Vector3 RestingRotation(BeltBagItem bag) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Vector3 beltBagTorsoOffsetRotation = bag.beltBagTorsoOffsetRotation; return new Vector3(beltBagTorsoOffsetRotation.x, 0f, beltBagTorsoOffsetRotation.z); } private static void Intangible(BeltBagItem bag, bool carried) { Collider[] propColliders = ((GrabbableObject)bag).propColliders; if (propColliders == null) { return; } foreach (Collider val in propColliders) { if (!((Object)(object)val == (Object)null) && !val.isTrigger && val.enabled == carried) { val.enabled = !carried; } } } private static void PlaceHeld(BeltBagItem bag) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Transform parentObject = ((GrabbableObject)bag).parentObject; if (!((Object)(object)parentObject == (Object)null)) { Vector3 zero = Vector3.zero; Vector3 zero2 = Vector3.zero; if (zero != Vector3.zero) { Transform transform = ((Component)bag).transform; transform.position += parentObject.rotation * zero; } if (zero2 != Vector3.zero) { ((Component)bag).transform.Rotate(zero2); } } } private static Vector3 Parse(string raw, Vector3 fallback) { //IL_0008: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(raw)) { return fallback; } string[] array = raw.Split(','); if (array.Length != 3) { return fallback; } float[] array2 = new float[3]; for (int i = 0; i < 3; i++) { if (!float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i])) { return fallback; } } return new Vector3(array2[0], array2[1], array2[2]); } internal static void Reset() { RestingYaw.Clear(); CrouchBlend.Clear(); } } [HarmonyPatch] internal static class CarryPatches { private static readonly Dictionary<int, float> LastRelease = new Dictionary<int, float>(); private const float DoublePressWindow = 0.6f; private const float Reach = 5f; private const int GrabbableMask = 1073742656; private static int _enemyMask; private const float AimCone = 30f; private static int PetMask { get { if (_enemyMask == 0) { int num = LayerMask.NameToLayer("Enemies"); _enemyMask = 1 << ((num >= 0) ? num : 19); } return 0x40000340 | _enemyMask; } } internal static CaveDwellerAI TargetedPet(PlayerControllerB player) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player.gameplayCamera == (Object)null) { return null; } GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; BeltBagItem val = (BeltBagItem)(object)((currentlyHeldObjectServer is BeltBagItem) ? currentlyHeldObjectServer : null); if (val == null || !CarrierContent.IsCarrier((GrabbableObject)(object)val)) { return null; } Transform transform = ((Component)player.gameplayCamera).transform; RaycastHit[] array = Physics.RaycastAll(transform.position, transform.forward, 5f, PetMask, (QueryTriggerInteraction)2); Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); for (int num = 0; num < array.Length; num++) { CaveDwellerAI val2 = PetOn(((RaycastHit)(ref array[num])).collider); if ((Object)(object)val2 != (Object)null) { return val2; } } return NearestPetInFront(player); } private static CaveDwellerAI PetOn(Collider collider) { if ((Object)(object)collider == (Object)null) { return null; } CaveDwellerPhysicsProp component = ((Component)collider).gameObject.GetComponent<CaveDwellerPhysicsProp>(); if ((Object)(object)component != (Object)null) { return component.caveDwellerScript; } EnemyAICollisionDetect component2 = ((Component)collider).GetComponent<EnemyAICollisionDetect>(); if ((Object)(object)component2 != (Object)null) { EnemyAI mainScript = component2.mainScript; CaveDwellerAI val = (CaveDwellerAI)(object)((mainScript is CaveDwellerAI) ? mainScript : null); if (val != null) { return val; } } return ((Component)collider).GetComponentInParent<CaveDwellerAI>(); } private static CaveDwellerAI NearestPetInFront(PlayerControllerB player) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //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_0097: 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_00ad: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)player.gameplayCamera).transform; CaveDwellerAI result = null; float num = 30f; IReadOnlyList<PetMarker> markers = PetMarker.Markers; for (int i = 0; i < markers.Count; i++) { PetMarker petMarker = markers[i]; if ((Object)(object)petMarker == (Object)null || !petMarker.IsTamedPet || (Object)(object)petMarker.Ai == (Object)null || ((EnemyAI)petMarker.Ai).isEnemyDead || petMarker.Owner != player.playerClientId) { continue; } Vector3 val = ((Component)petMarker.Ai).transform.position + Vector3.up - transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > 25f)) { float num2 = Vector3.Angle(transform.forward, val); if (!(num2 > num)) { num = num2; result = petMarker.Ai; } } } return result; } internal static CaveDwellerPhysicsProp TargetedBaby(PlayerControllerB player) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player.gameplayCamera == (Object)null) { return null; } GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; BeltBagItem val = (BeltBagItem)(object)((currentlyHeldObjectServer is BeltBagItem) ? currentlyHeldObjectServer : null); if (val == null || !CarrierContent.IsCarrier((GrabbableObject)(object)val)) { return null; } Transform transform = ((Component)player.gameplayCamera).transform; RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(transform.position, transform.forward, ref val2, 5f, 1073742656, (QueryTriggerInteraction)1)) { return null; } if (!((Component)((RaycastHit)(ref val2)).collider).CompareTag("PhysicsProp")) { return null; } CaveDwellerPhysicsProp component = ((Component)((RaycastHit)(ref val2)).collider).gameObject.GetComponent<CaveDwellerPhysicsProp>(); if (!((Object)(object)component != (Object)null) || !((Object)(object)component.caveDwellerScript != (Object)null)) { return null; } return component; } internal static void Interact(BeltBagItem bag) { PlayerControllerB playerHeldBy = ((GrabbableObject)bag).playerHeldBy; if (!((Object)(object)playerHeldBy == (Object)null)) { CaveDwellerAI val = PetCarry.RiderOf(bag); if ((Object)(object)val != (Object)null) { TryRelease(bag, val); } else { TryTake(bag, playerHeldBy); } } } private static void TryTake(BeltBagItem bag, PlayerControllerB holder) { CaveDwellerAI val = TargetedPet(holder); if ((Object)(object)val == (Object)null) { TryStow(bag, holder); return; } if (PetForm.IsAdult(val)) { if (!PetMarker.IsOwnedBy((EnemyAI)(object)val, holder)) { Plugin.DebugLog("A carrier was offered to a Maneater that is not this player's pet."); } else if (!((Object)(object)PetPack.PackOn(val) != (Object)null)) { PetNetwork.RequestPack(val, bag, attach: true); } return; } CaveDwellerPhysicsProp propScript = val.propScript; if ((Object)(object)propScript == (Object)null || ((GrabbableObject)propScript).isHeld || ((GrabbableObject)propScript).isHeldByEnemy || !((GrabbableObject)propScript).grabbable) { return; } if (bag.objectsInBag != null && bag.objectsInBag.Count > 0) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(Plugin.Cfg.CarrierName.Value, "There is something in it. Empty it first.", false, false, "LC_Tip1"); } } else { PetNetwork.RequestCarry(val, bag, attach: true); } } internal static void Load(BeltBagItem bag) { if ((Object)(object)((GrabbableObject)bag).playerHeldBy != (Object)null) { TryStow(bag, ((GrabbableObject)bag).playerHeldBy); } } private static void TryStow(BeltBagItem bag, PlayerControllerB holder) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (bag.objectsInBag != null && bag.objectsInBag.Count >= PetPack.Capacity) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(Plugin.Cfg.CarrierName.Value, "It is already full.", false, false, "LC_Tip1"); } return; } Camera gameplayCamera = holder.gameplayCamera; RaycastHit val = default(RaycastHit); if ((Object)(object)gameplayCamera == (Object)null || !Physics.Raycast(((Component)gameplayCamera).transform.position, ((Component)gameplayCamera).transform.forward, ref val, 5f, 1073742656, (QueryTriggerInteraction)1) || !((Component)((RaycastHit)(ref val)).collider).CompareTag("PhysicsProp")) { return; } GrabbableObject component = ((Component)((RaycastHit)(ref val)).collider).gameObject.GetComponent<GrabbableObject>(); if ((Object)(object)component == (Object)null || (Object)(object)component == (Object)(object)bag || component.isHeld || component.isHeldByEnemy || !component.grabbable) { return; } if ((Object)(object)component.itemProperties != (Object)null && !component.itemProperties.twoHanded) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(Plugin.Cfg.CarrierName.Value, component.itemProperties.itemName + " is small enough to carry yourself.", false, false, "LC_Tip1"); } } else if (!CarrierContent.IsCarrier(component) && !(component is CaveDwellerPhysicsProp)) { bag.TryAddObjectToBag(component); } } private static void TryRelease(BeltBagItem bag, CaveDwellerAI rider) { int instanceID = ((Object)bag).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (LastRelease.TryGetValue(instanceID, out var value) && realtimeSinceStartup - value <= 0.6f) { LastRelease.Remove(instanceID); PetNetwork.RequestCarry(rider, bag, attach: false); return; } LastRelease[instanceID] = realtimeSinceStartup; if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(Plugin.Cfg.CarrierName.Value, "Press E again to put it down.", false, false, "LC_Tip1"); } } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] private static bool BeginGrabObject_Prefix(PlayerControllerB __instance) { if (!Plugin.Cfg.Enabled.Value) { return true; } return (Object)(object)TargetedBaby(__instance) == (Object)null; } [HarmonyPostfix] [HarmonyPatch(typeof(BeltBagItem), "EquipItem")] private static void EquipItem_Postfix(BeltBagItem __instance) { if (Plugin.Cfg.Enabled.Value && CarrierContent.IsCarrier((GrabbableObject)(object)__instance) && PetPack.IsPacked(__instance)) { PetNetwork.RequestPack(PetPack.WearerOf(__instance), __instance, attach: false); } } [HarmonyPrefix] [HarmonyPatch(typeof(CaveDwellerAI), "StartTransformationAnim")] private static void StartTransformationAnim_Prefix(CaveDwellerAI __instance) { BeltBagItem val = PetCarry.BagOf(__instance); if (PetCarry.IsRiding(__instance)) { PetCarry.Detach(__instance); } else { val = PetCarry.JustLeftBag(__instance); } if ((Object)(object)val == (Object)null || !Plugin.Cfg.Enabled.Value || ((GrabbableObject)val).isHeld || ((GrabbableObject)val).isPocketed) { return; } try { ((GrabbableObject)val).EnableItemMeshes(false); ((MonoBehaviour)Plugin.Instance).StartCoroutine(ShowAgainWhenGrown(__instance, val)); Plugin.DebugLog("A carrier was put out of sight for the length of a transformation."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Pet] Could not hide a carrier for the transformation: " + ex.Message)); } } private static IEnumerator ShowAgainWhenGrown(CaveDwellerAI ai, BeltBagItem bag) { float until = Time.realtimeSinceStartup + 6f; while (Time.realtimeSinceStartup < until) { if ((Object)(object)bag == (Object)null) { yield break; } if (((GrabbableObject)bag).isHeld || ((GrabbableObject)bag).isPocketed || (Object)(object)ai == (Object)null || (PetForm.IsAdult(ai) && !((EnemyAI)ai).inSpecialAnimation)) { break; } yield return null; } if ((Object)(object)bag != (Object)null) { ((GrabbableObject)bag).EnableItemMeshes(true); } } [HarmonyPrefix] [HarmonyPatch(typeof(CaveDwellerAI), "KillEnemy")] private static void KillEnemy_Prefix(CaveDwellerAI __instance) { if (!PetShield.IsProtected(__instance)) { if ((Object)(object)__instance != (Object)null && ((NetworkBehaviour)__instance).IsServer) { PetNetwork.RequestPack(__instance, PetPack.PackOn(__instance), attach: false); } PetPack.DetachFrom(__instance); } } internal static void Reset() { LastRelease.Clear(); } } [HarmonyPatch] internal static class ContentPatch { private static bool _registered; [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(GameNetworkManager), "Start")] private static void BeforeGameNetworkManagerStart() { if (_registered || !Plugin.Cfg.Enabled.Value) { return; } try { WhistleContent.Build(); CarrierContent.Build(); if (WhistleContent.Ready) { Items.RegisterShopItem(WhistleContent.Item, Plugin.Cfg.WhistlePrice.Value); Plugin.Log.LogInfo((object)("[Pet] \"" + WhistleContent.Item.itemName + "\" registered in the shop for " + $"{Plugin.Cfg.WhistlePrice.Value} credits.")); if (CarrierContent.Ready) { Items.RegisterShopItem(CarrierContent.Item, Plugin.Cfg.CarrierPrice.Value); Plugin.Log.LogInfo((object)("[Pet] \"" + CarrierContent.Item.itemName + "\" registered in the shop for " + $"{Plugin.Cfg.CarrierPrice.Value} credits.")); } PetOrderContent.Build(); if (PetOrderContent.Ready) { Items.RegisterShopItem(PetOrderContent.Item, (TerminalNode)null, (TerminalNode)null, PetOrderContent.Info, Plugin.Cfg.PetOrderPrice.Value); Plugin.Log.LogInfo((object)("[Pet] \"Tamed Maneater\" registered in the shop for " + $"{Plugin.Cfg.PetOrderPrice.Value} credits.")); } _registered = true; } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Failed to register the whistle; it will not be purchasable: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Start")] private static void AfterStartOfRoundStart(StartOfRound __instance) { List<Item> list = __instance.allItemsList?.itemsList; if (list != null) { Publish(list, WhistleContent.Item, "whistle"); Publish(list, CarrierContent.Item, "carrier"); } } private static void Publish(List<Item> list, Item item, string what) { if (!((Object)(object)item == (Object)null) && !list.Contains(item)) { list.Add(item); Plugin.DebugLog("Added the " + what + " to StartOfRound.allItemsList so debug tools can spawn it."); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "Start")] private static void AfterGameNetworkManagerStart() { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.NetworkConfig != null && singleton.NetworkConfig.Prefabs != null) { Ensure(singleton, WhistleContent.Prefab, "whistle"); Ensure(singleton, CarrierContent.Prefab, "carrier"); } } private static void Ensure(NetworkManager nm, GameObject prefab, string what) { if ((Object)(object)prefab == (Object)null) { return; } try { if (!nm.NetworkConfig.Prefabs.Contains(prefab)) { nm.AddNetworkPrefab(prefab); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Pet] Could not add the " + what + " network prefab directly: " + ex.Message)); } } } [HarmonyPatch] internal static class CorePatches { [HarmonyPostfix] [HarmonyPatch(typeof(NetworkManager), "Initialize")] private static void AfterNetworkInitialize() { PetNetwork.Register(); } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "SetInstanceValuesBackToDefault")] private static void OnNetworkTearDown() { PetNetwork.Unregister(); PetNetwork.Reset(); PetMarker.Clear(); PetSpawnTickets.Clear(); } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Start")] private static void OnLobbyStart(StartOfRound __instance) { if (((NetworkBehaviour)__instance).IsServer) { PetRules.UseLocal(); PetNetwork.Reset(); } PetKennel.Clear(); TamingTracker.ResetAngerForNewMoon(); } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnClientConnect")] private static void OnClientConnected(StartOfRound __instance, ulong __0) { if (((NetworkBehaviour)__instance).IsServer) { PetNetwork.SendRules(new ulong[1] { __0 }); } } [HarmonyPrefix] [HarmonyPatch(typeof(MenuManager), "Start")] private static void OnMainMenu() { PetMarker.Clear(); PetNetwork.Reset(); PetRules.UseLocal(); WhistlePatches.Reset(); WhistleAudio.Reset(); SteeringPatches.Reset(); ReversedAudio.Clear(); PetHorn.Reset(); Recolor.Clear(); Unprint.Clear(); PetPaint.Reset(); PetDecals.Reset(); SprayPatches.Reset(); StormPatches.Reset(); CarrierSkin.Reset(); PetTag.Reset(); PetPack.Clear(); PetPlay.Clear(); CarrierUiPatches.Reset(); PetFreeze.Clear(); PetKennel.Clear(); PetPersistence.Clear(); PetOrders.Clear(); GordionCompat.Reset(); QuicksandCompat.Clear(); CarrierScale.Clear(); CarrierWornPatches.Reset(); PetCarry.Clear(); CarryPatches.Reset(); PetUnstick.Reset(); PetDiagnostics.Reset(); PetCombat.Reset(); Tint.Reset(); PetSpawnTickets.Clear(); PetShield.Clear(); } [HarmonyPostfix] [HarmonyPriority(800)] [HarmonyPatch(typeof(EnemyAI), "Start")] private static void AfterEnemyStart(EnemyAI __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Cfg.Enabled.Value) { return; } CaveDwellerAI val = (CaveDwellerAI)(object)((__instance is CaveDwellerAI) ? __instance : null); if (val == null) { return; } PetMarker petMarker = PetMarker.Attach(val); PetNetwork.TryApplyBuffered(__instance); if (!PetSpawnTickets.Any || !PetNetwork.IsHost) { return; } bool recallOnSpawn = PetSpawnTickets.RecallOnSpawn; if (PetSpawnTickets.TryConsume(out var owner)) { Color colour = PetSpawnTickets.Colour; int tagSeed = PetSpawnTickets.TagSeed; List<PetDecals.Mark> marks = PetSpawnTickets.Marks; BeltBagItem carrier = PetSpawnTickets.Carrier; TamingTracker.TameDirectly(petMarker, owner); if (colour.a > 0f && petMarker.IsTamedPet) { petMarker.Colour = colour; petMarker.TagSeed = tagSeed; PetNetwork.RequestColour(val, colour, tagSeed); } if (marks != null && marks.Count > 0 && petMarker.IsTamedPet) { petMarker.Marks.Clear(); petMarker.Marks.AddRange(marks); PetNetwork.SendMarks(val, null); PetDecals.Reapply(val); } if ((Object)(object)carrier != (Object)null && petMarker.IsTamedPet) { PetNetwork.RequestCarry(val, carrier, attach: true); Plugin.DebugLog("A rebuilt pet climbed back into the carrier it had been wearing."); } if (recallOnSpawn && petMarker.IsTamedPet) { petMarker.FollowMode = PetFollowMode.Close; } } } [HarmonyPostfix] [HarmonyPatch(typeof(CaveDwellerAI), "Update")] private static void AfterCaveDwellerUpdate(CaveDwellerAI __instance) { if (!Plugin.Cfg.Enabled.Value || !PetNetwork.IsHost) { return; } try { TamingTracker.Tick(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Taming tick failed: {arg}"); } } } [HarmonyPatch] internal static class KennelPatches { [HarmonyPostfix] [HarmonyPatch(typeof(ShipBuildModeManager), "StoreShipObjectClientRpc")] private static void StoreShipObjectClientRpc_Postfix(int unlockableID) { if (Plugin.Cfg.Enabled.Value && PetNetwork.IsHost && PetKennel.IsTheKennel(unlockableID)) { PetKennel.ReleaseAll(); } } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")] private static bool BeginGrabObject_Prefix(PlayerControllerB __instance) { if (!Plugin.Cfg.Enabled.Value || !Plugin.Cfg.KennelEnabled.Value) { return true; } if (!PetKennel.IsWithinReachOf(__instance) || !PetKennel.OwnsAPet(__instance.playerClientId)) { return true; } PetNetwork.SendCommand(PetCommand.SendToKennel); return false; } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger")] private static void SetHoverTip_Postfix(PlayerControllerB __instance) { if (Plugin.Cfg.Enabled.Value && Plugin.Cfg.KennelEnabled.Value && !((Object)(object)__instance.cursorTip == (Object)null) && PetKennel.IsWithinReachOf(__instance) && PetKennel.OwnsAPet(__instance.playerClientId)) { ((TMP_Text)__instance.cursorTip).text = (PetKennel.PetOfIsParked(__instance.playerClientId) ? "Call your pet out : [E]" : "Send your pet here : [E]"); } } [HarmonyPostfix] [HarmonyPatch(typeof(Terminal), "RotateShipDecorSelection")] private static void RotateShipDecorSelection_Postfix(Terminal __instance) { if (Plugin.Cfg.Enabled.Value && PetRules.StockAllFurniture) { WidenStock(__instance); } } internal static void WidenStock(Terminal terminal) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)terminal == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)instance.unlockablesList == (Object)null || terminal.ShipDecorSelection == null) { return; } terminal.ShipDecorSelection.Clear(); List<UnlockableItem> unlockables = instance.unlockablesList.unlockables; for (int i = 0; i < unlockables.Count; i++) { TerminalNode val = unlockables[i]?.shopSelectionNode; if ((Object)(object)val != (Object)null && !terminal.ShipDecorSelection.Contains(val)) { terminal.ShipDecorSelection.Add(val); } } Plugin.DebugLog($"Stocking all {terminal.ShipDecorSelection.Count} pieces of furniture."); } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Update")] private static void Update_Postfix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetKennel.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Kennel tick failed: {arg}"); } try { PetPlay.DrawCarried(); } catch (Exception arg2) { Plugin.Log.LogError((object)$"[Pet] Carrying the ball failed: {arg2}"); } try { PetPlay.ReportLocalDot(); } catch (Exception arg3) { Plugin.Log.LogError((object)$"[Pet] Reporting the laser dot failed: {arg3}"); } } } [HarmonyPatch] internal static class PersistencePatches { private static void WarnIfLeftBehind() { PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val == (Object)null || (Object)(object)HUDManager.Instance == (Object)null) { return; } IReadOnlyList<PetMarker> markers = PetMarker.Markers; for (int i = 0; i < markers.Count; i++) { PetMarker petMarker = markers[i]; if (!((Object)(object)petMarker == (Object)null) && petMarker.IsTamedPet && !((Object)(object)petMarker.Ai == (Object)null) && !((EnemyAI)petMarker.Ai).isEnemyDead && petMarker.Owner == val.playerClientId && !PetPersistence.IsComingWithUs(petMarker.Ai)) { HUDManager.Instance.DisplayTip("Your pet was left behind", "It was not aboard when the ship left. It is gone for good.", true, false, "LC_Tip1"); break; } } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] private static void ShipLeave_Postfix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetShield.RememberWhoIsAboard(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Noting which pets were aboard failed: {arg}"); } } [HarmonyPrefix] [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] private static void DespawnPropsAtEndOfRound_Prefix(RoundManager __instance) { if (!Plugin.Cfg.Enabled.Value || !((NetworkBehaviour)__instance).IsServer) { return; } try { PetItems.CorrectBeforeDespawn(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Correcting item flags at the end of the round failed: {arg}"); } } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(RoundManager), "UnloadSceneObjectsEarly")] private static void UnloadSceneObjectsEarly_Prefix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetPersistence.Save(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Saving pets for the flight failed: {arg}"); } try { WarnIfLeftBehind(); } catch (Exception arg2) { Plugin.Log.LogError((object)$"[Pet] Reporting a lost pet failed: {arg2}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] private static void OnShipLandedMiscEvents_Postfix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetPersistence.Restore(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Bringing pets back from the flight failed: {arg}"); } try { PetShield.Landed(); } catch (Exception arg2) { Plugin.Log.LogError((object)$"[Pet] Clearing the takeoff note failed: {arg2}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")] private static void SaveGameValues_Postfix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetPersistence.WriteToSaveFile(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Saving pets with the ship failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "LoadShipGrabbableItems")] private static void LoadShipGrabbableItems_Postfix() { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetPersistence.ReadFromSaveFile(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Loading the saved pets failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "ResetSavedGameValues")] private static void ResetSavedGameValues_Postfix() { try { PetPersistence.ForgetSaveFile(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Clearing the saved pets failed: {arg}"); } } } [HarmonyPatch] internal static class PlayPatches { private static FieldInfo _execStage; private const int BallNoiseId = 941; [HarmonyPrefix] [HarmonyPatch(typeof(SoccerBallProp), "KickBallServerRpc")] private static void KickBallServerRpc_Prefix(SoccerBallProp __instance, Vector3 dest, int playerWhoKicked, bool setInElevator) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0078: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Cfg.Enabled.Value && ((NetworkBehaviour)__instance).IsServer && Executing((NetworkBehaviour)(object)__instance)) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && instance.allPlayerScripts != null && playerWhoKicked >= 0 && playerWhoKicked < instance.allPlayerScripts.Length) { Transform val = (setInElevator ? instance.elevatorTransform : instance.propsContainer); Vector3 destination = (((Object)(object)val != (Object)null) ? val.TransformPoint(dest) : dest); PetPlay.BallKicked((GrabbableObject)(object)__instance, instance.allPlayerScripts[playerWhoKicked], destination); } } } private static bool Executing(NetworkBehaviour rpc) { if ((object)_execStage == null) { _execStage = AccessTools.Field(typeof(NetworkBehaviour), "__rpc_exec_stage"); } if (_execStage == null) { return true; } object value = _execStage.GetValue(rpc); if (value != null) { return value.ToString() == "Execute"; } return false; } [HarmonyPostfix] [HarmonyPatch(typeof(GrabbableObject), "LateUpdate")] private static void LateUpdate_Postfix(GrabbableObject __instance) { if (!Plugin.Cfg.Enabled.Value) { return; } try { PetPlay.HoldInMouth(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Holding the ball failed: {arg}"); } } [HarmonyPrefix] [HarmonyPatch(typeof(CaveDwellerAI), "DetectNoise")] private static bool DetectNoise_Prefix(CaveDwellerAI __instance, int noiseID) { if (!Plugin.Cfg.Enabled.Value || !Plugin.Cfg.SoccerBallFetch.Value) { return true; } if (noiseID != 941 || !PetMarker.IsTamed((EnemyAI)(object)__instance)) { return true; } return false; } } [HarmonyPatch] internal static class RiderPatches { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerAI), "DetectNoise")] private static bool DetectNoise_Prefix(CaveDwellerAI __instance) { return !PetFreeze.IsFrozen(__instance); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerAI), "SetRolledOverLocalClient")] private static bool SetRolledOverLocalClient_Prefix(CaveDwellerAI __instance) { return !PetFreeze.IsFrozen(__instance); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerAI), "SetCryingLocalClient")] private static bool SetCryingLocalClient_Prefix(CaveDwellerAI __instance, bool setCrying) { if (setCrying) { return !PetFreeze.IsFrozen(__instance); } return true; } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerAI), "Update")] private static bool Update_Prefix(CaveDwellerAI __instance) { return !PetFreeze.IsFrozen(__instance); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerAI), "DoAIInterval")] private static bool DoAIInterval_Prefix(CaveDwellerAI __instance) { return !PetFreeze.IsFrozen(__instance); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(CaveDwellerPhysicsProp), "Update")] private static bool PropUpdate_Prefix(CaveDwellerPhysicsProp __instance) { return !PetFreeze.IsFrozen(__instance); } } [HarmonyPatch] internal static class ShopPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable")] private static bool LoadNewNodeIfAffordable_Prefix(Terminal __instance, TerminalNode node, out int __state) { __state = -1; if (!Plugin.Cfg.Enabled.Value || (Object)(object)node == (Object)null || !PetOrderContent.Ready) { return true; } int num = PetOrderContent.IndexIn(__instance); if (num < 0 || node.buyItemIndex != num) { return true; } string text = PetOrders.WhyNot(__instance); if (text != null) { __instance.LoadNewNode(PetOrderContent.Refusal(text)); Plugin.DebugLog("A Maneater order was refused at the terminal: " + text); return false; } __state = __instance.groupCredits; return true; } [HarmonyPostfix] [HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable")] private static void LoadNewNodeIfAffordable_Postfix(Terminal __instance, int __state) { if (__state >= 0 && __instance.groupCredits < __state) { PetOrders.Placed(); } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemDropship), "OpenShipDoorsOnServer")] private static void OpenShipDoorsOnServer_Prefix(ItemDropship __instance, out int __state) { __state = 0; if (!Plugin.Cfg.Enabled.Value || !__instance.shipLanded || __instance.shipDoorsOpened) { return; } int index = PetOrderContent.IndexIn(__instance.terminalScript); if (index >= 0 && __instance.itemsToDeliver != null) { __state = __instance.itemsToDeliver.RemoveAll((int i) => i == index); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemDropship), "OpenShipDoorsOnServer")] private static void OpenShipDoorsOnServer_Postfix(ItemDropship __instance, int __state) { if (__state > 0) { Plugin.Log.LogInfo((object)$"[Pet] The dropship carried {__state} tamed Maneater order(s)."); ((MonoBehaviour)Plugin.Instance).StartCoroutine(PetOrders.Deliver(__instance, __state)); } } } [HarmonyPatch] internal static class SightPatches { private static bool Blind(EnemyAI ai) { if (Plugin.Cfg.Enabled.Value && PetMarker.AnyTamed) { CaveDwellerAI val = (CaveDwellerAI)(object)((ai is CaveDwellerAI) ? ai : null); if (val != null && PetMarker.IsTamed((EnemyAI)(object)val)) { return !PetSenses.CanSee(val); } } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(EnemyAI), "GetAllPlayersInLineOfSightNonAlloc", new Type[] { typeof(float), typeof(int), typeof(Transform), typeof(float), typeof(int) })] private static bool GetAllPlayersInLineOfSightNonAlloc_Prefix(EnemyAI __instance, ref int __result) { if (!Blind(__instance)) { return true; } __result = 0; return false; } [HarmonyPrefix] [HarmonyPatch(typeof(EnemyAI), "TargetClosestPlayer")] private static bool TargetClosestPlayer_Prefix(EnemyAI __instance, ref bool __result) { if (!Blind(__instance)) { return true; } __instance.targetPlayer = null; __result = false; return false; } } [HarmonyPatch] internal static class SprayPatches { private const float SprayRange = 7f; private static readonly Dictionary<int, Color> CanColours = new Dictionary<int, Color>(); private static float _lastMissReport; [HarmonyPostfix] [HarmonyPatch(typeof(CaveDwellerAI), "StartTransformationAnim")] private static void StartTransformationAnim_Postfix(CaveDwellerAI __instance) { if (Plugin.Cfg.Enabled.Value) { PetForm.RepaintFromMarker(__instance, PetMarker.Get(__instance)); } } [HarmonyPostfix] [HarmonyPatch(typeof(CaveDwellerAI), "Start")] private static void Start_Postfix(CaveDwellerAI __instance) { if (Plugin.Cfg.Enabled.Value && Plugin.Cfg.SprayPaintRecolours.Value) { PetDecals.MakePaintable(__instance); } } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(SprayPaintItem), "AddSprayPaintLocal")] private static bool AddSprayPaintLocal_Prefix(SprayPaintItem __instance, Vector3 sprayPos, Vector3 sprayRot, ref bool __result) { //IL_0025: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Cfg.Enabled.Value || !Plugin.Cfg.SprayPaintRecolours.Value) { return true; } try { RaycastHit[] array = Physics.RaycastAll(sprayPos, sprayRot, 7f, __instance.sprayPaintMask, (QueryTriggerInteraction)2); if (array == null || array.Length == 0) { return true; } Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); RaycastHit val = default(RaycastHit); CaveDwellerAI val2 = null; for (int num = 0; num < array.Length; num++) { CaveDwellerAI val3 = PetDecals.OwnerOf(((RaycastHit)(ref array[num])).collider); if (!((Object)(object)val3 == (Object)null) && PetMarker.IsTamed((EnemyAI)(object)val3)) { val = array[num]; val2 = val3; break; } } if ((Object)(object)val2 == (Object)null) { ReportMiss(array); return true; } if (!Plugin.Cfg.SprayPaintMarks.Value) { PetMarker petMarker = PetMarker.Get(val2); if ((Object)(object)petMarker == (Object)null) { return true; } PetNetwork.RequestColour(val2, CanColour(__instance), petMarker.TagSeed); __instance.previousSprayPosition = ((RaycastHit)(ref val)).point; __result = true; return false; } if (Vector3.Distance(((RaycastHit)(ref val)).point, __instance.previousSprayPosition) < 0.175f) { __result = false; return false; } PetDecals.Paint(val2, sprayPos + sprayRot * (((RaycastHit)(ref val)).distance - 0.1f), sprayRot, (byte)Mathf.Max(0, __instance.sprayCanMatsIndex)); __instance.previousSprayPosition = ((RaycastHit)(ref val)).point; __result = true; return false; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Pet] Spraying a pet failed: " + ex.Message)); return true; } } private static Color CanColour(SprayPaintItem can) { //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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_0067: 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_008a: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(0, can.sprayCanMatsIndex); if (CanColours.TryGetValue(num, out var value)) { return value; } Color val = Color.white; string arg = "nothing; the can's material had no colour property"; Material[] sprayCanMats = can.sprayCanMats; if (sprayCanMats != null && num < sprayCanMats.Length && (Object)(object)sprayCanMats[num] != (Object)null) { Material val2 = sprayCanMats[num]; if (val2.HasProperty("_BaseColor")) { val = val2.GetColor("_BaseColor"); arg = "_BaseColor"; } else if (val2.HasProperty("_Color")) { val = val2.GetColor("_Color"); arg = "_Color"; } else if (val2.HasProperty("_UnlitColor")) { val = val2.GetColor("_UnlitColor"); arg = "_UnlitColor"; } } val.a = 1f; CanColours[num] = val; Plugin.DebugLog($"Spray can colour {num} is {ColorUtility.ToHtmlStringRGB(val)}, read from {arg}."); return val; } internal static void Reset() { CanColours.Clear(); } private static void ReportMiss(RaycastHit[] hits) { if (!(Time.realtimeSinceStartup - _lastMissReport < 1f)) { _lastMissReport = Time.realtimeSinceStartup; StringBuilder stringBuilder = new StringBuilder("Spray hit nothing of ours:"); for (int i = 0; i < hits.Length && i < 6; i++) { Collider collider = ((RaycastHit)(ref hits[i])).collider; stringBuilder.Append($" [{((RaycastHit)(ref hits[i])).distance:0.##}m '{((Object)collider).name}' layer {((Component)collider).gameObject.layer}" + (collider.isTrigger ? " trigger" : "") + "]"); } Plugin.DebugLog(stringBuilder.ToString()); } } } [HarmonyPatch] internal static class SteeringPatches { private readonly struct Errand { public readonly Vector3 Spot; public readonly float ChosenAt; public readonly float Dwell; public Errand(Vector3 spot, float chosenAt, float dwell) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Spot = spot; ChosenAt = chosenAt; Dwell = dwell; } } private static readonly Dictionary<int, Errand> Errands = new Dictionary<int, Errand>(); private const float ErrandTimeout = 8f; private const float Arrived = 1.5f; private const int PathAttempts = 6; [HarmonyPrefix] [HarmonyPatch(typeof(CaveDwellerAI), "DoAIInterval")] private static void BeforeDoAIInterval(CaveDwellerAI __instance) { if (!Plugin.Cfg.Enabled.Value || !PetMarker.AnyTamed || !PetMarker.IsTamed((EnemyAI)(object)__instance)) { return; } try { PetNodes.Sanitise((EnemyAI)(object)__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Checking a pet's nodes failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(CaveDwellerAI), "DoAIInterval")] private static void AfterDoAIInterval(CaveDwellerAI __instance) { try { Steer(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Pet] Steering failed: {arg}"); } } private static void Steer(CaveDwellerAI __instance) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Cfg.Enabled.Value || !PetMarker.AnyTamed || !((NetworkBehaviour)__instance).IsServer || !PetMarker.IsTamed((EnemyAI)(obj