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 GuaranteeApparatus v1.0.1
BepInEx/plugins/GuaranteeApparatus/GuaranteeApparatus.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DunGen; using DunGen.Graph; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 GuaranteeApparatus { internal enum InteriorClassification { Vanilla, KnownCustom, UnknownCustom } internal sealed class ValidationContext { [CompilerGenerated] private sealed class <FindSceneObjects>d__39<T> : IEnumerable<T>, IEnumerable, IEnumerator<T>, IEnumerator, IDisposable where T : notnull, Component { private int <>1__state; private T <>2__current; private int <>l__initialThreadId; private T[] <allObjects>5__2; private int <i>5__3; T IEnumerator<T>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FindSceneObjects>d__39(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <allObjects>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_007c; } <>1__state = -1; <allObjects>5__2 = Resources.FindObjectsOfTypeAll<T>(); <i>5__3 = 0; goto IL_008c; IL_007c: <i>5__3++; goto IL_008c; IL_008c: if (<i>5__3 < <allObjects>5__2.Length) { T val = <allObjects>5__2[<i>5__3]; if (!((Object)(object)val == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { <>2__current = val; <>1__state = 1; return true; } } goto IL_007c; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<T> IEnumerable<T>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <FindSceneObjects>d__39<T>(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<T>)this).GetEnumerator(); } } public RoundManager RoundManager { get; } public StartOfRound StartOfRound { get; } public Dungeon Dungeon { get; } public List<Tile> Tiles { get; } public Bounds DungeonBounds { get; } public LllContext LllContext { get; } public string FlowName { get; } public bool IsFactoryLike { get; } public InteriorClassification Classification { get; set; } public IKnownCustomInteriorRule? KnownRule { get; set; } public Vector3 EntrancePosition { get; set; } public Transform DungeonRootTransform => ((Component)Dungeon).transform; public ValidationContext(RoundManager roundManager, StartOfRound startOfRound, Dungeon dungeon, List<Tile> tiles, LllContext lllContext, string flowName, bool isFactoryLike) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) RoundManager = roundManager; StartOfRound = startOfRound; Dungeon = dungeon; Tiles = tiles; LllContext = lllContext; FlowName = flowName; IsFactoryLike = isFactoryLike; DungeonBounds = BuildDungeonBounds(tiles); } [IteratorStateMachine(typeof(<FindSceneObjects>d__39<>))] public IEnumerable<T> FindSceneObjects<T>() where T : Component { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FindSceneObjects>d__39<T>(-2); } public T[] FindDungeonComponents<T>() where T : Component { return ((Component)DungeonRootTransform).GetComponentsInChildren<T>(true); } public Transform[] FindDungeonTransforms() { return ((Component)DungeonRootTransform).GetComponentsInChildren<Transform>(true); } public bool IsInDungeonHierarchy(Transform? transform) { if ((Object)(object)transform != (Object)null) { if (!((Object)(object)transform == (Object)(object)DungeonRootTransform)) { return transform.IsChildOf(DungeonRootTransform); } return true; } return false; } public bool IsInDungeon(Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Tile tile; Bounds containingBounds; return TryFindContainingTile(position, out tile, out containingBounds); } public bool TryFindContainingTile(Vector3 position, out Tile? tile, out Bounds containingBounds) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < Tiles.Count; i++) { Tile val = Tiles[i]; Bounds val2 = ExpandedBounds(GetTileBounds(val), 1.5f); if (((Bounds)(ref val2)).Contains(position)) { tile = val; containingBounds = val2; return true; } } tile = null; containingBounds = default(Bounds); return false; } public Bounds GetTileBounds(Tile tile) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return tile.Bounds; } public string GetRelativeDungeonPath(Transform transform) { return ApparatusValidator.GetRelativeTransformPath(transform, DungeonRootTransform); } public static Bounds ExpandedBounds(Bounds bounds, float padding) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) return new Bounds(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).size + Vector3.one * (padding * 2f)); } private static Bounds BuildDungeonBounds(List<Tile> tiles) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0032: 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) if (tiles.Count == 0) { return new Bounds(Vector3.zero, Vector3.zero); } Bounds bounds = tiles[0].Bounds; for (int i = 1; i < tiles.Count; i++) { ((Bounds)(ref bounds)).Encapsulate(tiles[i].Bounds); } return bounds; } } internal readonly struct SpawnCandidate { public Vector3 Position { get; } public float DistanceFromEntrance { get; } public float TileVolume { get; } public float NormalizedDepth { get; } public bool PreferredUnusedAnchor { get; } public string StableKey { get; } public string RelativeDungeonPath { get; } public SpawnCandidate(Vector3 position, float distanceFromEntrance, float tileVolume, float normalizedDepth, bool preferredUnusedAnchor, string stableKey, string relativeDungeonPath) { //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) Position = position; DistanceFromEntrance = distanceFromEntrance; TileVolume = tileVolume; NormalizedDepth = normalizedDepth; PreferredUnusedAnchor = preferredUnusedAnchor; StableKey = stableKey; RelativeDungeonPath = relativeDungeonPath; } } internal readonly struct FallbackSpawnResult { public LungProp LungProp { get; } public Vector3 Position { get; } public string PlacementKey { get; } public string RelativeDungeonPath { get; } public FallbackSpawnResult(LungProp lungProp, Vector3 position, string placementKey, string relativeDungeonPath) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) LungProp = lungProp; Position = position; PlacementKey = placementKey; RelativeDungeonPath = relativeDungeonPath; } } internal static class ApparatusValidator { private const string VanillaApparatusItemName = "Apparatus"; private static readonly FieldInfo? NormalizedPathDepthField = AccessTools.Field(typeof(TilePlacementData), "normalizedPathDepth"); public static void Evaluate(RoundManager roundManager) { //IL_026e: Unknown result type (might be due to invalid IL or missing references) ScanNodeSupportService.EnsureMessagingReady(); ScanNodeSupportService.TryResolvePending(roundManager); if (!GuaranteeApparatusPlugin.Enabled.Value) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } if (!TryBuildContext(roundManager, out ValidationContext context)) { GuaranteeApparatusPlugin.Verbose("Skipping apparatus validation because dungeon context was incomplete."); return; } DetectedCustomApparatusCandidate detectedCustomApparatusCandidate = null; bool flag = false; if (context.Classification == InteriorClassification.KnownCustom) { IKnownCustomInteriorRule knownRule = context.KnownRule; if (knownRule != null) { CustomApparatusDetectionResult customApparatusDetectionResult = knownRule.DetectCustomApparatus(context); GuaranteeApparatusPlugin.Verbose($"Known custom detector '{knownRule.DisplayName}' => detected={customApparatusDetectionResult.Detected}, reason='{customApparatusDetectionResult.Reason}'."); if (customApparatusDetectionResult.Detected && (Object)(object)customApparatusDetectionResult.ApparatusRoot != (Object)null) { flag = true; detectedCustomApparatusCandidate = new DetectedCustomApparatusCandidate(InteriorClassification.KnownCustom, knownRule.RuleId, knownRule.DisplayName, customApparatusDetectionResult.ApparatusRoot, context.GetRelativeDungeonPath(customApparatusDetectionResult.ApparatusRoot), string.IsNullOrWhiteSpace(customApparatusDetectionResult.DisplayName) ? ((Object)customApparatusDetectionResult.ApparatusRoot).name : customApparatusDetectionResult.DisplayName, customApparatusDetectionResult.Reason, 0); ScanNodeSupportService.EnsureCustomScanSupport(context, detectedCustomApparatusCandidate); GuaranteeApparatusPlugin.Log.LogInfo((object)"known custom apparatus detected"); if (GuaranteeApparatusPlugin.RespectCustomApparatus.Value && !GuaranteeApparatusPlugin.ForceVanillaFallbackOnKnownCustomInteriors.Value) { return; } } goto IL_019c; } } if (context.Classification == InteriorClassification.UnknownCustom && UnknownCustomApparatusDiscovery.TryDiscover(context, out DetectedCustomApparatusCandidate detectedCandidate)) { flag = true; detectedCustomApparatusCandidate = detectedCandidate; ScanNodeSupportOutcome scanNodeSupportOutcome = ScanNodeSupportService.EnsureCustomScanSupport(context, detectedCustomApparatusCandidate); GuaranteeApparatusPlugin.Log.LogInfo((object)((scanNodeSupportOutcome == ScanNodeSupportOutcome.Generated) ? "unknown custom apparatus detected; scan node generated" : "unknown custom apparatus detected")); if (GuaranteeApparatusPlugin.RespectUnknownCustomApparatus.Value) { return; } } else if (context.Classification == InteriorClassification.UnknownCustom) { GuaranteeApparatusPlugin.Log.LogInfo((object)"unknown custom apparatus NOT found; proceeding to vanilla/fallback evaluation"); } goto IL_019c; IL_019c: if (TryFindValidVanillaApparatus(context, detectedCustomApparatusCandidate, out LungProp existingVanillaApparatus)) { GuaranteeApparatusPlugin.Verbose("Accepted vanilla apparatus at '" + GetStableTransformPath(((Component)existingVanillaApparatus).transform) + "'."); GuaranteeApparatusPlugin.Log.LogInfo((object)"vanilla apparatus detected"); } else { if ((context.Classification != 0 && (context.Classification != InteriorClassification.KnownCustom || !GuaranteeApparatusPlugin.ForceVanillaFallbackOnKnownCustomInteriors.Value) && (context.Classification != InteriorClassification.UnknownCustom || flag || !GuaranteeApparatusPlugin.SpawnFallbackWhenUnknownCustomHasNoDetectedApparatus.Value)) || GuaranteeApparatusPlugin.HasSpawnedFallbackThisRound) { return; } if (TrySpawnFallback(context, out var fallbackSpawnResult)) { GuaranteeApparatusPlugin.HasSpawnedFallbackThisRound = true; GuaranteeApparatusPlugin.Verbose("Spawned fallback vanilla apparatus using placement key '" + fallbackSpawnResult.PlacementKey + "'."); GuaranteeApparatusPlugin.Log.LogInfo((object)"fallback vanilla apparatus spawned"); GuaranteeApparatusPlugin.Log.LogInfo((object)("fallback vanilla apparatus location: position=" + FormatVector3(fallbackSpawnResult.Position) + ", dungeonPath='" + fallbackSpawnResult.RelativeDungeonPath + "'")); switch (ScanNodeSupportService.EnsureFallbackScanSupport(fallbackSpawnResult.LungProp, fallbackSpawnResult.RelativeDungeonPath)) { case ScanNodeSupportOutcome.Generated: GuaranteeApparatusPlugin.Log.LogInfo((object)"fallback vanilla apparatus scan node generated"); break; case ScanNodeSupportOutcome.AlreadyPresent: GuaranteeApparatusPlugin.Log.LogInfo((object)"fallback vanilla apparatus already had valid scan support"); break; default: GuaranteeApparatusPlugin.Log.LogWarning((object)"fallback vanilla apparatus scan support could not be ensured"); break; } } else { GuaranteeApparatusPlugin.Log.LogInfo((object)"skipped because no safe fallback point existed"); } } } internal static string GetRelativeTransformPath(Transform transform, Transform rootTransform) { if ((Object)(object)transform == (Object)(object)rootTransform) { return string.Empty; } Stack<string> stack = new Stack<string>(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)rootTransform) { stack.Push(((Object)val).name); val = val.parent; } if (!((Object)(object)val == (Object)(object)rootTransform)) { return GetStableTransformPath(transform); } return string.Join("/", stack); } private static bool TryBuildContext(RoundManager roundManager, out ValidationContext context) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) context = null; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)roundManager.dungeonGenerator == (Object)null || roundManager.dungeonGenerator.Generator == null) { return false; } Dungeon currentDungeon = roundManager.dungeonGenerator.Generator.CurrentDungeon; if ((Object)(object)currentDungeon == (Object)null) { return false; } List<Tile> list = new List<Tile>(); foreach (Tile allTile in currentDungeon.AllTiles) { if ((Object)(object)allTile != (Object)null) { list.Add(allTile); } } if (list.Count == 0) { return false; } LllContext lllContext = LllReflection.Capture(); string flowName = GetFlowName(roundManager, lllContext); bool isFactoryLike = IsFactoryLikeFlow(flowName); context = new ValidationContext(roundManager, instance, currentDungeon, list, lllContext, flowName, isFactoryLike); context.KnownRule = KnownCustomInteriorRules.Match(context); context.Classification = ((context.KnownRule != null) ? InteriorClassification.KnownCustom : ((lllContext.IsLoaded && lllContext.CurrentExtendedDungeonIsCustom) ? InteriorClassification.UnknownCustom : InteriorClassification.Vanilla)); context.EntrancePosition = FindEntrancePosition(context); GuaranteeApparatusPlugin.Verbose($"Built context: flow='{context.FlowName}', factoryLike={context.IsFactoryLike}, classification={context.Classification}, " + $"tiles={context.Tiles.Count}, doors={context.Dungeon.Doors.Count}, connections={context.Dungeon.Connections.Count}."); return true; } private static string GetFlowName(RoundManager roundManager, LllContext lllContext) { if (!string.IsNullOrWhiteSpace(lllContext.DungeonName)) { return lllContext.DungeonName; } IndoorMapType[] dungeonFlowTypes = roundManager.dungeonFlowTypes; if (dungeonFlowTypes != null && roundManager.currentDungeonType >= 0 && roundManager.currentDungeonType < dungeonFlowTypes.Length) { IndoorMapType val = dungeonFlowTypes[roundManager.currentDungeonType]; if (val != null && (Object)(object)val.dungeonFlow != (Object)null && !string.IsNullOrWhiteSpace(((Object)val.dungeonFlow).name)) { return ((Object)val.dungeonFlow).name; } } return "unknown"; } private static bool IsFactoryLikeFlow(string? flowName) { if (string.IsNullOrWhiteSpace(flowName)) { return false; } string[] array = GuaranteeApparatusPlugin.FactoryFlowPatterns.Value.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && flowName.Contains(text, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static Vector3 FindEntrancePosition(ValidationContext context) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) List<(int, string, Vector3)> list = new List<(int, string, Vector3)>(); foreach (EntranceTeleport item in context.FindSceneObjects<EntranceTeleport>()) { if (!((Object)(object)item == (Object)null) && item.isEntranceToBuilding) { Transform val = null; if ((Object)(object)item.entrancePoint != (Object)null && context.IsInDungeonHierarchy(item.entrancePoint)) { val = item.entrancePoint; } else if (context.IsInDungeonHierarchy(((Component)item).transform)) { val = ((Component)item).transform; } else if ((Object)(object)item.exitPoint != (Object)null && context.IsInDungeonHierarchy(item.exitPoint)) { val = item.exitPoint; } if (!((Object)(object)val == (Object)null)) { list.Add((item.entranceId, context.GetRelativeDungeonPath(val), val.position)); } } } if (list.Count > 0) { list.Sort(delegate((int EntranceId, string StablePath, Vector3 Position) left, (int EntranceId, string StablePath, Vector3 Position) right) { int num = left.EntranceId.CompareTo(right.EntranceId); return (num == 0) ? string.Compare(left.StablePath, right.StablePath, StringComparison.Ordinal) : num; }); return list[0].Item3; } Tile val2 = null; string strB = string.Empty; for (int i = 0; i < context.Tiles.Count; i++) { Tile val3 = context.Tiles[i]; string relativeDungeonPath = context.GetRelativeDungeonPath(((Component)val3).transform); if ((Object)(object)val2 == (Object)null) { val2 = val3; strB = relativeDungeonPath; continue; } float normalizedDepth = GetNormalizedDepth(val3); float normalizedDepth2 = GetNormalizedDepth(val2); if (normalizedDepth < normalizedDepth2 || (Math.Abs(normalizedDepth - normalizedDepth2) < 0.0001f && string.Compare(relativeDungeonPath, strB, StringComparison.Ordinal) < 0)) { val2 = val3; strB = relativeDungeonPath; } } if (!((Object)(object)val2 != (Object)null)) { return context.DungeonRootTransform.position; } Bounds bounds = val2.Bounds; return ((Bounds)(ref bounds)).center; } private static bool TryFindValidVanillaApparatus(ValidationContext context, DetectedCustomApparatusCandidate? detectedCustomCandidate, out LungProp? existingVanillaApparatus) { List<LungProp> list = new List<LungProp>(); foreach (LungProp item in context.FindSceneObjects<LungProp>()) { list.Add(item); } list.Sort((LungProp left, LungProp right) => string.Compare(GetStableTransformPath(((Component)left).transform), GetStableTransformPath(((Component)right).transform), StringComparison.Ordinal)); for (int i = 0; i < list.Count; i++) { LungProp val = list[i]; if (IsValidVanillaLungProp(context, val, detectedCustomCandidate)) { existingVanillaApparatus = val; return true; } } existingVanillaApparatus = null; return false; } private static bool IsValidVanillaLungProp(ValidationContext context, LungProp? lungProp, DetectedCustomApparatusCandidate? detectedCustomCandidate) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)lungProp == (Object)null)) { Scene scene = ((Component)lungProp).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ((Component)lungProp).gameObject.activeInHierarchy && ((Behaviour)lungProp).isActiveAndEnabled && !((GrabbableObject)lungProp).deactivated) { if ((Object)(object)lungProp.roundManager == (Object)null || lungProp.roundManager != context.RoundManager) { return false; } if (!IsVanillaApparatusItem(((GrabbableObject)lungProp).itemProperties)) { return false; } if (context.KnownRule != null && context.KnownRule.IsCustomControlledLungProp(context, lungProp)) { return false; } if (detectedCustomCandidate != null && detectedCustomCandidate.ControlsTransform(((Component)lungProp).transform)) { return false; } Bounds dungeonBounds; if (!context.IsInDungeonHierarchy(((Component)lungProp).transform) && !context.IsInDungeon(((Component)lungProp).transform.position)) { dungeonBounds = context.DungeonBounds; if (!((Bounds)(ref dungeonBounds)).Contains(((Component)lungProp).transform.position)) { return false; } } EvaluateReachability(context, ((Component)lungProp).transform.position, out var available, out var reachable); if (available && !reachable) { return false; } if (!available) { dungeonBounds = context.DungeonBounds; if (!((Bounds)(ref dungeonBounds)).Contains(((Component)lungProp).transform.position) || !((Component)lungProp).gameObject.activeInHierarchy) { return false; } } return true; } } return false; } private static bool IsVanillaApparatusItem(Item? item) { if ((Object)(object)item != (Object)null && string.Equals(item.itemName, "Apparatus", StringComparison.OrdinalIgnoreCase) && (Object)(object)item.spawnPrefab != (Object)null && (Object)(object)item.spawnPrefab.GetComponent<LungProp>() != (Object)null) { return LllReflection.IsVanillaItem(item); } return false; } private static bool TrySpawnFallback(ValidationContext context, out FallbackSpawnResult fallbackSpawnResult) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_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) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) fallbackSpawnResult = default(FallbackSpawnResult); if (!ResolveVanillaApparatusItem(context.StartOfRound, out Item apparatusItem) || (Object)(object)apparatusItem?.spawnPrefab == (Object)null) { GuaranteeApparatusPlugin.Verbose("Could not resolve the real vanilla Apparatus item."); return false; } if (!TryFindFallbackPosition(context, out Vector3 position, out string stableKey, out string relativeDungeonPath)) { return false; } GameObject mapPropsContainer = context.RoundManager.mapPropsContainer; Transform val = ((mapPropsContainer != null) ? mapPropsContainer.transform : null) ?? context.RoundManager.spawnedScrapContainer ?? ((Component)context.RoundManager).transform; Vector3 val2 = position; Quaternion val3 = Quaternion.identity; bool flag = false; Transform val4 = null; if (VanillaApparatusSetpieceService.TryCreateServerSetpiece(context.RoundManager, val, position, out var result)) { flag = true; val4 = result.DockAnchor; val2 = result.DockPosition; val3 = result.DockRotation; GuaranteeApparatusPlugin.Log.LogInfo((object)("fallback vanilla apparatus machine spawned: sourceFlow='" + result.SourceFlowName + "', tile='" + result.TilePrefabName + "', machineRoot='" + result.MachineRootPath + "', dockPosition=" + FormatVector3(val2))); } else { GuaranteeApparatusPlugin.Log.LogWarning((object)"fallback vanilla apparatus machine could not be resolved; using loose vanilla fallback"); } GameObject val5 = Object.Instantiate<GameObject>(apparatusItem.spawnPrefab, val2, val3, val); if ((Object)(object)val5 == (Object)null) { return false; } LungProp component = val5.GetComponent<LungProp>(); NetworkObject component2 = val5.GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { Object.Destroy((Object)(object)val5); return false; } int num = CalculateScrapValue(apparatusItem, context.StartOfRound.randomMapSeed, stableKey); ((GrabbableObject)component).itemProperties = apparatusItem; component.roundManager = context.RoundManager; ((GrabbableObject)component).isInFactory = true; ((GrabbableObject)component).isInElevator = false; ((GrabbableObject)component).isInShipRoom = false; component.isLungDocked = flag; component.isLungDockedInElevator = false; component.isLungPowered = flag; ((GrabbableObject)component).deactivated = false; ((GrabbableObject)component).targetFloorPosition = val2; ((GrabbableObject)component).startFallingPosition = val2 + Vector3.up * (flag ? 0.1f : 2f); ((GrabbableObject)component).scrapValue = num; ((GrabbableObject)component).SetScrapValue(num); if (!component2.IsSpawned) { component2.Spawn(false); } if (flag && (Object)(object)val4 != (Object)null) { DockedFallbackApparatusController.Attach(component, val4, result.DockVisualRoot); ((GrabbableObject)component).parentObject = val4; ((Component)component).transform.SetPositionAndRotation(val4.position, val4.rotation); if ((Object)(object)((GrabbableObject)component).propBody != (Object)null) { ((GrabbableObject)component).propBody.isKinematic = true; ((GrabbableObject)component).propBody.useGravity = false; ((GrabbableObject)component).propBody.velocity = Vector3.zero; ((GrabbableObject)component).propBody.angularVelocity = Vector3.zero; } } else { ((GrabbableObject)component).FallToGround(true, true, val2); } FallbackApparatusFacilityMeltdownBridge.Attach(component); RoundManager roundManager = context.RoundManager; roundManager.totalScrapValueInLevel += (float)num; fallbackSpawnResult = new FallbackSpawnResult(component, val2, stableKey, relativeDungeonPath); return true; } private static bool ResolveVanillaApparatusItem(StartOfRound startOfRound, out Item? apparatusItem) { apparatusItem = null; if ((Object)(object)startOfRound.allItemsList == (Object)null || startOfRound.allItemsList.itemsList == null) { return false; } List<Item> itemsList = startOfRound.allItemsList.itemsList; for (int i = 0; i < itemsList.Count; i++) { Item val = itemsList[i]; if (IsVanillaApparatusItem(val)) { apparatusItem = val; return true; } } return false; } private static int CalculateScrapValue(Item apparatusItem, int mapSeed, string stableKey) { int num = Math.Max(0, apparatusItem.minValue); int num2 = Math.Max(num, apparatusItem.maxValue); if (num2 <= num) { return num; } return new Random(mapSeed ^ GetStableHash(stableKey) ^ 0x5F3759DF).Next(num, num2 + 1); } private static bool TryFindFallbackPosition(ValidationContext context, out Vector3 position, out string stableKey, out string relativeDungeonPath) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) List<SpawnCandidate> list = new List<SpawnCandidate>(); BuildAnchorCandidates(context, list); BuildTileCandidates(context, list); SortCandidates(list); for (int i = 0; i < list.Count; i++) { SpawnCandidate spawnCandidate = list[i]; if (TryValidateSpawnPosition(context, spawnCandidate.Position, out var validatedPosition)) { position = validatedPosition; stableKey = spawnCandidate.StableKey; relativeDungeonPath = spawnCandidate.RelativeDungeonPath; return true; } } position = default(Vector3); stableKey = string.Empty; relativeDungeonPath = string.Empty; return false; } private static void BuildAnchorCandidates(ValidationContext context, List<SpawnCandidate> candidates) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) ValidationContext context2 = context; RandomScrapSpawn[] array = context2.FindDungeonComponents<RandomScrapSpawn>(); Array.Sort(array, (RandomScrapSpawn left, RandomScrapSpawn right) => string.Compare(context2.GetRelativeDungeonPath(((Component)left).transform), context2.GetRelativeDungeonPath(((Component)right).transform), StringComparison.Ordinal)); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (RandomScrapSpawn val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !context2.IsInDungeonHierarchy(((Component)val).transform)) { continue; } Vector3 position = ((Component)val).transform.position; if (context2.TryFindContainingTile(position, out Tile tile, out Bounds containingBounds) && !((Object)(object)tile == (Object)null)) { string relativeDungeonPath = context2.GetRelativeDungeonPath(((Component)val).transform); string text = "anchor:" + relativeDungeonPath; if (hashSet.Add(text)) { candidates.Add(new SpawnCandidate(position, Vector3.Distance(context2.EntrancePosition, position), ((Bounds)(ref containingBounds)).size.x * ((Bounds)(ref containingBounds)).size.y * ((Bounds)(ref containingBounds)).size.z, GetNormalizedDepth(tile), !val.spawnUsed, text, relativeDungeonPath)); } } } } private static void BuildTileCandidates(ValidationContext context, List<SpawnCandidate> candidates) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) List<(Tile, string)> list = new List<(Tile, string)>(context.Tiles.Count); for (int i = 0; i < context.Tiles.Count; i++) { Tile val = context.Tiles[i]; list.Add((val, context.GetRelativeDungeonPath(((Component)val).transform))); } list.Sort(((Tile Tile, string Path) left, (Tile Tile, string Path) right) => string.Compare(left.Path, right.Path, StringComparison.Ordinal)); for (int j = 0; j < list.Count; j++) { Tile item = list[j].Item1; Bounds tileBounds = context.GetTileBounds(item); float num = Math.Max(1.5f, Math.Min(((Bounds)(ref tileBounds)).extents.x, ((Bounds)(ref tileBounds)).extents.z) * 0.75f); int seed = context.StartOfRound.randomMapSeed ^ GetStableHash("tile:" + list[j].Item2); Vector3 val2 = context.RoundManager.GetRandomNavMeshPositionInBoxPredictable(((Bounds)(ref tileBounds)).center, num, context.RoundManager.navHit, new Random(seed), context.RoundManager.collisionsMask, Math.Max(2f, ((Bounds)(ref tileBounds)).extents.y + 2f)); if (val2 == Vector3.zero) { val2 = ((Bounds)(ref tileBounds)).center; } candidates.Add(new SpawnCandidate(val2, Vector3.Distance(context.EntrancePosition, val2), ((Bounds)(ref tileBounds)).size.x * ((Bounds)(ref tileBounds)).size.y * ((Bounds)(ref tileBounds)).size.z, GetNormalizedDepth(item), preferredUnusedAnchor: false, "tile:" + list[j].Item2, list[j].Item2)); } } private static void SortCandidates(List<SpawnCandidate> candidates) { candidates.Sort(delegate(SpawnCandidate left, SpawnCandidate right) { int num = right.PreferredUnusedAnchor.CompareTo(left.PreferredUnusedAnchor); if (num != 0) { return num; } int num2 = right.DistanceFromEntrance.CompareTo(left.DistanceFromEntrance); if (num2 != 0) { return num2; } int num3 = right.NormalizedDepth.CompareTo(left.NormalizedDepth); if (num3 != 0) { return num3; } int num4 = right.TileVolume.CompareTo(left.TileVolume); return (num4 != 0) ? num4 : string.Compare(left.StableKey, right.StableKey, StringComparison.Ordinal); }); } private static bool TryValidateSpawnPosition(ValidationContext context, Vector3 candidatePosition, out Vector3 validatedPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) validatedPosition = candidatePosition; Vector3 val = candidatePosition; int collidersAndRoomMask = context.StartOfRound.collidersAndRoomMask; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(candidatePosition + Vector3.up * 6f, Vector3.down, ref val2, 16f, collidersAndRoomMask)) { val = ((RaycastHit)(ref val2)).point + Vector3.up * 0.15f; } if (!context.TryFindContainingTile(val, out Tile _, out Bounds containingBounds)) { Bounds dungeonBounds = context.DungeonBounds; if (!((Bounds)(ref dungeonBounds)).Contains(val)) { return false; } containingBounds = ValidationContext.ExpandedBounds(context.DungeonBounds, 1f); } if (Vector3.Distance(context.EntrancePosition, val) < GuaranteeApparatusPlugin.MinimumEntranceDistance.Value) { return false; } EvaluateReachability(context, val, out var available, out var reachable); if (available && !reachable) { return false; } if (!available && !((Bounds)(ref containingBounds)).Contains(val)) { return false; } validatedPosition = val; return true; } private static void EvaluateReachability(ValidationContext context, Vector3 position, out bool available, out bool reachable) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 available = false; reachable = false; NavMeshHit val = default(NavMeshHit); NavMeshHit val2 = default(NavMeshHit); if (NavMesh.SamplePosition(position, ref val, 2f, -1) && NavMesh.SamplePosition(context.EntrancePosition, ref val2, 3f, -1)) { available = true; NavMeshPath val3 = new NavMeshPath(); reachable = NavMesh.CalculatePath(((NavMeshHit)(ref val2)).position, ((NavMeshHit)(ref val)).position, -1, val3) && (int)val3.status == 0; } } private static float GetNormalizedDepth(Tile tile) { object placement = tile.Placement; object obj = NormalizedPathDepthField?.GetValue(placement); if (obj is float) { return (float)obj; } return 0f; } private static string GetStableTransformPath(Transform transform) { Stack<string> stack = new Stack<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { stack.Push(((Object)val).name); val = val.parent; } return string.Join("/", stack); } private static int GetStableHash(string value) { int num = -2128831035; for (int i = 0; i < value.Length; i++) { num ^= value[i]; num *= 16777619; } return num; } private static string FormatVector3(Vector3 vector) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return $"({vector.x:F2}, {vector.y:F2}, {vector.z:F2})"; } } internal readonly struct CustomApparatusDetectionResult { public bool Detected { get; } public string Reason { get; } public Transform? ApparatusRoot { get; } public string DisplayName { get; } public CustomApparatusDetectionResult(bool detected, string reason, Transform? apparatusRoot = null, string? displayName = null) { Detected = detected; Reason = reason; ApparatusRoot = apparatusRoot; DisplayName = displayName ?? ((apparatusRoot != null) ? ((Object)apparatusRoot).name : null) ?? string.Empty; } } internal interface IKnownCustomInteriorRule { string RuleId { get; } string DisplayName { get; } bool Matches(ValidationContext context); CustomApparatusDetectionResult DetectCustomApparatus(ValidationContext context); bool IsCustomControlledLungProp(ValidationContext context, LungProp lungProp); } internal static class KnownCustomInteriorRules { private static readonly IKnownCustomInteriorRule[] Rules = new IKnownCustomInteriorRule[1] { new LcOfficeRule() }; public static IKnownCustomInteriorRule? Match(ValidationContext context) { IKnownCustomInteriorRule[] rules = Rules; foreach (IKnownCustomInteriorRule knownCustomInteriorRule in rules) { if (knownCustomInteriorRule.Matches(context)) { return knownCustomInteriorRule; } } return null; } } internal sealed class LcOfficeRule : IKnownCustomInteriorRule { private const string PluginGuid = "Piggy.LCOffice"; private const string OfficeModName = "LC Office"; private const string OfficeElevatorTypeName = "LCOffice.Components.ElevatorSystem"; private const string OfficeInsertedFieldName = "inserted"; private const string OfficeSystemPropertyName = "System"; private const string OfficeLungParentFieldName = "lungParent"; private static readonly Type? ElevatorSystemType = AccessTools.TypeByName("LCOffice.Components.ElevatorSystem"); private static readonly PropertyInfo? ElevatorSystemProperty = AccessTools.Property(ElevatorSystemType, "System"); private static readonly FieldInfo? ElevatorInsertedField = AccessTools.Field(ElevatorSystemType, "inserted"); private static readonly FieldInfo? ElevatorLungParentField = AccessTools.Field(ElevatorSystemType, "lungParent"); public string RuleId => "lc_office"; public string DisplayName => "LC_Office"; public bool Matches(ValidationContext context) { if (!Chainloader.PluginInfos.ContainsKey("Piggy.LCOffice")) { return false; } if (context.LllContext.IsLoaded && (MatchesOfficeString(context.LllContext.DungeonModName) || MatchesOfficeString(context.LllContext.DungeonUniqueId) || MatchesOfficeString(context.LllContext.DungeonName))) { return true; } if (TryFindOfficeItemRoot(context, out Transform officeRoot)) { return (Object)(object)officeRoot != (Object)null; } if (TryGetElevatorSystemComponent(out Component elevatorSystem) && (Object)(object)elevatorSystem != (Object)null) { return context.IsInDungeonHierarchy(elevatorSystem.transform); } return false; } public CustomApparatusDetectionResult DetectCustomApparatus(ValidationContext context) { if (TryGetElevatorSystemComponent(out Component elevatorSystem) && (Object)(object)elevatorSystem != (Object)null && context.IsInDungeonHierarchy(elevatorSystem.transform)) { return new CustomApparatusDetectionResult(detected: true, "active LC_Office elevator system marker found", elevatorSystem.transform, ((Object)elevatorSystem).name); } if (TryFindOfficeItemRoot(context, out Transform officeRoot) && (Object)(object)officeRoot != (Object)null) { return new CustomApparatusDetectionResult(detected: true, "LC_Office apparatus item found in dungeon", officeRoot, ((Object)officeRoot).name); } return new CustomApparatusDetectionResult(detected: false, "no LC_Office custom apparatus markers found"); } public bool IsCustomControlledLungProp(ValidationContext context, LungProp lungProp) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (IsOfficeApparatusItem(((GrabbableObject)lungProp).itemProperties)) { return true; } if (!TryGetElevatorSystemComponent(out Component elevatorSystem)) { return false; } object? obj = ElevatorInsertedField?.GetValue(elevatorSystem); LungProp val = (LungProp)((obj is LungProp) ? obj : null); if (val != null && val == lungProp) { return true; } object? obj2 = ElevatorLungParentField?.GetValue(elevatorSystem); Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if (val2 != null) { if (((Component)lungProp).transform.IsChildOf(val2)) { return true; } if (Vector3.Distance(((Component)lungProp).transform.position, val2.position) <= 1.5f) { return true; } } return false; } private static bool TryGetElevatorSystemComponent(out Component? elevatorSystem) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) elevatorSystem = null; object? obj = ElevatorSystemProperty?.GetValue(null); Component val = (Component)((obj is Component) ? obj : null); if (val == null) { return false; } Scene scene = val.gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { return false; } elevatorSystem = val; return true; } private static bool TryFindOfficeItemRoot(ValidationContext context, out Transform? officeRoot) { LungProp[] array = context.FindDungeonComponents<LungProp>(); foreach (LungProp val in array) { if (IsOfficeApparatusItem(((GrabbableObject)val).itemProperties)) { officeRoot = ((Component)val).transform; return true; } } GrabbableObject[] array2 = context.FindDungeonComponents<GrabbableObject>(); foreach (GrabbableObject val2 in array2) { if (IsOfficeApparatusItem(val2.itemProperties)) { officeRoot = ((Component)val2).transform; return true; } } officeRoot = null; return false; } private static bool IsOfficeApparatusItem(Item? item) { if ((Object)(object)item == (Object)null) { return false; } string text = item.itemName ?? string.Empty; string text2 = ((Object)item).name ?? string.Empty; if (!text.Contains("apparatus", StringComparison.OrdinalIgnoreCase) && !text2.Contains("apparatus", StringComparison.OrdinalIgnoreCase)) { return false; } if (text.Contains("office", StringComparison.OrdinalIgnoreCase) || text2.Contains("office", StringComparison.OrdinalIgnoreCase) || text2.Contains("upturned", StringComparison.OrdinalIgnoreCase)) { return true; } if (LllReflection.TryGetExtendedItemMetadata(item, out string modName, out string uniqueId)) { if (!MatchesOfficeString(modName)) { return MatchesOfficeString(uniqueId); } return true; } return false; } private static bool MatchesOfficeString(string? value) { if (string.IsNullOrWhiteSpace(value)) { return false; } if (!value.Contains("LC Office", StringComparison.OrdinalIgnoreCase)) { return value.Contains("office", StringComparison.OrdinalIgnoreCase); } return true; } } internal sealed class DockedFallbackApparatusController : MonoBehaviour { private LungProp? _lungProp; private Transform? _dockAnchor; private Transform? _dockVisualRoot; private bool _released; public static void Attach(LungProp lungProp, Transform dockAnchor, Transform dockVisualRoot) { DockedFallbackApparatusController obj = ((Component)lungProp).GetComponent<DockedFallbackApparatusController>() ?? ((Component)lungProp).gameObject.AddComponent<DockedFallbackApparatusController>(); obj._lungProp = lungProp; obj._dockAnchor = dockAnchor; obj._dockVisualRoot = dockVisualRoot; obj.LockToDock(); } private void LateUpdate() { if ((Object)(object)_lungProp == (Object)null || (Object)(object)_dockAnchor == (Object)null) { Object.Destroy((Object)(object)this); } else if (ShouldRelease()) { ReleaseDock(); } else { LockToDock(); } } private bool ShouldRelease() { if ((Object)(object)_lungProp == (Object)null || (Object)(object)_dockAnchor == (Object)null) { return true; } if (!_lungProp.isLungDocked) { return true; } if (((GrabbableObject)_lungProp).isHeld || (Object)(object)((GrabbableObject)_lungProp).playerHeldBy != (Object)null) { return true; } if ((Object)(object)((GrabbableObject)_lungProp).parentObject != (Object)null) { return (Object)(object)((GrabbableObject)_lungProp).parentObject != (Object)(object)_dockAnchor; } return false; } private void LockToDock() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_lungProp == (Object)null) && !((Object)(object)_dockAnchor == (Object)null)) { ((GrabbableObject)_lungProp).parentObject = _dockAnchor; ((Component)_lungProp).transform.SetPositionAndRotation(_dockAnchor.position, _dockAnchor.rotation); if ((Object)(object)((GrabbableObject)_lungProp).propBody != (Object)null) { ((GrabbableObject)_lungProp).propBody.isKinematic = true; ((GrabbableObject)_lungProp).propBody.useGravity = false; ((GrabbableObject)_lungProp).propBody.velocity = Vector3.zero; ((GrabbableObject)_lungProp).propBody.angularVelocity = Vector3.zero; } ((GrabbableObject)_lungProp).EnableItemMeshes(false); if ((Object)(object)_dockVisualRoot != (Object)null) { ((Component)_dockVisualRoot).gameObject.SetActive(true); } } } private void ReleaseDock() { //IL_0075: 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) if ((Object)(object)_lungProp != (Object)null) { if ((Object)(object)((GrabbableObject)_lungProp).parentObject == (Object)(object)_dockAnchor) { ((GrabbableObject)_lungProp).parentObject = null; } if ((Object)(object)((GrabbableObject)_lungProp).propBody != (Object)null) { ((GrabbableObject)_lungProp).propBody.isKinematic = false; ((GrabbableObject)_lungProp).propBody.useGravity = true; ((GrabbableObject)_lungProp).propBody.velocity = Vector3.zero; ((GrabbableObject)_lungProp).propBody.angularVelocity = Vector3.zero; } ((GrabbableObject)_lungProp).EnableItemMeshes(true); } if (!_released && (Object)(object)_dockVisualRoot != (Object)null) { ((Component)_dockVisualRoot).gameObject.SetActive(false); } _released = true; Object.Destroy((Object)(object)this); } } internal sealed class FallbackApparatusFacilityMeltdownBridge : MonoBehaviour { private LungProp? _lungProp; private bool _checked; public static void Attach(LungProp lungProp) { if (FacilityMeltdownCompatibility.IsLoaded) { (((Component)lungProp).GetComponent<FallbackApparatusFacilityMeltdownBridge>() ?? ((Component)lungProp).gameObject.AddComponent<FallbackApparatusFacilityMeltdownBridge>())._lungProp = lungProp; } } private void Update() { if (!_checked) { if ((Object)(object)_lungProp == (Object)null) { Object.Destroy((Object)(object)this); } else if (((GrabbableObject)_lungProp).hasBeenHeld || ((GrabbableObject)_lungProp).isHeld || !((Object)(object)((GrabbableObject)_lungProp).playerHeldBy == (Object)null)) { _checked = true; FacilityMeltdownCompatibility.TryBeginMeltdownForFallback(_lungProp); Object.Destroy((Object)(object)this); } } } } internal static class FacilityMeltdownCompatibility { private const string FacilityMeltdownGuid = "me.loaforc.facilitymeltdown"; private static readonly Type? MeltdownApiType = AccessTools.TypeByName("FacilityMeltdown.API.MeltdownAPI"); private static readonly Type? MeltdownPluginType = AccessTools.TypeByName("FacilityMeltdown.MeltdownPlugin"); private static readonly Type? MeltdownAssetsType = AccessTools.TypeByName("FacilityMeltdown.MeltdownAssets"); private static readonly Type? MeltdownHandlerType = AccessTools.TypeByName("FacilityMeltdown.MeltdownSequence.Behaviours.MeltdownHandler"); private static readonly PropertyInfo? MeltdownStartedProperty = AccessTools.Property(MeltdownApiType, "MeltdownStarted"); private static readonly PropertyInfo? AssetsProperty = AccessTools.Property(MeltdownPluginType, "assets"); private static readonly PropertyInfo? MeltdownHandlerPrefabProperty = AccessTools.Property(MeltdownAssetsType, "meltdownHandlerPrefab"); private static readonly FieldInfo? CausingLungPropField = AccessTools.Field(MeltdownHandlerType, "causingLungProp"); public static bool IsLoaded { get { if (Chainloader.PluginInfos.ContainsKey("me.loaforc.facilitymeltdown") && MeltdownStartedProperty != null && AssetsProperty != null && MeltdownHandlerPrefabProperty != null && MeltdownHandlerType != null) { return CausingLungPropField != null; } return false; } } public static void TryBeginMeltdownForFallback(LungProp lungProp) { if (!IsLoaded || (Object)(object)lungProp == (Object)null) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || GetMeltdownStarted()) { return; } object? obj = SafeGetValue(SafeGetValue(null, AssetsProperty), MeltdownHandlerPrefabProperty); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = Object.Instantiate<GameObject>(val); Component component = val2.GetComponent(MeltdownHandlerType); NetworkObject component2 = val2.GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { Object.Destroy((Object)(object)val2); return; } CausingLungPropField.SetValue(component, lungProp); if (!component2.IsSpawned) { component2.Spawn(false); } GuaranteeApparatusPlugin.Verbose("Facility Meltdown compatibility bridge spawned a meltdown handler for fallback apparatus pickup."); } private static bool GetMeltdownStarted() { try { object obj = SafeGetValue(null, MeltdownStartedProperty); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static object? SafeGetValue(object? instance, PropertyInfo? property) { if (property == null) { return null; } try { return property.GetValue(instance); } catch { return null; } } } internal sealed class LllContext { public bool IsLoaded { get; set; } public bool MetadataUnreliable { get; set; } public object? CurrentExtendedDungeon { get; set; } public object? CurrentExtendedLevel { get; set; } public bool CurrentExtendedDungeonIsCustom { get; set; } public string? DungeonModName { get; set; } public string? DungeonAuthorName { get; set; } public string? DungeonUniqueId { get; set; } public string? DungeonName { get; set; } } internal static class LllReflection { private const string LllGuid = "imabatby.lethallevelloader"; private static readonly Type? DungeonManagerType = AccessTools.TypeByName("LethalLevelLoader.DungeonManager"); private static readonly Type? LevelManagerType = AccessTools.TypeByName("LethalLevelLoader.LevelManager"); private static readonly Type? PatchedContentType = AccessTools.TypeByName("LethalLevelLoader.PatchedContent"); private static readonly Type? ExtendedContentType = AccessTools.TypeByName("LethalLevelLoader.ExtendedContent"); private static readonly Type? ExtendedDungeonFlowType = AccessTools.TypeByName("LethalLevelLoader.ExtendedDungeonFlow"); private static readonly PropertyInfo? CurrentExtendedDungeonProperty = AccessTools.Property(DungeonManagerType, "CurrentExtendedDungeonFlow"); private static readonly PropertyInfo? CurrentExtendedLevelProperty = AccessTools.Property(LevelManagerType, "CurrentExtendedLevel"); private static readonly PropertyInfo? VanillaExtendedDungeonFlowsProperty = AccessTools.Property(PatchedContentType, "VanillaExtendedDungeonFlows"); private static readonly PropertyInfo? VanillaModProperty = AccessTools.Property(PatchedContentType, "VanillaMod"); private static readonly FieldInfo? ExtendedItemDictionaryField = AccessTools.Field(PatchedContentType, "ExtendedItemDictionary"); private static readonly PropertyInfo? ModNameProperty = AccessTools.Property(ExtendedContentType, "ModName"); private static readonly PropertyInfo? AuthorNameProperty = AccessTools.Property(ExtendedContentType, "AuthorName"); private static readonly PropertyInfo? UniqueIdentificationNameProperty = AccessTools.Property(ExtendedContentType, "UniqueIdentificationName"); private static readonly PropertyInfo? DungeonNameProperty = AccessTools.Property(ExtendedDungeonFlowType, "DungeonName"); public static bool IsLoaded { get { if (Chainloader.PluginInfos.ContainsKey("imabatby.lethallevelloader") && DungeonManagerType != null) { return PatchedContentType != null; } return false; } } public static LllContext Capture() { if (!IsLoaded) { return new LllContext { IsLoaded = false }; } bool metadataUnreliable = false; object obj = TryGetPropertyValue(null, CurrentExtendedDungeonProperty, "CurrentExtendedDungeonProperty", ref metadataUnreliable); object currentExtendedLevel = TryGetPropertyValue(null, CurrentExtendedLevelProperty, "CurrentExtendedLevelProperty", ref metadataUnreliable); string contentString = GetContentString(obj, ModNameProperty, "ModNameProperty", ref metadataUnreliable); string contentString2 = GetContentString(obj, AuthorNameProperty, "AuthorNameProperty", ref metadataUnreliable); string contentString3 = GetContentString(obj, UniqueIdentificationNameProperty, "UniqueIdentificationNameProperty", ref metadataUnreliable); string contentString4 = GetContentString(obj, DungeonNameProperty, "DungeonNameProperty", ref metadataUnreliable); return new LllContext { IsLoaded = true, MetadataUnreliable = metadataUnreliable, CurrentExtendedDungeon = obj, CurrentExtendedLevel = currentExtendedLevel, CurrentExtendedDungeonIsCustom = (!metadataUnreliable && IsCustomExtendedDungeon(obj)), DungeonModName = contentString, DungeonAuthorName = contentString2, DungeonUniqueId = contentString3, DungeonName = contentString4 }; } public static bool TryGetExtendedItemMetadata(Item? item, out string? modName, out string? uniqueId) { modName = null; uniqueId = null; if (!IsLoaded || (Object)(object)item == (Object)null || ExtendedItemDictionaryField == null) { return false; } object value; try { value = ExtendedItemDictionaryField.GetValue(null); } catch { return false; } if (!(value is IDictionary dictionary) || !dictionary.Contains(item)) { return false; } object obj2 = dictionary[item]; if (obj2 == null) { return false; } bool metadataUnreliable = false; modName = GetContentString(obj2, ModNameProperty, "ModNameProperty", ref metadataUnreliable); uniqueId = GetContentString(obj2, UniqueIdentificationNameProperty, "UniqueIdentificationNameProperty", ref metadataUnreliable); return true; } public static bool IsVanillaItem(Item? item) { if ((Object)(object)item == (Object)null) { return false; } if (!TryGetExtendedItemMetadata(item, out string modName, out string _)) { return true; } string vanillaModName = GetVanillaModName(); if (!string.IsNullOrWhiteSpace(vanillaModName)) { return string.Equals(modName, vanillaModName, StringComparison.OrdinalIgnoreCase); } return false; } private static bool IsCustomExtendedDungeon(object? currentExtendedDungeon) { if (currentExtendedDungeon == null || VanillaExtendedDungeonFlowsProperty == null) { return false; } bool metadataUnreliable = false; if (!(TryGetPropertyValue(null, VanillaExtendedDungeonFlowsProperty, "VanillaExtendedDungeonFlowsProperty", ref metadataUnreliable) is IEnumerable enumerable)) { return false; } try { foreach (object item in enumerable) { if (item == currentExtendedDungeon) { return false; } } } catch { return false; } return true; } private static string? GetVanillaModName() { bool metadataUnreliable = false; object? obj = TryGetPropertyValue(null, VanillaModProperty, "VanillaModProperty", ref metadataUnreliable); PropertyInfo property = AccessTools.Property(obj?.GetType(), "ModName"); return GetContentString(obj, property, "VanillaMod.ModName", ref metadataUnreliable); } private static string? GetContentString(object? instance, PropertyInfo? property, string propertyName, ref bool metadataUnreliable) { return TryGetPropertyValue(instance, property, propertyName, ref metadataUnreliable) as string; } private static object? TryGetPropertyValue(object? instance, PropertyInfo? property, string propertyName, ref bool metadataUnreliable) { if (property == null) { metadataUnreliable = true; return null; } try { return property.GetValue(instance); } catch { metadataUnreliable = true; GuaranteeApparatusPlugin.Verbose("LLL reflection failed while reading '" + propertyName + "'."); return null; } } } [BepInPlugin("squirt.guaranteeapparatus", "GuaranteeApparatus", "1.0.0")] public sealed class GuaranteeApparatusPlugin : BaseUnityPlugin { internal const string PluginGuid = "squirt.guaranteeapparatus"; internal const string PluginName = "GuaranteeApparatus"; internal const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> VerboseLogging; internal static ConfigEntry<bool> RespectCustomApparatus; internal static ConfigEntry<bool> RespectUnknownCustomApparatus; internal static ConfigEntry<bool> SpawnFallbackWhenUnknownCustomHasNoDetectedApparatus; internal static ConfigEntry<bool> GenerateScanNodesForDetectedCustomApparatus; internal static ConfigEntry<bool> ForceVanillaFallbackOnKnownCustomInteriors; internal static ConfigEntry<bool> EnableVanillaSetpieceFallback; internal static ConfigEntry<float> MinimumEntranceDistance; internal static ConfigEntry<string> FactoryFlowPatterns; private Harmony? _harmony; internal static bool HasSpawnedFallbackThisRound { get; set; } private void Awake() { //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable GuaranteeApparatus."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Enable verbose logging for classification and validation details."); RespectCustomApparatus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RespectCustomApparatus", true, "When enabled, known custom apparatus systems suppress fallback by default."); RespectUnknownCustomApparatus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RespectUnknownCustomApparatus", true, "When enabled, detected unknown custom apparatuses suppress fallback by default."); SpawnFallbackWhenUnknownCustomHasNoDetectedApparatus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SpawnFallbackWhenUnknownCustomHasNoDetectedApparatus", true, "Allow vanilla fallback inside unknown custom interiors when no plausible custom apparatus is detected."); GenerateScanNodesForDetectedCustomApparatus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "GenerateScanNodesForDetectedCustomApparatus", true, "Generate scan-node support for detected custom apparatuses when they lack discoverability metadata."); ForceVanillaFallbackOnKnownCustomInteriors = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceVanillaFallbackOnKnownCustomInteriors", false, "Force vanilla fallback inside known custom interiors when no valid vanilla apparatus is found."); EnableVanillaSetpieceFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableVanillaSetpieceFallback", false, "Deprecated. The fallback now always prefers a strict vanilla LungMachine setpiece sourced from factory tile prefabs, so this setting is ignored."); MinimumEntranceDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinimumEntranceDistance", 10f, "Minimum distance from the main dungeon entrance required for fallback placement."); FactoryFlowPatterns = ((BaseUnityPlugin)this).Config.Bind<string>("General", "FactoryFlowPatterns", "factory,facility,level1flow,level2flow", "Comma-separated flow name fragments treated as factory-like for diagnostics."); _harmony = new Harmony("squirt.guaranteeapparatus"); _harmony.PatchAll(typeof(RoundManagerPatches)); Log.LogInfo((object)"GuaranteeApparatus 1.0.0 loaded."); } internal static void Verbose(string message) { if (VerboseLogging.Value) { Log.LogInfo((object)message); } } } internal static class RoundManagerPatches { [HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")] [HarmonyPrefix] private static void GenerateNewFloorPrefix(RoundManager __instance) { GuaranteeApparatusPlugin.HasSpawnedFallbackThisRound = false; SafeRun("GenerateNewFloorPrefix/ScanNodeSupportService.EnsureMessagingReady", ScanNodeSupportService.EnsureMessagingReady); SafeRun("GenerateNewFloorPrefix/VanillaApparatusSetpieceService.EnsureMessagingReady", VanillaApparatusSetpieceService.EnsureMessagingReady); } [HarmonyPatch(typeof(RoundManager), "GeneratedFloorPostProcessing")] [HarmonyPostfix] private static void GeneratedFloorPostProcessingPostfix(RoundManager __instance) { RoundManager __instance2 = __instance; SafeRun("GeneratedFloorPostProcessing/ScanNodeSupportService.EnsureMessagingReady", ScanNodeSupportService.EnsureMessagingReady); SafeRun("GeneratedFloorPostProcessing/ScanNodeSupportService.TryResolvePending", delegate { ScanNodeSupportService.TryResolvePending(__instance2); }); SafeRun("GeneratedFloorPostProcessing/VanillaApparatusSetpieceService.EnsureMessagingReady", VanillaApparatusSetpieceService.EnsureMessagingReady); SafeRun("GeneratedFloorPostProcessing/VanillaApparatusSetpieceService.TryResolvePending", VanillaApparatusSetpieceService.TryResolvePending); SafeRun("GeneratedFloorPostProcessing/ApparatusValidator.Evaluate", delegate { ApparatusValidator.Evaluate(__instance2); }); } private static void SafeRun(string operationName, Action action) { try { action(); } catch (Exception ex) { GuaranteeApparatusPlugin.Log.LogWarning((object)(operationName + " failed; continuing without that step. " + ex.GetType().Name + ": " + ex.Message)); } } } internal sealed class GuaranteeApparatusScanNodeMarker : MonoBehaviour { public string RelativeDungeonPath = string.Empty; } internal sealed class GuaranteeApparatusPendingScanNodeResolver : MonoBehaviour { private void Update() { ScanNodeSupportService.TryResolvePendingForActiveRound(); } } internal enum ScanNodeSupportOutcome { Failed, AlreadyPresent, Generated } internal readonly struct ScanNodeDescriptor { public bool UseNetworkObjectLookup { get; } public ulong NetworkObjectId { get; } public string RelativeDungeonPath { get; } public string HeaderText { get; } public string SubText { get; } public int MinRange { get; } public int MaxRange { get; } public bool RequiresLineOfSight { get; } public int NodeType { get; } public ScanNodeDescriptor(bool useNetworkObjectLookup, ulong networkObjectId, string relativeDungeonPath, string headerText, string subText, int minRange, int maxRange, bool requiresLineOfSight, int nodeType) { UseNetworkObjectLookup = useNetworkObjectLookup; NetworkObjectId = networkObjectId; RelativeDungeonPath = relativeDungeonPath; HeaderText = headerText; SubText = subText; MinRange = minRange; MaxRange = maxRange; RequiresLineOfSight = requiresLineOfSight; NodeType = nodeType; } } internal static class ScanNodeSupportService { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnScanNodeMessageReceived; } private const string MessageName = "GuaranteeApparatus/ScanNode"; private const string HelperObjectName = "GuaranteeApparatusScanNode"; private const string ResolverObjectName = "GuaranteeApparatusPendingScanNodeResolver"; private static readonly List<ScanNodeDescriptor> PendingDescriptors = new List<ScanNodeDescriptor>(); private static NetworkManager? _registeredManager; private static bool _messageHandlerRegistered; private static GuaranteeApparatusPendingScanNodeResolver? _resolver; public static void EnsureMessagingReady() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown EnsureResolver(); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && (_registeredManager != singleton || !_messageHandlerRegistered)) { if ((Object)(object)_registeredManager != (Object)null && _messageHandlerRegistered && _registeredManager.CustomMessagingManager != null) { _registeredManager.CustomMessagingManager.UnregisterNamedMessageHandler("GuaranteeApparatus/ScanNode"); } CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnScanNodeMessageReceived; if (obj == null) { HandleNamedMessageDelegate val = OnScanNodeMessageReceived; <>O.<0>__OnScanNodeMessageReceived = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("GuaranteeApparatus/ScanNode", (HandleNamedMessageDelegate)obj); _registeredManager = singleton; _messageHandlerRegistered = true; } } public static void TryResolvePending(RoundManager roundManager) { if (PendingDescriptors.Count == 0) { return; } for (int num = PendingDescriptors.Count - 1; num >= 0; num--) { ScanNodeDescriptor descriptor = PendingDescriptors[num]; if (TryApplyDescriptor(roundManager, descriptor, out var _)) { PendingDescriptors.RemoveAt(num); } } } public static void TryResolvePendingForActiveRound() { if (PendingDescriptors.Count != 0) { RoundManager val = Object.FindObjectOfType<RoundManager>(); if ((Object)(object)val != (Object)null) { TryResolvePending(val); } } } public static ScanNodeSupportOutcome EnsureCustomScanSupport(ValidationContext context, DetectedCustomApparatusCandidate candidate) { if (!GuaranteeApparatusPlugin.GenerateScanNodesForDetectedCustomApparatus.Value || (Object)(object)candidate.RootTransform == (Object)null || !context.IsInDungeonHierarchy(candidate.RootTransform)) { return ScanNodeSupportOutcome.Failed; } ScanNodeDescriptor descriptor = BuildCustomDescriptor(candidate); return EnsureDescriptor(candidate.RootTransform, descriptor); } public static ScanNodeSupportOutcome EnsureFallbackScanSupport(LungProp lungProp, string relativeDungeonPath) { NetworkObject val = (((Object)(object)lungProp != (Object)null) ? ((Component)lungProp).GetComponent<NetworkObject>() : null); if ((Object)(object)lungProp == (Object)null || (Object)(object)val == (Object)null || !val.IsSpawned) { return ScanNodeSupportOutcome.Failed; } ScanNodeDescriptor descriptor = BuildFallbackDescriptor(lungProp, val.NetworkObjectId, relativeDungeonPath); return EnsureFallbackDescriptor(((Component)lungProp).transform, descriptor); } private static void OnScanNodeMessageReceived(ulong senderClientId, FastBufferReader reader) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0094: 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_00a7: 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) bool useNetworkObjectLookup = false; ulong networkObjectId = 0uL; string empty = string.Empty; string empty2 = string.Empty; string empty3 = string.Empty; int minRange = 0; int maxRange = 0; bool requiresLineOfSight = false; int nodeType = 0; ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref useNetworkObjectLookup, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref empty, true); ((FastBufferReader)(ref reader)).ReadValueSafe(ref empty2, true); ((FastBufferReader)(ref reader)).ReadValueSafe(ref empty3, true); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref minRange, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref maxRange, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref requiresLineOfSight, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref nodeType, default(ForPrimitives)); ScanNodeDescriptor scanNodeDescriptor = new ScanNodeDescriptor(useNetworkObjectLookup, networkObjectId, empty, empty2, empty3, minRange, maxRange, requiresLineOfSight, nodeType); if (!TryApplyDescriptor(Object.FindObjectOfType<RoundManager>(), scanNodeDescriptor, out var _)) { PendingDescriptors.Add(scanNodeDescriptor); } } private static void SendDescriptorToAll(ScanNodeDescriptor descriptor) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(2048, (Allocator)2, 2048); try { bool useNetworkObjectLookup = descriptor.UseNetworkObjectLookup; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref useNetworkObjectLookup, default(ForPrimitives)); ulong networkObjectId = descriptor.NetworkObjectId; ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(descriptor.RelativeDungeonPath, true); ((FastBufferWriter)(ref val)).WriteValueSafe(descriptor.HeaderText, true); ((FastBufferWriter)(ref val)).WriteValueSafe(descriptor.SubText, true); int minRange = descriptor.MinRange; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref minRange, default(ForPrimitives)); minRange = descriptor.MaxRange; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref minRange, default(ForPrimitives)); useNetworkObjectLookup = descriptor.RequiresLineOfSight; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref useNetworkObjectLookup, default(ForPrimitives)); minRange = descriptor.NodeType; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref minRange, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("GuaranteeApparatus/ScanNode", val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static ScanNodeDescriptor BuildCustomDescriptor(DetectedCustomApparatusCandidate candidate) { string headerText = (string.IsNullOrWhiteSpace(candidate.DisplayName) ? "Custom Apparatus" : candidate.DisplayName); string subText = ((candidate.Classification == InteriorClassification.KnownCustom) ? "Known Custom Apparatus" : "Unknown Custom Apparatus"); return new ScanNodeDescriptor(useNetworkObjectLookup: false, 0uL, candidate.RelativeDungeonPath, headerText, subText, 0, 30, requiresLineOfSight: false, 0); } private static ScanNodeDescriptor BuildFallbackDescriptor(LungProp lungProp, ulong networkObjectId, string relativeDungeonPath) { string headerText = (string.IsNullOrWhiteSpace(((GrabbableObject)lungProp).itemProperties?.itemName) ? "Apparatus" : ((GrabbableObject)lungProp).itemProperties.itemName); return new ScanNodeDescriptor(useNetworkObjectLookup: true, networkObjectId, relativeDungeonPath, headerText, "Fallback Vanilla Apparatus", 0, 30, requiresLineOfSight: false, 0); } private static ScanNodeSupportOutcome EnsureDescriptor(Transform localTargetRoot, ScanNodeDescriptor descriptor) { if (HasExistingScanSupport(localTargetRoot)) { return ScanNodeSupportOutcome.AlreadyPresent; } if (!TryApplyDescriptorToResolvedTarget(localTargetRoot, descriptor, out var generatedScanNode) || !generatedScanNode) { return ScanNodeSupportOutcome.Failed; } EnsureMessagingReady(); SendDescriptorToAll(descriptor); return ScanNodeSupportOutcome.Generated; } private static ScanNodeSupportOutcome EnsureFallbackDescriptor(Transform localTargetRoot, ScanNodeDescriptor descriptor) { Transform val = SelectAttachmentTransform(localTargetRoot); bool num = (Object)(object)((Component)val).GetComponent<GuaranteeApparatusScanNodeMarker>() != (Object)null; EnsureMarker(((Component)val).gameObject, descriptor.RelativeDungeonPath); ApplyScanNodeComponent(((Component)val).gameObject, descriptor); if ((Object)(object)((Component)val).GetComponent<Collider>() == (Object)null && (Object)(object)((Component)val).GetComponent<Renderer>() == (Object)null) { EnsureHelperCollider(((Component)val).gameObject, localTargetRoot); } EnsureMessagingReady(); SendDescriptorToAll(descriptor); if (!num) { return ScanNodeSupportOutcome.Generated; } return ScanNodeSupportOutcome.AlreadyPresent; } private static bool TryApplyDescriptor(RoundManager? roundManager, ScanNodeDescriptor descriptor, out bool generatedScanNode) { generatedScanNode = false; if (!TryResolveTargetTransform(roundManager, descriptor, out Transform targetRoot) || (Object)(object)targetRoot == (Object)null) { return false; } if (descriptor.UseNetworkObjectLookup) { Transform val = SelectAttachmentTransform(targetRoot); bool flag = (Object)(object)((Component)val).GetComponent<GuaranteeApparatusScanNodeMarker>() != (Object)null; EnsureMarker(((Component)val).gameObject, descriptor.RelativeDungeonPath); ApplyScanNodeComponent(((Component)val).gameObject, descriptor); if ((Object)(object)((Component)val).GetComponent<Collider>() == (Object)null && (Object)(object)((Component)val).GetComponent<Renderer>() == (Object)null) { EnsureHelperCollider(((Component)val).gameObject, targetRoot); } generatedScanNode = !flag; return true; } return TryApplyDescriptorToResolvedTarget(targetRoot, descriptor, out generatedScanNode); } private static bool TryResolveTargetTransform(RoundManager? roundManager, ScanNodeDescriptor descriptor, out Transform? targetRoot) { if (descriptor.UseNetworkObjectLookup) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.SpawnManager != null && singleton.SpawnManager.SpawnedObjects.TryGetValue(descriptor.NetworkObjectId, out var value) && (Object)(object)value != (Object)null) { targetRoot = ((Component)value).transform; return true; } } if ((Object)(object)roundManager == (Object)null || (Object)(object)roundManager.dungeonGenerator == (Object)null || roundManager.dungeonGenerator.Generator == null) { targetRoot = null; return false; } Dungeon currentDungeon = roundManager.dungeonGenerator.Generator.CurrentDungeon; if ((Object)(object)currentDungeon == (Object)null) { targetRoot = null; return false; } return TryFindByRelativePath(((Component)currentDungeon).transform, descriptor.RelativeDungeonPath, out targetRoot); } private static bool TryApplyDescriptorToResolvedTarget(Transform targetRoot, ScanNodeDescriptor descriptor, out bool generatedScanNode) { generatedScanNode = false; if (HasExistingScanSupport(targetRoot)) { return true; } Transform val = SelectAttachmentTransform(targetRoot); if (CanAttachDirectly(val)) { EnsureMarker(((Component)val).gameObject, descriptor.RelativeDungeonPath); ApplyScanNodeComponent(((Component)val).gameObject, descriptor); generatedScanNode = true; return true; } GameObject orCreateHelperObject = GetOrCreateHelperObject(targetRoot, descriptor.RelativeDungeonPath); ApplyScanNodeComponent(orCreateHelperObject, descriptor); EnsureHelperCollider(orCreateHelperObject, targetRoot); generatedScanNode = true; return true; } private static bool HasExistingScanSupport(Transform rootTransform) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (((Component)rootTransform).GetComponentsInChildren<GuaranteeApparatusScanNodeMarker>(true).Length != 0) { return true; } ScanNodeProperties[] componentsInChildren = ((Component)rootTransform).GetComponentsInChildren<ScanNodeProperties>(true); foreach (ScanNodeProperties val in componentsInChildren) { if ((Object)(object)val != (Object)null) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ((Behaviour)val).isActiveAndEnabled) { return true; } } } return false; } private static void EnsureResolver() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if (!((Object)(object)_resolver != (Object)null)) { GuaranteeApparatusPendingScanNodeResolver guaranteeApparatusPendingScanNodeResolver = Object.FindObjectOfType<GuaranteeApparatusPendingScanNodeResolver>(); if ((Object)(object)guaranteeApparatusPendingScanNodeResolver != (Object)null) { _resolver = guaranteeApparatusPendingScanNodeResolver; return; } GameObject val = new GameObject("GuaranteeApparatusPendingScanNodeResolver"); Object.DontDestroyOnLoad((Object)val); _resolver = val.AddComponent<GuaranteeApparatusPendingScanNodeResolver>(); } } private static Transform SelectAttachmentTransform(Transform targetRoot) { Transform targetRoot2 = targetRoot; Transform[] componentsInChildren = ((Component)targetRoot2).GetComponentsInChildren<Transform>(true); Array.Sort(componentsInChildren, (Transform left, Transform right) => string.Compare(GetRelativePath(left, targetRoot2), GetRelativePath(right, targetRoot2), StringComparison.Ordinal)); Transform val = null; Transform val2 = null; GrabbableObject val4 = default(GrabbableObject); InteractTrigger val5 = default(InteractTrigger); TerminalAccessibleObject val6 = default(TerminalAccessibleObject); foreach (Transform val3 in componentsInChildren) { if (((Component)val3).TryGetComponent<GrabbableObject>(ref val4) || ((Component)val3).TryGetComponent<InteractTrigger>(ref val5) || ((Component)val3).TryGetComponent<TerminalAccessibleObject>(ref val6)) { return val3; } if ((Object)(object)val == (Object)null && (Object)(object)((Component)val3).GetComponent<Collider>() != (Object)null) { val = val3; } if ((Object)(object)val2 == (Object)null && (Object)(object)((Component)val3).GetComponent<Renderer>() != (Object)null) { val2 = val3; } } return val ?? val2 ?? targetRoot2; } private static bool CanAttachDirectly(Transform attachmentTransform) { GrabbableObject val = default(GrabbableObject); InteractTrigger val2 = default(InteractTrigger); TerminalAccessibleObject val3 = default(TerminalAccessibleObject); if (!((Component)attachmentTransform).TryGetComponent<GrabbableObject>(ref val) && !((Component)attachmentTransform).TryGetComponent<InteractTrigger>(ref val2) && !((Component)attachmentTransform).TryGetComponent<TerminalAccessibleObject>(ref val3) && !((Object)(object)((Component)attachmentTransform).GetComponent<Collider>() != (Object)null)) { return (Object)(object)((Component)attachmentTransform).GetComponent<Renderer>() != (Object)null; } return true; } private static void EnsureMarker(GameObject gameObject, string relativeDungeonPath) { (gameObject.GetComponent<GuaranteeApparatusScanNodeMarker>() ?? gameObject.AddComponent<GuaranteeApparatusScanNodeMarker>()).RelativeDungeonPath = relativeDungeonPath; } private static void ApplyScanNodeComponent(GameObject gameObject, ScanNodeDescriptor descriptor) { ScanNodeProperties obj = gameObject.GetComponent<ScanNodeProperties>() ?? gameObject.AddComponent<ScanNodeProperties>(); obj.headerText = descriptor.HeaderText; obj.subText = descriptor.SubText; obj.minRange = descriptor.MinRange; obj.maxRange = descriptor.MaxRange; obj.requiresLineOfSight = descriptor.RequiresLineOfSight; obj.nodeType = descriptor.NodeType; obj.scrapValue = 0; obj.creatureScanID = -1; } private static GameObject GetOrCreateHelperObject(Transform targetRoot, string relativeDungeonPath) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_0099: Expected O, but got Unknown GuaranteeApparatusScanNodeMarker[] componentsInChildren = ((Component)targetRoot).GetComponentsInChildren<GuaranteeApparatusScanNodeMarker>(true); foreach (GuaranteeApparatusScanNodeMarker guaranteeApparatusScanNodeMarker in componentsInChildren) { if (!((Object)(object)guaranteeApparatusScanNodeMarker == (Object)null) && string.Equals(guaranteeApparatusScanNodeMarker.RelativeDungeonPath, relativeDungeonPath, StringComparison.Ordinal)) { return ((Component)guaranteeApparatusScanNodeMarker).gameObject; } } GameObject val = new GameObject("GuaranteeApparatusScanNode"); val.transform.SetParent(targetRoot, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; val.layer = ((Component)targetRoot).gameObject.layer; EnsureMarker(val, relativeDungeonPath); return val; } private static void EnsureHelperCollider(GameObject helperObject, Transform targetRoot) { SphereCollider obj = helperObject.GetComponent<SphereCollider>() ?? helperObject.AddComponent<SphereCollider>(); ((Collider)obj).isTrigger = true; obj.radius = 0.35f; helperObject.layer = ((Component)targetRoot).gameObject.layer; } private static string GetRelativePath(Transform transform, Transform rootTransform) { if ((Object)(object)transform == (Object)(object)rootTransform) { return string.Empty; } Stack<string> stack = new Stack<string>(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)rootTransform) { stack.Push(((Object)val).name); val = val.parent; } return string.Join("/", stack); } private static bool TryFindByRelativePath(Transform rootTransform, string relativePath, out Transform? foundTransform) { if (string.IsNullOrWhiteSpace(relativePath)) { foundTransform = null; return false; } string[] array = relativePath.Split('/'); Transform val = rootTransform; foreach (string b in array) { Transform val2 = null; int childCount = val.childCount; for (int j = 0; j < childCount; j++) { Transform child = val.GetChild(j); if (string.Equals(((Object)child).name, b, StringComparison.Ordinal)) { val2 = child; break; } } if ((Object)(object)val2 == (Object)null) { foundTransform = null; return false; } val = val2; } foundTransform = val; return (Object)(object)foundTransform != (Object)null; } } internal sealed class DetectedCustomApparatusCandidate { public InteriorClassification Classification { get; } public string SourceId { get; } public string SourceDisplayName { get; } public Transform RootTransform { get; } public string RelativeDungeonPath { get; } public string DisplayName { get; } public string Reason { get; } public int Score { get; } public DetectedCustomApparatusCandidate(InteriorClassification classification, string sourceId, string sourceDisplayName, Transform rootTransform, string relativeDungeonPath, string displayName, string reason, int score) { Classification = classification; SourceId = sourceId; SourceDisplayName = sourceDisplayName; RootTransform = rootTransform; RelativeDungeonPath = relativeDungeonPath; DisplayName = displayName; Reason = reason; Score = score; } public bool ControlsTransform(Transform transform) { if (!((Object)(object)transform == (Object)(object)RootTransform)) { return transform.IsChildOf(RootTransform); } return true; } } internal readonly struct SignalEvidence { public string Key { get; } public string Reason { get; } public SignalEvidence(string key, string reason) { Key = key; Reason = reason; } } internal readonly struct RootCandidateGroup { public Transform RootTransform { get; } public string RelativeDungeonPath { get; } public RootCandidateGroup(Transform rootTransform, string relativeDungeonPath) { RootTransform = rootTransform; RelativeDungeonPath = relativeDungeonPath; } } internal static class UnknownCustomApparatusDiscovery { private static readonly string[] PrimaryTokens = new string[7] { "apparatus", "lung", "generator", "socket", "upturned", "power", "core" }; private static readonly string[] PowerTokens = new string[11] { "power", "generator", "socket", "breaker", "battery", "reactor", "charge", "switch", "fuse", "core", "elevator" }; private static readonly string[] VariantTokens = new string[6] { "off", "disabled", "inactive", "turnedoff", "turned_off", "turned-off" }; private const int MaxGroupedRootsPerScan = 100; public static bool TryDiscover(ValidationContext context, out DetectedCustomApparatusCandidate? detectedCandidate) { List<RootCandidateGroup> list = BuildRootCandidateGroups(context); for (int i = 0; i < list.Count; i++) { if (TryEvaluateGroup(context, list[i], out DetectedCustomApparatusCandidate detectedCandidate2)) { DetectedCustomApparatusCandidate detectedCustomApparatusCandidate = (detectedCandidate = detectedCandidate2); Guara