Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of OrganizedCrime v2.0.0
Mods/OrganizedCrime.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading; using HarmonyLib; using Il2Cpp; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Managing; using Il2CppFishNet.Managing.Client; using Il2CppFishNet.Managing.Object; using Il2CppFishNet.Managing.Server; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.Avatar; using Il2CppScheduleOne.AvatarFramework; using Il2CppScheduleOne.AvatarFramework.Equipping; using Il2CppScheduleOne.Building.Doors; using Il2CppScheduleOne.Calling; using Il2CppScheduleOne.Cartel; using Il2CppScheduleOne.Combat; using Il2CppScheduleOne.Core.Avatar; using Il2CppScheduleOne.Core.Items.Framework; using Il2CppScheduleOne.Delivery; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Dialogue; using Il2CppScheduleOne.Doors; using Il2CppScheduleOne.Economy; using Il2CppScheduleOne.Employees; using Il2CppScheduleOne.EntityFramework; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.Interaction; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Law; using Il2CppScheduleOne.Management; using Il2CppScheduleOne.Map; using Il2CppScheduleOne.Messaging; using Il2CppScheduleOne.Misc; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Actions; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.NPCs.Framework; using Il2CppScheduleOne.NPCs.Responses; using Il2CppScheduleOne.Networking; using Il2CppScheduleOne.ObjectScripts; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.Persistence.Datas; using Il2CppScheduleOne.Persistence.Loaders; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.PlayerScripts.Health; using Il2CppScheduleOne.Police; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.Product.Packaging; using Il2CppScheduleOne.Property; using Il2CppScheduleOne.ScriptableObjects; using Il2CppScheduleOne.Storage; using Il2CppScheduleOne.Tiles; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Management; using Il2CppScheduleOne.UI.Phone; using Il2CppScheduleOne.UI.Phone.Delivery; using Il2CppScheduleOne.UI.SleepMessage; using Il2CppScheduleOne.Vehicles; using Il2CppScheduleOne.VoiceOver; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using OrganizedCrime; using OrganizedCrime.Core.Diagnostics; using OrganizedCrime.Core.Hosting; using OrganizedCrime.Core.Lifecycle; using OrganizedCrime.Core.Persistence; using OrganizedCrime.Core.Scheduling; using OrganizedCrime.Core.Time; using OrganizedCrime.Model; using OrganizedCrime.Persistence; using OrganizedCrime.Runtime; using OrganizedCrime.Runtime.Chapter3; using OrganizedCrime.Runtime.Composition; using OrganizedCrime.Runtime.Platform; using OrganizedCrime.Runtime.Police; using OrganizedCrime.Runtime.Qa; using OrganizedCrime.Runtime.Release1.Missions; using OrganizedCrime.Runtime.Release1.World; using S1API.Building; using S1API.Console; using S1API.DeadDrops; using S1API.Entities; using S1API.Entities.Appearances.AccessoryFields; using S1API.Entities.Appearances.FaceLayerFields; using S1API.GameTime; using S1API.Items; using S1API.Law; using S1API.Lifecycle; using S1API.Messaging; using S1API.Money; using S1API.PhoneCalls; using S1API.Products; using S1API.Property; using S1API.Quests; using S1API.Quests.Constants; using S1API.Storages; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(OrganizedCrimeMod), "Organized Crime", "2.0.0", "MadJag Studios", null)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OrganizedCrime { public sealed class OrganizedCrimeMod : MelonMod { private bool _initialized; private OcCompositionRoot? _root; private OcLoggingPolicy? _logging; private readonly OcCompositionLifetime _lifetime = new OcCompositionLifetime(); public override void OnInitializeMelon() { if (_initialized) { ((MelonBase)this).LoggerInstance.Warning("duplicate initialization was ignored."); return; } _initialized = true; OcLoggingPolicy logging = new OcLoggingPolicy((Action<string>)((MelonBase)this).LoggerInstance.Msg, (Action<string>)((MelonBase)this).LoggerInstance.Warning); _logging = logging; OrganizedCrimeLog.DiagnosticSink = logging.Diagnostic; OrganizedCrimeLog.FlushWarningSummaries = logging.FlushWarningSummaries; OrganizedCrimeLog.CommandResponse = logging.CommandResponse; (_root = new OcCompositionRoot(new MelonEngineAdapters(((MelonBase)this).HarmonyInstance, logging.Warning, delegate(bool enabled) { logging.DiagnosticsEnabled = enabled; }), logging.Information, logging.Warning, _lifetime)).Build(); logging.Startup(((MelonBase)this).Info.Version); } public override void OnDeinitializeMelon() { TeardownAndFlush(delegate { _root?.Teardown(); }, _logging, delegate(string message) { ((MelonBase)this).LoggerInstance.Error(message); }, delegate { _root = null; _logging = null; _initialized = false; }); } public override void OnUpdate() { _root?.Lifecycle.Update(); } public override void OnGUI() { _root?.Lifecycle.OnGUI(); } private static void TeardownAndFlush(Action teardown, OcLoggingPolicy? logging, Action<string> error, Action reset) { try { teardown(); } catch (Exception value) { error($"teardown failed: {value}"); throw; } finally { try { logging?.FlushWarningSummaries(); } catch (Exception value2) { error($"teardown warning-summary flush failed: {value2}"); throw; } finally { reset(); } } } } } namespace OrganizedCrime.Runtime { public static class Chapter3ArthurAppearance { public const string OldPersonWrinklesPath = "avatar/layers/face/oldpersonwrinkles"; public const string ButtonUpPath = "avatar/layers/top/buttonup"; public const string JeansPath = "avatar/layers/bottom/jeans"; public const string ChevronPath = "Avatar/Accessories/FacialHair/Chevron/Chevron"; public static readonly IReadOnlyList<string> ExpectedPaths = new string[11] { "Avatar/Hair/bowlcut/BowlCut", "Avatar/Layers/Face/Face_Agitated", "avatar/layers/face/oldpersonwrinkles", "avatar/layers/top/buttonup", "avatar/layers/bottom/jeans", "Avatar/Accessories/FacialHair/Chevron/Chevron", "Avatar/Accessories/Head/FlatCap/FlatCap", "Avatar/Accessories/Chest/Blazer/Blazer", "Avatar/Accessories/Feet/DressShoes/DressShoes", "Avatar/Accessories/Waist/Belt/Belt", "Avatar/Accessories/Neck/GoldChain/GoldChain" }; public static void ApplyTo(NPCPrefabBuilder builder) { if (builder != null) { builder.WithAppearanceDefaults((Action<AvatarDefaultsBuilder>)delegate(AvatarDefaultsBuilder appearance) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) appearance.Gender = 0f; appearance.Height = 0.9822339f; appearance.Weight = 0.700238f; appearance.SkinColor = new Color32((byte)180, (byte)144, (byte)113, byte.MaxValue); appearance.LeftEyeLidColor = Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, (byte)0)); appearance.RightEyeLidColor = Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, (byte)0)); appearance.EyeBallTint = Color32.op_Implicit(new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)); appearance.EyeballMaterialIdentifier = ""; appearance.PupilDilation = 0.65f; appearance.EyebrowScale = 1.064336f; appearance.EyebrowThickness = 1.246624f; appearance.EyebrowRestingHeight = -0.521843f; appearance.EyebrowRestingAngle = 1f; appearance.LeftEye = (0.5197827f, 0.5f); appearance.RightEye = (0.5197827f, 0.5f); appearance.HairPath = "Avatar/Hair/bowlcut/BowlCut"; appearance.HairColor = Color32.op_Implicit(new Color32((byte)63, (byte)49, (byte)36, byte.MaxValue)); appearance.WithFaceLayer<Face>("Avatar/Layers/Face/Face_Agitated", Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue))); appearance.WithFaceLayer("avatar/layers/face/oldpersonwrinkles", Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, (byte)133))); appearance.WithBodyLayer("avatar/layers/top/buttonup", Color32.op_Implicit(new Color32((byte)237, (byte)237, (byte)237, byte.MaxValue))); appearance.WithBodyLayer("avatar/layers/bottom/jeans", Color32.op_Implicit(new Color32((byte)31, (byte)42, (byte)68, byte.MaxValue))); appearance.WithAccessoryLayer<FacialHairAccessory>("Avatar/Accessories/FacialHair/Chevron/Chevron", Color32.op_Implicit(new Color32((byte)59, (byte)42, (byte)32, byte.MaxValue))); appearance.WithAccessoryLayer<Head>("Avatar/Accessories/Head/FlatCap/FlatCap", Color32.op_Implicit(new Color32((byte)58, (byte)58, (byte)58, byte.MaxValue))); appearance.WithAccessoryLayer<Chest>("Avatar/Accessories/Chest/Blazer/Blazer", Color32.op_Implicit(new Color32((byte)31, (byte)42, (byte)68, byte.MaxValue))); appearance.WithAccessoryLayer<Feet>("Avatar/Accessories/Feet/DressShoes/DressShoes", Color32.op_Implicit(new Color32((byte)26, (byte)26, (byte)26, byte.MaxValue))); appearance.WithAccessoryLayer<Waist>("Avatar/Accessories/Waist/Belt/Belt", Color32.op_Implicit(new Color32((byte)26, (byte)26, (byte)26, byte.MaxValue))); appearance.WithAccessoryLayer<Neck>("Avatar/Accessories/Neck/GoldChain/GoldChain", Color32.op_Implicit(new Color32((byte)212, (byte)175, (byte)55, byte.MaxValue))); }); } } } public interface IChapter3BeatingWorld { bool IsHost { get; } bool IsPlayerSpawned { get; } bool TryReadHealth(out float health, out float maxHealth, out string reason); bool TrySetHealth(float health, out string reason); bool TryLock(out string reason); bool TryUnlock(out string reason); bool TryFadeOut(out string reason); bool TryFadeIn(out string reason); bool TryJoltCamera(out string reason); bool TryMoveToWake(out (float X, float Y, float Z) wokeAt, out string propertyCode, out string reason); bool TryOpenBill(out string reason); } public interface IChapter3BeatingHq { bool PlayerInsideHq { get; } bool TryExit(out string reason); } public sealed class Chapter3SyndicateHqExit : IChapter3BeatingHq { private readonly Func<bool> _playerInsideHq; private readonly Func<bool> _tryExit; public bool PlayerInsideHq { get { try { return _playerInsideHq(); } catch { return false; } } } public Chapter3SyndicateHqExit(Func<bool> playerInsideHq, Func<bool> tryExit) { _playerInsideHq = playerInsideHq ?? throw new ArgumentNullException("playerInsideHq"); _tryExit = tryExit ?? throw new ArgumentNullException("tryExit"); } public bool TryExit(out string reason) { reason = "the HQ exit did not run."; try { if (_tryExit()) { reason = "left the HQ through its own exit."; return true; } reason = "the HQ service refused the exit."; return false; } catch (Exception ex) { reason = "the HQ exit threw " + ex.GetType().Name + "."; return false; } } } public enum Chapter3BeatingKind { Scheduled, Attack } public sealed class Chapter3ArthurBeating { private delegate bool TryBeatingStep(out string reason); private enum Chapter3BeatingStage { Lock, Line, Punches, FadeOut, Move, Park, Bill, Unlock, Record, Nell, Done, Aborted, Faulted } public const int PunchCount = 4; public const float PunchIntervalSeconds = 1.1f; public const float LineSeconds = 2f; public const float FadeSeconds = 0.7f; public const float HealthFloorFraction = 0.25f; public const float TimeoutSeconds = 30f; private readonly IChapter3BeatingWorld _world; private readonly IChapter3ArthurWorld _arthur; private readonly IChapter3BeatingHq _hq; private readonly Action<string> _log; private readonly Action _record; private readonly Action _noteNell; private readonly Action _parkArthur; private readonly float _startedAt; private Chapter3ArthurVisitState _pumpAtStart; private Chapter3BeatingStage _stage; private float _stageDueAt; private int _punchesDone; private float _startHealth; private float _maxHealth; private bool _moved; private bool _terminalLogged; public Chapter3BeatingKind Kind { get; } public bool ParkArthur { get; } public bool IsDone { get { Chapter3BeatingStage stage = _stage; if ((uint)(stage - 10) <= 2u) { return true; } return false; } } public bool CompletedOk => _stage == Chapter3BeatingStage.Done; public Chapter3ArthurBeating(IChapter3BeatingWorld world, IChapter3ArthurWorld arthur, IChapter3BeatingHq hq, Chapter3BeatingKind kind, bool parkArthur, Chapter3ArthurVisitState pumpAtStart, float startedAt, Action<string> log, Action record, Action noteNell, Action parkArthurAction) { _world = world ?? throw new ArgumentNullException("world"); _arthur = arthur ?? throw new ArgumentNullException("arthur"); _hq = hq ?? throw new ArgumentNullException("hq"); Kind = kind; ParkArthur = parkArthur; _pumpAtStart = pumpAtStart; _startedAt = startedAt; _log = log ?? throw new ArgumentNullException("log"); _record = record ?? throw new ArgumentNullException("record"); _noteNell = noteNell ?? throw new ArgumentNullException("noteNell"); _parkArthur = parkArthurAction ?? throw new ArgumentNullException("parkArthurAction"); _stage = Chapter3BeatingStage.Lock; _stageDueAt = startedAt; } public void Tick(float now, Chapter3ArthurVisitState pumpState) { if (IsDone) { return; } if (now - _startedAt > 30f && now >= _startedAt) { Abort($"timed out after {30f:0} s."); return; } if (!_moved && pumpState != _pumpAtStart) { if (_pumpAtStart != Chapter3ArthurVisitState.Coming || pumpState != Chapter3ArthurVisitState.Arrived) { Abort($"the pump left {_pumpAtStart} for {pumpState} mid-beating."); return; } _pumpAtStart = pumpState; } if (now < _stageDueAt && now >= _startedAt) { return; } try { TickStage(now); } catch (Exception ex) { Abort("threw " + ex.GetType().Name + "."); } } public void AbortOutside(string reason) { Abort(reason); } public void AbortForUnload(string reason) { if (!IsDone) { _stage = Chapter3BeatingStage.Aborted; if (!_terminalLogged) { _terminalLogged = true; _log("Chapter 3 Arthur beating aborted: " + reason); } UnlockBestEffort(); } } private void TickStage(float now) { switch (_stage) { case Chapter3BeatingStage.Lock: { if (!Guarded(_world.TryLock, out string reason8)) { Abort("the lock failed: " + reason8); break; } if (!GuardedHealth(out float health, out float maxHealth, out string reason9)) { Abort("the health read failed: " + reason9); break; } if (maxHealth <= 0f || health <= 0f) { Abort("the player is not up for a beating."); break; } _startHealth = health; _maxHealth = maxHealth; _log($"Chapter 3 Arthur beating: locked (invincible, movement and items held) at {health:0} / {maxHealth:0} health."); Advance(Chapter3BeatingStage.Line, now); break; } case Chapter3BeatingStage.Line: { if ((ParkArthur ? _arthur.TryFacePlayer(out string reason6) : _arthur.TryFacePlayerAtHq(out reason6)) != Chapter3ArthurMutationStatus.Succeeded) { Abort("the face failed: " + reason6); break; } string text = ((Kind == Chapter3BeatingKind.Scheduled) ? "I said I wouldn't be polite." : "Wrong fucking move."); if (_arthur.TrySayArthur(text, out string reason7) != Chapter3ArthurMutationStatus.Succeeded) { Abort("the line failed: " + reason7); break; } _log("Chapter 3 Arthur beating: Arthur says \"" + text + "\""); _stage = Chapter3BeatingStage.Punches; _stageDueAt = now + 2f; break; } case Chapter3BeatingStage.Punches: ThrowPunch(now); break; case Chapter3BeatingStage.FadeOut: { if (!Guarded(_world.TryFadeOut, out string reason2)) { Abort("the fade out failed: " + reason2); } else { Advance(Chapter3BeatingStage.Move, now + 0.7f); } break; } case Chapter3BeatingStage.Move: { if (ReadHqInside()) { if (!Guarded(_hq.TryExit, out string reason3)) { Abort("the HQ exit failed: " + reason3); break; } _log("Chapter 3 Arthur beating: left the HQ through its own exit."); } if (!GuardedMove(out (float, float, float) wokeAt, out string propertyCode, out string reason4)) { Abort("the move failed: " + reason4); break; } _log($"Chapter 3 Arthur beating: woke at ({wokeAt.Item1:0.0}, {wokeAt.Item2:0.0}, {wokeAt.Item3:0.0}) ({propertyCode})."); _moved = true; Advance(Chapter3BeatingStage.Park, now); break; } case Chapter3BeatingStage.Park: { if (ParkArthur) { _parkArthur(); _log("Chapter 3 Arthur beating: Arthur parked."); } else { _log("Chapter 3 Arthur beating: no park; the pump owns Arthur at the HQ."); } if (!Guarded(_world.TryFadeIn, out string reason10)) { Abort("the fade in failed: " + reason10); } else { Advance(Chapter3BeatingStage.Bill, now + 0.7f); } break; } case Chapter3BeatingStage.Bill: { if (!Guarded(_world.TryOpenBill, out string reason5)) { Abort("the bill failed: " + reason5); break; } _log("Chapter 3 Arthur beating: the hospital bill is open."); Advance(Chapter3BeatingStage.Unlock, now); break; } case Chapter3BeatingStage.Unlock: { if (!Guarded(_world.TryUnlock, out string reason)) { Abort("the unlock failed: " + reason); break; } _log("Chapter 3 Arthur beating: unlocked, exactly as it was."); Advance(Chapter3BeatingStage.Record, now); break; } case Chapter3BeatingStage.Record: _record(); Advance(Chapter3BeatingStage.Nell, now); break; case Chapter3BeatingStage.Nell: _noteNell(); _stage = Chapter3BeatingStage.Done; _log("Chapter 3 Arthur beating: complete; Nell's text is queued."); break; } } private bool ThrowPunch(float now) { if (_arthur.TryPlayAnimationTrigger("Punch", out string reason) != Chapter3ArthurMutationStatus.Succeeded) { Abort($"punch {_punchesDone + 1} animation failed: {reason}"); return false; } if (!Guarded(_world.TryJoltCamera, out string reason2)) { Abort($"punch {_punchesDone + 1} jolt failed: {reason2}"); return false; } float num = _maxHealth * 0.25f; float num2 = ((_startHealth > num) ? Math.Max(num, _startHealth - (_startHealth - num) * (float)(_punchesDone + 1) / 4f) : _startHealth); if (!GuardedSetHealth(num2, out string reason3)) { Abort($"punch {_punchesDone + 1} health set failed: {reason3}"); return false; } _punchesDone++; _log($"Chapter 3 Arthur beating: punch {_punchesDone}/{4} lands at {num2:0} health."); if (_punchesDone >= 4) { Advance(Chapter3BeatingStage.FadeOut, now); } else { _stageDueAt = now + 1.1f; } return true; } private void Advance(Chapter3BeatingStage stage, float dueAt) { _stage = stage; _stageDueAt = dueAt; } private void Abort(string reason) { if (IsDone) { return; } if (_moved) { FinishWithFault(reason); return; } _stage = Chapter3BeatingStage.Aborted; if (!_terminalLogged) { _terminalLogged = true; _log("Chapter 3 Arthur beating aborted: " + reason); } UnlockBestEffort(); } private void FinishWithFault(string reason) { _stage = Chapter3BeatingStage.Faulted; UnlockBestEffort(); try { if (!_world.TryFadeIn(out string reason2)) { _log("Chapter 3 Arthur beating fault fade-in failed: " + reason2); } } catch (Exception ex) { _log("Chapter 3 Arthur beating fault fade-in threw " + ex.GetType().Name + "."); } try { _record(); } catch (Exception ex2) { _log("Chapter 3 Arthur beating fault record threw " + ex2.GetType().Name + "."); } try { _noteNell(); } catch (Exception ex3) { _log("Chapter 3 Arthur beating fault note threw " + ex3.GetType().Name + "."); } _log("Chapter 3 Arthur beating finished with a fault after the wake: " + reason + "; recorded."); } private void UnlockBestEffort() { try { if (!_world.TryUnlock(out string reason)) { _log("Chapter 3 Arthur beating abort unlock failed: " + reason); } } catch (Exception ex) { _log("Chapter 3 Arthur beating abort unlock threw " + ex.GetType().Name + "."); } } private bool Guarded(TryBeatingStep step, out string reason) { reason = "the step did not run."; try { return step(out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + "."; return false; } } private bool GuardedHealth(out float health, out float maxHealth, out string reason) { health = 0f; maxHealth = 0f; reason = "the health read did not run."; try { return _world.TryReadHealth(out health, out maxHealth, out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + "."; return false; } } private bool GuardedSetHealth(float health, out string reason) { reason = "the health set did not run."; try { return _world.TrySetHealth(health, out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + "."; return false; } } private bool GuardedMove(out (float X, float Y, float Z) wokeAt, out string propertyCode, out string reason) { wokeAt = (X: 0f, Y: 0f, Z: 0f); propertyCode = string.Empty; reason = "the move did not run."; try { return _world.TryMoveToWake(out wokeAt, out propertyCode, out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + "."; return false; } } private bool ReadHqInside() { try { return _hq.PlayerInsideHq; } catch { return false; } } } public static class Chapter3ArthurIds { public const string NpcId = "oc_chapter3_arthur"; public const string FirstName = "Arthur"; public const string LastName = "Selby"; } public enum Chapter3ArthurVisitState { Parked, Coming, Arrived, Leaving, AtHq } public enum Chapter3ArthurReadStatus { Ready, Pending, Unavailable, Faulted } public enum Chapter3ArthurMutationStatus { Succeeded, Rejected, Unavailable, Ambiguous } public sealed record Chapter3ArthurSnapshot(bool Present, bool Spawned, bool Visible, bool Moving, bool Conscious, (float X, float Y, float Z) Position, float DistanceToPlayer, int RegisteredCount, float Health = float.NaN, bool InPlayerView = true) { public static Chapter3ArthurSnapshot Absent() { return new Chapter3ArthurSnapshot(Present: false, Spawned: false, Visible: false, Moving: false, Conscious: false, (X: 0f, Y: 0f, Z: 0f), float.NaN, 0); } } public interface IChapter3ArthurWorld { bool HasAuthority { get; } Chapter3ArthurReadStatus TryReadArthur(out Chapter3ArthurSnapshot snapshot); Chapter3ArthurMutationStatus TryMoveOutOfSight(out string reason); Chapter3ArthurMutationStatus TryParkArthur(out string reason); Chapter3ArthurMutationStatus TryVerifyAppearancePaths(out string reason); Chapter3ArthurMutationStatus TryComeArthur(out string reason); Chapter3ArthurMutationStatus TrySayArthur(string text, out string reason); Chapter3ArthurMutationStatus TryLeaveArthur(out string reason); Chapter3ArthurMutationStatus TryFacePlayer(out string reason); Chapter3ArthurMutationStatus TryFacePlayerAtHq(out string reason); Chapter3ArthurMutationStatus TryCaptureHqSpot(out (float X, float Y, float Z) local, out float yawDegrees, out string reason); Chapter3ArthurMutationStatus TryPlaceAtHq(out string reason); Chapter3ArthurMutationStatus TryResumeWalk(out string reason); Chapter3ArthurMutationStatus TryDedupeArthur(out string reason); Chapter3ArthurMutationStatus TryPlayAnimationTrigger(string trigger, out string reason); bool PollArthurImpact(); } public sealed record Chapter3ArthurCandidate((float X, float Y, float Z) Point, float DistanceToPlayer, bool BehindPlayer, bool Reachable); public interface IChapter3ArthurHqBody { Chapter3ArthurMutationStatus TryActivateBody(out string reason); Chapter3ArthurMutationStatus TrySetAgentEnabled(bool enabled, out string reason); Chapter3ArthurMutationStatus TrySetPhysicsHeld(bool held, out string reason); Chapter3ArthurMutationStatus TrySetTransform((float X, float Y, float Z) position, float yawDegrees, out string reason); Chapter3ArthurMutationStatus TrySetVisible(bool visible, out string reason); Chapter3ArthurMutationStatus TryFacePlayerDirect(out string reason); Chapter3ArthurMutationStatus TryReadPlacement(out (float X, float Y, float Z) position, out bool agentEnabled, out string reason); } public static class Chapter3ArthurHqPlacer { private delegate Chapter3ArthurMutationStatus Step(out string reason); public const float PlaceToleranceMetres = 0.5f; public static Chapter3ArthurMutationStatus TryPlace(IChapter3ArthurHqBody? body, (float X, float Y, float Z) spot, float yawDegrees, out string reason) { reason = "the HQ place did not run."; if (body == null) { reason = "the HQ body was not wired up."; return Chapter3ArthurMutationStatus.Unavailable; } Chapter3ArthurMutationStatus chapter3ArthurMutationStatus = Guarded(body.TryActivateBody, out string reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"activate {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetAgentEnabled(enabled: false, out r); }, out reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"agent off {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetPhysicsHeld(held: true, out r); }, out reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"physics hold {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetTransform(spot, yawDegrees, out r); }, out reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"transform {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetVisible(visible: true, out r); }, out reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"visible {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = GuardedRead(body, out (float, float, float) position, out bool agentEnabled, out string reason3); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"read-back {chapter3ArthurMutationStatus}: {reason3}"; return chapter3ArthurMutationStatus; } float num = position.Item1 - spot.X; float num2 = position.Item2 - spot.Y; float num3 = position.Item3 - spot.Z; float num4 = (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3); reason = $"HQ place: requested {Format(spot)}, actual {Format(position)}, agent {(agentEnabled ? "on" : "off")}; {reason3}"; if (!(num4 <= 0.5f)) { return Chapter3ArthurMutationStatus.Ambiguous; } return Chapter3ArthurMutationStatus.Succeeded; } public static Chapter3ArthurMutationStatus TryRestoreForWalk(IChapter3ArthurHqBody? body, out string reason) { reason = "the movement restore did not run."; if (body == null) { reason = "the HQ body was not wired up."; return Chapter3ArthurMutationStatus.Unavailable; } Chapter3ArthurMutationStatus chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetAgentEnabled(enabled: true, out r); }, out string reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"agent on {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } chapter3ArthurMutationStatus = Guarded(delegate(out string r) { return body.TrySetPhysicsHeld(held: false, out r); }, out reason2); if (chapter3ArthurMutationStatus != Chapter3ArthurMutationStatus.Succeeded) { reason = $"physics restore {chapter3ArthurMutationStatus}: {reason2}"; return chapter3ArthurMutationStatus; } reason = "movement restored for the walk: agent on, physics restored."; return Chapter3ArthurMutationStatus.Succeeded; } public static Chapter3ArthurMutationStatus TryFacePlayer(IChapter3ArthurHqBody? body, out string reason) { reason = "the HQ face did not run."; if (body == null) { reason = "the HQ body was not wired up."; return Chapter3ArthurMutationStatus.Unavailable; } return Guarded(body.TryFacePlayerDirect, out reason); } private static string Format((float X, float Y, float Z) point) { return $"({point.X:0.00}, {point.Y:0.00}, {point.Z:0.00})"; } private static Chapter3ArthurMutationStatus Guarded(Step step, out string reason) { reason = "the HQ body step did not run."; try { return step(out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + ": " + ex.Message; return Chapter3ArthurMutationStatus.Ambiguous; } } private static Chapter3ArthurMutationStatus GuardedRead(IChapter3ArthurHqBody body, out (float X, float Y, float Z) position, out bool agentEnabled, out string reason) { position = (X: 0f, Y: 0f, Z: 0f); agentEnabled = true; reason = "the HQ body step did not run."; try { return body.TryReadPlacement(out position, out agentEnabled, out reason); } catch (Exception ex) { reason = "threw " + ex.GetType().Name + ": " + ex.Message; return Chapter3ArthurMutationStatus.Ambiguous; } } } public interface IChapter3HqPresence { bool ChapterActive { get; } bool PlayerInsideHq { get; } } public enum Chapter3ArthurHqForce { Auto, On, Off } public static class Chapter3ArthurHqSpot { public static readonly (float X, float Y, float Z) Local = (X: -2.9f, Y: 0f, Z: 3.7f); public const float YawDegrees = 180f; public const float FaceRangeMetres = 6f; public const float MaintainIntervalSeconds = 0.25f; public static (float X, float Y, float Z) ToWorld((float X, float Y, float Z) pocketRoot, float floorTopY) { return (X: pocketRoot.X + Local.X, Y: pocketRoot.Y + floorTopY, Z: pocketRoot.Z + Local.Z); } public static (float X, float Y, float Z) ToLocal((float X, float Y, float Z) world, (float X, float Y, float Z) pocketRoot) { return (X: world.X - pocketRoot.X, Y: world.Y - pocketRoot.Y, Z: world.Z - pocketRoot.Z); } public static bool IsInsideFrame((float X, float Y, float Z) local) { if (Math.Abs(local.X) <= 7f && Math.Abs(local.Z) <= 5f && local.Y >= -2f) { return local.Y <= 4.5f; } return false; } } public sealed class Chapter3SyndicateHqPresence : IChapter3HqPresence { private readonly Func<bool> _chapterActive; private readonly Func<bool> _playerInsideHq; public bool ChapterActive { get { try { return _chapterActive(); } catch { return false; } } } public bool PlayerInsideHq { get { try { return _playerInsideHq(); } catch { return false; } } } public Chapter3SyndicateHqPresence(Func<bool> chapterActive, Func<bool> playerInsideHq) { _chapterActive = chapterActive ?? throw new ArgumentNullException("chapterActive"); _playerInsideHq = playerInsideHq ?? throw new ArgumentNullException("playerInsideHq"); } } public sealed class Chapter3ArthurNpc : NPC { public override bool IsPhysical => true; protected override void ConfigurePrefab(NPCPrefabBuilder builder) { if (builder != null) { builder.WithIdentity("oc_chapter3_arthur", "Arthur", "Selby"); Chapter3ArthurAppearance.ApplyTo(builder); } } } public interface IChapter3ArthurPhone { bool TryCall(Chapter3ArthurVisitKind kind, IReadOnlyList<string> stages, int chapterDay, out string reason); } public static class Chapter3ArthurPlacement { public const float ComeMinMetres = 20f; public const float ComeMaxMetres = 30f; public const float LeaveMetres = 30f; public static Chapter3ArthurCandidate? SelectComeCandidate(IEnumerable<Chapter3ArthurCandidate> candidates) { if (candidates == null) { return null; } Chapter3ArthurCandidate[] array = candidates.Where((Chapter3ArthurCandidate candidate) => candidate?.Reachable ?? false).ToArray(); if (array.Length == 0) { return null; } return (from candidate in array orderby (!InBand(candidate.DistanceToPlayer)) ? 1 : 0, (!candidate.BehindPlayer) ? 1 : 0, Math.Abs(candidate.DistanceToPlayer - 25f) select candidate).First(); } public static Chapter3ArthurCandidate? SelectLeaveCandidate(IEnumerable<Chapter3ArthurCandidate> candidates) { if (candidates == null) { return null; } Chapter3ArthurCandidate[] array = candidates.Where((Chapter3ArthurCandidate candidate) => candidate?.Reachable ?? false).ToArray(); if (array.Length == 0) { return null; } return (from candidate in array orderby (!candidate.BehindPlayer) ? 1 : 0, Math.Abs(candidate.DistanceToPlayer - 30f) select candidate).First(); } private static bool InBand(float distance) { if (distance >= 20f) { return distance <= 30f; } return false; } } public static class Chapter3ArthurResponseFilter { private static int _impactFlag; public static bool ShouldSuppress(string? npcId) { return string.Equals(npcId, "oc_chapter3_arthur", StringComparison.Ordinal); } public static void NoteImpact() { Interlocked.Exchange(ref _impactFlag, 1); } public static bool PollImpact() { return Interlocked.Exchange(ref _impactFlag, 0) == 1; } } internal static class Chapter3ArthurResponsePatch { public static bool Apply(Harmony harmony, Action<string>? log = null) { if (log == null) { log = delegate { }; } return (byte)(1u & (TryPatch(harmony, log, typeof(NPC), "ReceiveImpact", new Type[1] { typeof(Impact) }, "ReceiveImpactPrefix") ? 1u : 0u) & (TryPatch(harmony, log, typeof(NPCResponses), "ImpactReceived", new Type[1] { typeof(Impact) }, "ImpactReceivedPrefix") ? 1u : 0u) & (TryPatch(harmony, log, typeof(NPCHealth), "TakeDamage", new Type[2] { typeof(float), typeof(bool) }, "TakeDamagePrefix") ? 1u : 0u) & (TryPatch(harmony, log, typeof(NPCHealth), "KnockOut", Type.EmptyTypes, "KnockOutPrefix") ? 1u : 0u) & (TryPatch(harmony, log, typeof(NPCHealth), "Die", Type.EmptyTypes, "DiePrefix") ? 1u : 0u)) != 0; } private static bool TryPatch(Harmony harmony, Action<string> log, Type targetType, string methodName, Type[] argumentTypes, string prefixName) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(targetType, methodName, argumentTypes, (Type[])null); if ((object)methodInfo == null) { log($"Chapter 3 Arthur response patch missed {targetType.Name}.{methodName}; Arthur may react like a native NPC."); return false; } MethodInfo methodInfo2 = AccessTools.Method(typeof(Chapter3ArthurResponsePatch), prefixName, (Type[])null, (Type[])null); if ((object)methodInfo2 == null) { log("Chapter 3 Arthur response patch missed its own " + prefixName + "."); return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { log($"Chapter 3 Arthur response patch failed on {targetType.Name}.{methodName}: {ex.GetType().Name}."); return false; } } private static bool ReceiveImpactPrefix(NPC __instance, Impact __0) { if (!IsArthurNpc(__instance)) { return true; } Chapter3ArthurResponseFilter.NoteImpact(); return false; } private static bool ImpactReceivedPrefix(NPCResponses __instance, Impact __0) { if (!IsArthurResponses(__instance)) { return true; } Chapter3ArthurResponseFilter.NoteImpact(); return false; } private static void TakeDamagePrefix(NPCHealth __instance, ref float __0) { if (IsArthurHealth(__instance)) { __0 = 0f; } } private static bool KnockOutPrefix(NPCHealth __instance) { return !IsArthurHealth(__instance); } private static bool DiePrefix(NPCHealth __instance) { return !IsArthurHealth(__instance); } private static bool IsArthurNpc(NPC? npc) { try { return npc != null && Chapter3ArthurResponseFilter.ShouldSuppress(npc.ID); } catch { return false; } } private static bool IsArthurHealth(NPCHealth? health) { try { return health != null && IsArthurNpc(health._npc); } catch { return false; } } private static bool IsArthurResponses(NPCResponses? responses) { try { return responses != null && IsArthurNpc(responses.npc); } catch { return false; } } } public sealed record Chapter3ArthurVisitConditions(bool OnFoot, bool Outdoors, bool ClearOfPursuit, string Detail) { public bool Met { get { if (OnFoot && Outdoors) { return ClearOfPursuit; } return false; } } public static Chapter3ArthurVisitConditions Clear(string detail = "on foot, outdoors, clear of pursuit") { return new Chapter3ArthurVisitConditions(OnFoot: true, Outdoors: true, ClearOfPursuit: true, detail); } public static Chapter3ArthurVisitConditions Blocked(bool onFoot, bool outdoors, bool clearOfPursuit, string detail) { return new Chapter3ArthurVisitConditions(onFoot, outdoors, clearOfPursuit, detail); } } public interface IChapter3ArthurVisitConditions { bool TryRead(out Chapter3ArthurVisitConditions conditions); } public enum Chapter3ArthurVisitOutcome { Parked, Arrived, SpawnPending, ReadFailed, BoundExpired, WalkReissued, HqPlaced, Attacked } public sealed record Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome Outcome, IReadOnlyList<string> Lines); public sealed class Chapter3ArthurVisitPump { public const float RetryIntervalSeconds = 1f; public const float MaxSeconds = 90f; public const float LeaveSeconds = 30f; public const float ComeSeconds = 30f; public const float ArrivedMetres = 2.5f; public const float LeaveParkMetres = 25f; public const float WalkGraceSeconds = 1f; public const int MaxWalkResumes = 2; public const float HqAttemptIntervalSeconds = 1f; public const float AttackDropHealth = 1f; public const float AttackIncidentSeconds = 10f; private const int MaxDetailLength = 160; private readonly Func<float> _realtimeSinceStartup; private bool _reconcilePending; private bool _failureReported; private bool _verifyDone; private bool _preSpawnMoveLogged; private float _deadline; private readonly IntervalGate _retryGate; private float _leaveDeadline; private float _comeDeadline; private float _walkIssuedAt; private int _resumesLeft = 2; private readonly IntervalGate _hqAttemptGate; private readonly IntervalGate _hqMaintainGate; private bool _hqInteriorAbsent; private float _attackBaseline = float.NaN; private float _attackLastHealth = float.NaN; private float _attackLastDropAt; private bool _attackIncidentOpen; public Chapter3ArthurVisitState State { get; private set; } public IChapter3HqPresence? HqPresence { get; set; } public Chapter3ArthurHqForce HqForce { get; set; } public Chapter3ArthurVisitPump(Func<float> realtimeSinceStartup) { _realtimeSinceStartup = realtimeSinceStartup ?? throw new ArgumentNullException("realtimeSinceStartup"); _retryGate = new IntervalGate(1f); _hqAttemptGate = new IntervalGate(1f); _hqMaintainGate = new IntervalGate(0.25f); } public void BeginAfterLoad() { State = Chapter3ArthurVisitState.Parked; float num = _realtimeSinceStartup(); _reconcilePending = true; _failureReported = false; _verifyDone = false; _preSpawnMoveLogged = false; _deadline = num + 90f; _retryGate.Reset(num); _resumesLeft = 2; _hqAttemptGate.Reset(num); _hqMaintainGate.Reset(num); _hqInteriorAbsent = false; _attackBaseline = float.NaN; _attackLastHealth = float.NaN; _attackIncidentOpen = false; } public bool BeginCome() { if (State != Chapter3ArthurVisitState.Parked && State != Chapter3ArthurVisitState.AtHq) { return false; } State = Chapter3ArthurVisitState.Coming; _comeDeadline = (_walkIssuedAt = _realtimeSinceStartup()) + 30f; _resumesLeft = 2; return true; } public bool BeginLeave() { if (State != Chapter3ArthurVisitState.Coming && State != Chapter3ArthurVisitState.Arrived) { return false; } State = Chapter3ArthurVisitState.Leaving; _leaveDeadline = (_walkIssuedAt = _realtimeSinceStartup()) + 30f; _resumesLeft = 2; return true; } public void ParkAfterBeating() { State = Chapter3ArthurVisitState.Parked; _resumesLeft = 2; _attackBaseline = float.NaN; _attackLastHealth = float.NaN; _attackIncidentOpen = false; } public void CancelLoadReconcile() { _reconcilePending = false; } public Chapter3ArthurVisitStep? Pump(IChapter3ArthurWorld? world) { if (world == null || !world.HasAuthority) { return null; } if (_reconcilePending) { return PumpReconcile(world); } bool flag = WantHq(); Chapter3ArthurVisitState state = State; if ((uint)(state - 1) <= 2u) { if (flag) { return PumpEndVisitForHq(world); } return State switch { Chapter3ArthurVisitState.Coming => PumpComing(world), Chapter3ArthurVisitState.Arrived => PumpArrived(world), _ => PumpLeaving(world), }; } if (State == Chapter3ArthurVisitState.AtHq) { if (!flag) { State = Chapter3ArthurVisitState.Parked; string reason; Chapter3ArthurMutationStatus value = world.TryParkArthur(out reason); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"left the HQ spot; park {value}: {reason}" }); } return PumpAtHq(world); } if (flag) { return PumpParkedWantHq(world); } world.PollArthurImpact(); return null; } private Chapter3ArthurVisitStep? PumpReconcile(IChapter3ArthurWorld world) { float num = _realtimeSinceStartup(); if (num >= _deadline) { _reconcilePending = false; return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.BoundExpired, new string[1] { $"gave up after {90f} seconds; Arthur never spawned, left untouched." }); } if (!_retryGate.TryPass(num)) { return null; } Chapter3ArthurSnapshot snapshot; Chapter3ArthurReadStatus chapter3ArthurReadStatus = TryRead(world, out snapshot); if (chapter3ArthurReadStatus != Chapter3ArthurReadStatus.Ready || (object)snapshot == null) { return ReportFailureOnce($"the Arthur read status was {chapter3ArthurReadStatus}."); } if (!snapshot.Present) { return ReportFailureOnce("no Arthur contact constructed yet."); } List<string> list = new List<string>(); if (!_verifyDone) { _verifyDone = true; string reason; Chapter3ArthurMutationStatus value = world.TryVerifyAppearancePaths(out reason); list.Add($"appearance paths {value}: {reason}"); } if (!snapshot.Spawned) { string reason2; Chapter3ArthurMutationStatus value2 = world.TryMoveOutOfSight(out reason2); if (!_preSpawnMoveLogged) { _preSpawnMoveLogged = true; list.Add($"pre-spawn move {value2}: {reason2}"); } if (list.Count == 0) { return null; } return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.SpawnPending, list); } _reconcilePending = false; State = Chapter3ArthurVisitState.Parked; if (snapshot.RegisteredCount > 1) { string reason3; Chapter3ArthurMutationStatus value3 = world.TryDedupeArthur(out reason3); list.Add($"dedupe {value3}: {reason3}"); } string reason4; Chapter3ArthurMutationStatus value4 = world.TryParkArthur(out reason4); list.Add($"park {value4}: {reason4}"); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, list); } private Chapter3ArthurVisitStep? ReportFailureOnce(string line) { if (_failureReported) { return null; } _failureReported = true; return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.ReadFailed, new string[1] { Truncate(line) }); } private Chapter3ArthurVisitStep? PumpComing(IChapter3ArthurWorld world) { float num = _realtimeSinceStartup(); if (TryRead(world, out Chapter3ArthurSnapshot snapshot) != Chapter3ArthurReadStatus.Ready || (object)snapshot == null || !snapshot.Present || !snapshot.Spawned) { return null; } Chapter3ArthurVisitStep chapter3ArthurVisitStep = CheckAttacked(snapshot, "visit", num, world.PollArthurImpact()); if ((object)chapter3ArthurVisitStep != null) { return chapter3ArthurVisitStep; } if (num < _walkIssuedAt + 1f) { return null; } if (snapshot.DistanceToPlayer <= 2.5f) { State = Chapter3ArthurVisitState.Arrived; string reason; Chapter3ArthurMutationStatus value = world.TryFacePlayer(out reason); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Arrived, new string[1] { $"arrived {snapshot.DistanceToPlayer:0.0} m from the player; face {value}: {reason}" }); } if (num >= _comeDeadline) { State = Chapter3ArthurVisitState.Arrived; string reason2; Chapter3ArthurMutationStatus value2 = world.TryFacePlayer(out reason2); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Arrived, new string[1] { $"come bound expired after {30f} seconds; stopped and faced the player where he is ({snapshot.DistanceToPlayer:0.0} m); face {value2}: {reason2}" }); } if (!snapshot.Moving && _resumesLeft > 0) { _resumesLeft--; string reason3; Chapter3ArthurMutationStatus value3 = world.TryResumeWalk(out reason3); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.WalkReissued, new string[1] { $"stopped {snapshot.DistanceToPlayer:0.0} m out; walk re-issue {value3}: {reason3} ({_resumesLeft} left)" }); } return null; } private Chapter3ArthurVisitStep? PumpArrived(IChapter3ArthurWorld world) { if (TryRead(world, out Chapter3ArthurSnapshot snapshot) != Chapter3ArthurReadStatus.Ready || (object)snapshot == null || !snapshot.Present || !snapshot.Spawned) { return null; } return CheckAttacked(snapshot, "visit", _realtimeSinceStartup(), world.PollArthurImpact()); } private Chapter3ArthurVisitStep? PumpLeaving(IChapter3ArthurWorld world) { float num = _realtimeSinceStartup(); Chapter3ArthurSnapshot snapshot; bool flag = TryRead(world, out snapshot) == Chapter3ArthurReadStatus.Ready && (object)snapshot != null && snapshot.Present && snapshot.Spawned; if (flag) { Chapter3ArthurVisitStep chapter3ArthurVisitStep = CheckAttacked(snapshot, "visit", num, world.PollArthurImpact()); if ((object)chapter3ArthurVisitStep != null) { return chapter3ArthurVisitStep; } if (num < _walkIssuedAt + 1f) { return null; } if (snapshot.DistanceToPlayer >= 25f) { State = Chapter3ArthurVisitState.Parked; string reason; Chapter3ArthurMutationStatus value = world.TryParkArthur(out reason); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"reached {snapshot.DistanceToPlayer:0.0} m from the player; park {value}: {reason}" }); } if (!snapshot.InPlayerView) { State = Chapter3ArthurVisitState.Parked; string reason2; Chapter3ArthurMutationStatus value2 = world.TryParkArthur(out reason2); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"out of the player's view; park {value2}: {reason2}" }); } } if (num >= _leaveDeadline) { State = Chapter3ArthurVisitState.Parked; string reason3; Chapter3ArthurMutationStatus value3 = world.TryParkArthur(out reason3); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"leave bound expired; park {value3}: {reason3}" }); } if (flag && num >= _walkIssuedAt + 1f && !snapshot.Moving && _resumesLeft > 0) { _resumesLeft--; string reason4; Chapter3ArthurMutationStatus value4 = world.TryResumeWalk(out reason4); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.WalkReissued, new string[1] { $"stopped {snapshot.DistanceToPlayer:0.0} m out; walk re-issue {value4}: {reason4} ({_resumesLeft} left)" }); } return null; } private Chapter3ArthurVisitStep? PumpEndVisitForHq(IChapter3ArthurWorld world) { string reason; Chapter3ArthurMutationStatus value = world.TryParkArthur(out reason); string reason2; Chapter3ArthurMutationStatus chapter3ArthurMutationStatus = world.TryPlaceAtHq(out reason2); if (chapter3ArthurMutationStatus == Chapter3ArthurMutationStatus.Succeeded) { State = Chapter3ArthurVisitState.AtHq; _hqInteriorAbsent = false; _hqMaintainGate.Reset(_realtimeSinceStartup() + 0.25f); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.HqPlaced, new string[1] { $"visit ended: the player entered the HQ; park {value}: {reason}; place {chapter3ArthurMutationStatus}: {reason2}" }); } State = Chapter3ArthurVisitState.Parked; _hqAttemptGate.Reset(_realtimeSinceStartup() + 1f); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"visit ended: the player entered the HQ; park {value}: {reason}; HQ place {chapter3ArthurMutationStatus}: {reason2}" }); } private Chapter3ArthurVisitStep? PumpParkedWantHq(IChapter3ArthurWorld world) { float num = _realtimeSinceStartup(); if (!_hqAttemptGate.TryPass(num)) { return null; } if (TryRead(world, out Chapter3ArthurSnapshot snapshot) != Chapter3ArthurReadStatus.Ready || (object)snapshot == null || !snapshot.Present || !snapshot.Spawned) { return null; } if (world.TryPlaceAtHq(out string reason) != Chapter3ArthurMutationStatus.Succeeded) { return null; } State = Chapter3ArthurVisitState.AtHq; _hqInteriorAbsent = false; _hqMaintainGate.Reset(num + 0.25f); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.HqPlaced, new string[1] { "placed at the HQ spot: " + reason }); } private Chapter3ArthurVisitStep? PumpAtHq(IChapter3ArthurWorld world) { float num = _realtimeSinceStartup(); if (!_hqMaintainGate.TryPass(num)) { return null; } if (TryRead(world, out Chapter3ArthurSnapshot snapshot) != Chapter3ArthurReadStatus.Ready || (object)snapshot == null) { return null; } if (!snapshot.Present || !snapshot.Spawned) { State = Chapter3ArthurVisitState.Parked; string reason; Chapter3ArthurMutationStatus value = world.TryParkArthur(out reason); return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Parked, new string[1] { $"lost the HQ contact; park {value}: {reason}" }); } Chapter3ArthurVisitStep chapter3ArthurVisitStep = CheckAttacked(snapshot, "HQ", num, world.PollArthurImpact()); if ((object)chapter3ArthurVisitStep != null) { return chapter3ArthurVisitStep; } if (world.TryPlaceAtHq(out string reason2) == Chapter3ArthurMutationStatus.Succeeded) { Chapter3ArthurVisitStep result = null; if (_hqInteriorAbsent) { _hqInteriorAbsent = false; result = new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.HqPlaced, new string[1] { "re-placed after the interior rebuild: " + reason2 }); } if (PlayerInside() && snapshot.DistanceToPlayer <= 6f) { world.TryFacePlayerAtHq(out string _); } return result; } if (_hqInteriorAbsent) { return null; } _hqInteriorAbsent = true; return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.HqPlaced, new string[1] { "the HQ interior is gone; holding position (" + reason2 + ")" }); } private Chapter3ArthurVisitStep? CheckAttacked(Chapter3ArthurSnapshot snapshot, string context, float now, bool impactObserved) { if (float.IsNaN(snapshot.Health)) { return null; } if (float.IsNaN(_attackBaseline)) { _attackBaseline = snapshot.Health; _attackLastHealth = snapshot.Health; return null; } if (impactObserved) { _attackLastDropAt = now; _attackLastHealth = snapshot.Health; if (_attackIncidentOpen) { return null; } _attackIncidentOpen = true; return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Attacked, new string[2] { "Chapter 3 Arthur was attacked (" + context + ").", "Chapter 3 Arthur native reaction suppressed (ImpactReceived)." }); } if (snapshot.Health < _attackLastHealth - 1f) { _attackLastDropAt = now; _attackLastHealth = snapshot.Health; if (_attackIncidentOpen) { return null; } _attackIncidentOpen = true; return new Chapter3ArthurVisitStep(Chapter3ArthurVisitOutcome.Attacked, new string[1] { "Chapter 3 Arthur was attacked (" + context + ")." }); } _attackLastHealth = snapshot.Health; if (snapshot.Health > _attackBaseline) { _attackBaseline = snapshot.Health; } if (snapshot.Health >= _attackBaseline) { _attackIncidentOpen = false; return null; } if (_attackIncidentOpen && now - _attackLastDropAt >= 10f) { _attackIncidentOpen = false; _attackBaseline = snapshot.Health; } return null; } private bool WantHq() { if (HqForce == Chapter3ArthurHqForce.On) { return true; } if (HqForce == Chapter3ArthurHqForce.Off) { return false; } try { return HqPresence != null && HqPresence.ChapterActive && HqPresence.PlayerInsideHq; } catch { return false; } } private bool PlayerInside() { if (HqForce == Chapter3ArthurHqForce.On) { return true; } if (HqForce == Chapter3ArthurHqForce.Off) { return false; } try { return HqPresence != null && HqPresence.PlayerInsideHq; } catch { return false; } } private static Chapter3ArthurReadStatus TryRead(IChapter3ArthurWorld world, out Chapter3ArthurSnapshot? snapshot) { try { return world.TryReadArthur(out snapshot); } catch { snapshot = null; return Chapter3ArthurReadStatus.Faulted; } } private static string Truncate(string text) { if (text == null) { text = string.Empty; } if (text.Length > 160) { return text.Substring(0, 160); } return text; } } public interface IChapter3ArthurSpeedControl { bool TryReadSpeeds(out float moveSpeed, out float agentSpeed); bool TryAddBoost(float boostedSpeed, out float agentSpeedAfter); bool TryRemoveBoost(); } public static class Chapter3ArthurWalkSpeed { public static string BeginWalk(IChapter3ArthurSpeedControl? control, bool isSpawned) { if (!isSpawned || control == null) { return string.Empty; } control.TryRemoveBoost(); if (!control.TryReadSpeeds(out var moveSpeed, out var agentSpeed) || !IsUsableBaseline(moveSpeed)) { return "walk speed left native: no usable baseline."; } float boostedSpeed = moveSpeed * 1.35f; if (!control.TryAddBoost(boostedSpeed, out var agentSpeedAfter)) { return "walk speed left native: the boost refused."; } return $"Arthur walk speed: {agentSpeed:0.00} -> {agentSpeedAfter:0.00} m/s (x{1.35f:0.00})."; } public static void EndWalk(IChapter3ArthurSpeedControl? control, bool isSpawned) { if (isSpawned) { control?.TryRemoveBoost(); } } public static string DescribeArrival(float distanceMetres, float elapsedSeconds) { if (elapsedSeconds > 0f) { return $"come walk averaged {distanceMetres / elapsedSeconds:0.00} m/s over {distanceMetres:0.0} m in {elapsedSeconds:0.0} s."; } return $"come walk covered {distanceMetres:0.0} m in no measurable time."; } private static bool IsUsableBaseline(float baseline) { if (float.IsFinite(baseline)) { return baseline > 0f; } return false; } } public interface IChapter3BeatProbeWorld { bool IsHost { get; } bool TryReadSnapshot(out Chapter3BeatSnapshot snapshot, out string reason); bool TryReadInvincible(out bool on, out string reason); bool TrySetInvincible(bool on, out string reason); } public sealed record Chapter3BeatSlot(string Id, int Quantity); public sealed record Chapter3BeatSnapshot(float Health, float MaxHealth, bool IsAlive, bool IsInvincible, float Cash, float Bank, IReadOnlyList<Chapter3BeatSlot> Slots, float X, float Y, float Z, int ElapsedDays, string TimeText, string Pursuit) { public int SlotCount => Slots.Count; } public static class Chapter3BeatProbe { public const int KnockoutTimeoutSeconds = 60; public const int MinHurtPercent = 1; public const int MaxHurtPercent = 100; public static readonly IReadOnlyList<string> KnownTriggers = new string[4] { "Punch", "Shoot", "Nod", "GrabItem" }; public static bool TryParsePercent(string? text, out int percent) { percent = 0; if (!int.TryParse(text?.Trim(), out var result)) { return false; } if (result < 1 || result > 100) { return false; } percent = result; return true; } public static IReadOnlyList<string> DescribeSnapshot(Chapter3BeatSnapshot snapshot) { return new List<string> { $"health: {snapshot.Health:0} / {snapshot.MaxHealth:0} ({(snapshot.IsAlive ? "alive" : "dead")}, invincible {(snapshot.IsInvincible ? "on" : "off")})", $"cash: ${snapshot.Cash:0}, bank: ${snapshot.Bank:0}", DescribeSlots(snapshot.Slots), $"position: ({snapshot.X:0.0}, {snapshot.Y:0.0}, {snapshot.Z:0.0})", $"time: {snapshot.TimeText}, day {snapshot.ElapsedDays}", "pursuit: " + snapshot.Pursuit }; } public static string DescribeSlots(IReadOnlyList<Chapter3BeatSlot> slots) { if (slots.Count == 0) { return "inventory: 0 slots"; } string value = string.Join(", ", slots.Select((Chapter3BeatSlot slot) => $"{slot.Id} x{slot.Quantity}")); return $"inventory: {slots.Count} slots ({value})"; } public static IReadOnlyList<string> DescribeDiff(Chapter3BeatSnapshot before, Chapter3BeatSnapshot after, bool billShown, bool timedOut) { int value = after.ElapsedDays - before.ElapsedDays; float value2 = after.Cash - before.Cash; float value3 = after.Bank - before.Bank; return new string[7] { timedOut ? $"knockout timed out after {60} s with no revive." : "knockout complete: the player revived through the native flow.", $"woke at ({after.X:0.0}, {after.Y:0.0}, {after.Z:0.0}) from ({before.X:0.0}, {before.Y:0.0}, {before.Z:0.0}).", $"time {before.TimeText} day {before.ElapsedDays} -> {after.TimeText} day {after.ElapsedDays} ({value:+0;-0;+0} days).", $"cash ${before.Cash:0} -> ${after.Cash:0} ({value2:+0;-0;+0}), bank ${before.Bank:0} -> ${after.Bank:0} ({value3:+0;-0;+0}).", "inventory: " + DescribeInventoryChange(before.Slots, after.Slots), $"health after: {after.Health:0} / {after.MaxHealth:0} ({(after.IsAlive ? "alive" : "dead")}).", "bill screen showed: " + (billShown ? "yes" : "no") + "." }; } public static string DescribeInventoryChange(IReadOnlyList<Chapter3BeatSlot> before, IReadOnlyList<Chapter3BeatSlot> after) { Dictionary<string, int> dictionary = (from slot in before group slot by slot.Id).ToDictionary((IGrouping<string, Chapter3BeatSlot> group) => group.Key, (IGrouping<string, Chapter3BeatSlot> group) => group.Sum((Chapter3BeatSlot slot) => slot.Quantity)); Dictionary<string, int> dictionary2 = (from slot in after group slot by slot.Id).ToDictionary((IGrouping<string, Chapter3BeatSlot> group) => group.Key, (IGrouping<string, Chapter3BeatSlot> group) => group.Sum((Chapter3BeatSlot slot) => slot.Quantity)); List<string> list = new List<string>(); foreach (string item in dictionary.Keys.Union(dictionary2.Keys).OrderBy<string, string>((string id) => id, StringComparer.Ordinal)) { dictionary.TryGetValue(item, out var value); dictionary2.TryGetValue(item, out var value2); if (value != value2) { list.Add($"{item} {value} -> {value2}"); } } if (list.Count != 0) { return $"{before.Count} -> {after.Count} slots ({string.Join(", ", list)})"; } return $"{before.Count} slots, unchanged"; } } public sealed class Chapter3BeatInvincibleProbe { private readonly IChapter3BeatProbeWorld _world; private bool _setByProbe; public Chapter3BeatInvincibleProbe(IChapter3BeatProbeWorld world) { _world = world ?? throw new ArgumentNullException("world"); } public IReadOnlyList<string> TurnOn() { if (_world.TryReadInvincible(out bool on, out string reason)) { if (!on) { if (_world.TrySetInvincible(on: true, out reason)) { _setByProbe = true; return new string[1] { "Chapter 3 QA beat probe invincible: on." }; } return new string[1] { "Chapter 3 QA beat probe invincible refused: " + reason }; } return new string[1] { "Chapter 3 QA beat probe invincible: already on, left as found; this probe will not turn it off." }; } return new string[1] { "Chapter 3 QA beat probe invincible refused: " + reason }; } public IReadOnlyList<string> TurnOff() { if (!_setByProbe) { if (_world.TryReadInvincible(out bool on, out string reason)) { if (on) { return new string[1] { "Chapter 3 QA beat probe invincible refused: on, but not by this probe; left on." }; } return new string[1] { "Chapter 3 QA beat probe invincible: already off." }; } return new string[1] { "Chapter 3 QA beat probe invincible refused: " + reason }; } if (_world.TrySetInvincible(on: false, out string reason2)) { _setByProbe = false; return new string[1] { "Chapter 3 QA beat probe invincible: off, restored." }; } return new string[1] { "Chapter 3 QA beat probe invincible refused: " + reason2 }; } } public enum Chapter3BindState { Binding, Bound } public sealed record Chapter3CheckpointOfficerEntry(int RosterHandle, Chapter3BindState BindState, string ActiveBehaviour, double? DistanceM); public sealed record Chapter3CheckpointStatusEntry(string Label, string? WorldHandle, bool Spawned, IReadOnlyList<Chapter3CheckpointOfficerEntry> Officers); public sealed record Chapter3CheckpointSnapshot(LocalPressureTier? Tier, bool Protected, Chapter3CheckpointTarget Target, IReadOnlyList<string> ActiveLabels, IReadOnlyList<Chapter3CheckpointStatusEntry> Managed, IReadOnlyList<Chapter3CheckpointStatusEntry> Tests, string? AssertedNative); public interface IChapter3CheckpointHost { Chapter3State? State { get; } Chapter3CheckpointTarget ReadCheckpointTarget(); bool TryWriteCheckpointTarget(Chapter3CheckpointTarget target); } public sealed class Chapter3CheckpointService : IDisposable { private sealed class ManagedCheckpoint { public string Label { get; set; } = string.Empty; public string WorldHandle { get; set; } = string.Empty; public Chapter3CheckpointSpot Spot { get; set; } = new Chapter3CheckpointSpot(string.Empty, 0f, 0f, 0f, 0f); public string OwnerPrefix { get; set; } = "checkpoint"; public int[] OfficerHandles { get; set; } = new int[2] { -1, -1 }; public Chapter3BindState[] OfficerBindState { get; set; } = new Chapter3BindState[2]; public string[] OfficerActiveName { get; set; } = new string[2] { "unknown", "unknown" }; public double?[] OfficerDistanceM { get; set; } = new double?[2]; public double?[] BindStartMinutes { get; set; } = new double?[2]; public IReadOnlyList<Chapter3StandPoint>? StandPoints { get; set; } } private const double PickHourMinutes = 360.0; internal const int OfficersPerCheckpoint = 2; private const int NoHandle = -1; private const string ManagedOwnerPrefix = "checkpoint"; private const string TestOwnerPrefix = "checkpoint-test"; private const double BindNearMeters = 3.0; private const double BindTimeoutGameMinutes = 20.0; private readonly IChapter3CheckpointHost _runtime; private readonly Release1ChiefService _chief; private readonly IProductionActivityMissionWorld _world; private readonly IChapter3CheckpointWorld _checkpoints; private readonly OcOfficerRosterService _roster; private readonly Action<string> _log; private readonly GameHourGate _throttle = new GameHourGate(); private readonly Dictionary<string, ManagedCheckpoint> _managed = new Dictionary<string, ManagedCheckpoint>(StringComparer.Ordinal); private readonly Dictionary<string, ManagedCheckpoint> _tests = new Dictionary<string, ManagedCheckpoint>(StringComparer.Ordinal); private readonly OnceLog _warned; private IReadOnlyList<string> _activeLabels = Array.Empty<string>(); private double? _lastStructuralMinute; private string? _assertedNative; private bool _needsPostLoadPick; private bool _disposed; public Chapter3CheckpointService(IChapter3CheckpointHost runtime, Release1ChiefService chief, IProductionActivityMissionWorld world, IChapter3CheckpointWorld checkpoints, OcOfficerRosterService roster, Action<string>? log = null) { _runtime = runtime ?? throw new ArgumentNullException("runtime"); _chief = chief ?? throw new ArgumentNullException("chief"); _world = world ?? throw new ArgumentNullException("world"); _checkpoints = checkpoints ?? throw new ArgumentNullException("checkpoints"); _roster = roster ?? throw new ArgumentNullException("roster"); _log = log ?? ((Action<string>)delegate { }); _warned = new OnceLog(_log); } public void Update() { if (_disposed || !IsHost()) { return; } double totalMinutes; double? num = ((_world.TryReadCanonicalTotalMinutes(out totalMinutes) == Release1SmallCourtesyWorldReadStatus.Ready) ? new double?(totalMinutes) : ((double?)null)); if (!_throttle.TryPass(num, _runtime.State?.Revision ?? (-1))) { return; } int? obj; if (num.HasValue) { double valueOrDefault = num.GetValueOrDefault(); obj = (int)Math.Floor(valueOrDefault / 1440.0); } else { obj = null; } int? today = obj; MaybePick(num, today); Reconcile(num); foreach (ManagedCheckpoint value in _tests.Values) { EnsureOfficers(value, num); } } public void OnSaveStart() { if (!_disposed) { _throttle.Clear(); } } public void OnSaveComplete() { if (!_disposed) { _throttle.Clear(); MarkBindsStale(_managed); MarkBindsStale(_tests); } } private static void MarkBindsStale(Dictionary<string, ManagedCheckpoint> set) { foreach (ManagedCheckpoint value in set.Values) { for (int i = 0; i < 2; i++) { value.OfficerBindState[i] = Chapter3BindState.Binding; value.BindStartMinutes[i] = null; value.OfficerActiveName[i] = "unknown"; value.OfficerDistanceM[i] = null; } } } public void OnPreLoad() { if (!_disposed) { ClearLocal(); } } public void OnLoadComplete() { if (!_disposed) { _throttle.Clear(); _needsPostLoadPick = true; } } public void Dispose() { if (!_disposed) { _disposed = true; ClearLocal(); } } public Chapter3QaResult QaSpawnTest(string label) { if (_disposed) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: Chapter 3 is not active."); } if (string.IsNullOrWhiteSpace(label)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: a label is required."); } if (!IsHost()) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: not host."); } Chapter3CheckpointSpot chapter3CheckpointSpot = Chapter3CheckpointSpots.Baked.FirstOrDefault((Chapter3CheckpointSpot entry) => string.Equals(entry.Label, label, StringComparison.Ordinal)); if ((object)chapter3CheckpointSpot == null) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: unknown spot '" + label + "'."); } if (_tests.ContainsKey(label)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: a test checkpoint '" + label + "' already exists."); } if (!TrySpawnInto(_tests, "checkpoint-test", label, chapter3CheckpointSpot)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: the spawn failed for '" + label + "'."); } return new Chapter3QaResult(Ok: true, "Test checkpoint spawned: " + label + "."); } public Chapter3QaResult QaSpawnTestAt(string label, float x, float y, float z, float yawDegrees) { if (_disposed) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: Chapter 3 is not active."); } if (string.IsNullOrWhiteSpace(label)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: a label is required."); } if (!IsHost()) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: not host."); } if (_tests.ContainsKey(label)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: a test checkpoint '" + label + "' already exists."); } if (!TrySpawnInto(_tests, "checkpoint-test", label, new Chapter3CheckpointSpot(label, x, y, z, yawDegrees))) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint test refused: the spawn failed for '" + label + "'."); } return new Chapter3QaResult(Ok: true, "Test checkpoint spawned: " + label + "."); } public Chapter3QaResult QaSpotHere(string label) { if (_disposed) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint spot here refused: Chapter 3 is not active."); } if (string.IsNullOrWhiteSpace(label)) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint spot here refused: a label is required."); } if (!IsHost()) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint spot here refused: not host."); } if (TryReadCameraPose(out Chapter3CameraPose pose) != Chapter3CheckpointWorldStatus.Ok) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint spot here refused: the camera pose could not be read."); } string text = string.Format(CultureInfo.InvariantCulture, "new Chapter3CheckpointSpot(\"{0}\", {1:F2}f, {2:F2}f, {3:F2}f, {4:F2}f),", label, pose.X, pose.Y, pose.Z, pose.YawDegrees); Chapter3QaResult chapter3QaResult = QaSpawnTestAt(label, pose.X, pose.Y, pose.Z, pose.YawDegrees); return new Chapter3QaResult(chapter3QaResult.Ok, chapter3QaResult.Ok ? (text + " Test checkpoint spawned: " + label + ".") : (text + " Test checkpoint spawn failed: " + chapter3QaResult.Message)); } public Chapter3QaResult QaClearTests() { if (_disposed) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint clear refused: Chapter 3 is not active."); } int num = 0; List<string> list = new List<string>(); foreach (string item in _tests.Keys.OrderBy<string, string>((string label) => label, StringComparer.Ordinal).ToList()) { if (TryRemoveFrom(_tests, item)) { num++; } else { list.Add(item); } } if (list.Count != 0) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint clear refused: " + string.Join(", ", list) + " could not be removed."); } return new Chapter3QaResult(Ok: true, $"Cleared {num} test checkpoint(s)."); } public Chapter3QaResult QaPickNow() { if (_disposed) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint pick refused: Chapter 3 is not active."); } if (!IsHost()) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint pick refused: not host."); } if (_world.TryReadCanonicalTotalMinutes(out var totalMinutes) != Release1SmallCourtesyWorldReadStatus.Ready) { return new Chapter3QaResult(Ok: false, "Chapter 3 QA checkpoint pick refused: the clock could not be read."); } return PickCore((int)Math.Floor(totalMinutes / 1440.0)); } public IReadOnlyList<string> QaStatus() { try { return QaStatusCore(); } catch (Exception ex) { return new string[1] { "checkpoint status unavailable: " + ex.GetType().Name + "." }; } } public Chapter3CheckpointSnapshot Snapshot() { if (_disposed) { return new Chapter3CheckpointSnapshot(null, Protected: false, new Chapter3CheckpointTarget(null, null), Array.Empty<string>(), Array.Empty<Chapter3CheckpointStatusEntry>(), Array.Empty<Chapter3CheckpointStatusEntry>(), null); } return new Chapter3CheckpointSnapshot(_chief.ReadTier(), _chief.IsProtected(), _runtime.ReadCheckpointTarget(), _activeLabels, StatusEntries(_managed), StatusEntries(_tests), _assertedNative); } private IReadOnlyList<string> QaStatusCore() { if (_disposed) { return new string[1] { "checkpoints: the service is disposed." }; } Chapter3CheckpointSnapshot chapter3CheckpointSnapshot = Snapshot(); string value = chapter3CheckpointSnapshot.Target.PropertyCode ?? "none"; int? pickDay = chapter3CheckpointSnapshot.Target.PickDay; object obj; if (pickDay.HasValue) { int valueOrDefault = pickDay.GetValueOrDefault(); obj = $" (day {valueOrDefault})"; } else { obj = string.Empty; } string value2 = (string)obj; List<string> list = new List<string> { $"checkpoints: tier {chapter3CheckpointSnapshot.Tier?.ToString() ?? "unknown"}, protected {(chapter3CheckpointSnapshot.Protected ? "yes" : "no")}, target {value}{value2}.", "checkpoints: active " + ((chapter3CheckpointSnapshot.ActiveLabels.Count == 0) ? "none" : string.Join(", ", chapter3CheckpointSnapshot.ActiveLabels)) + "." }; foreach (Chapter3CheckpointStatusEntry item in chapter3CheckpointSnapshot.Managed) { list.AddRange(DescribeEntry(item, test: false)); list.AddRange(DescribeOfficerDiagnostics(item)); } foreach (Chapter3CheckpointStatusEntry test in chapter3CheckpointSnapshot.Tests) { list.AddRange(DescribeEntry(test, test: true)); list.AddRange(DescribeOfficerDiagnostics(test)); } list.Add((chapter3CheckpointSnapshot.AssertedNative == null) ? "checkpoints: no native asserted." : ("checkpoints: native " + chapter3CheckpointSnapshot.AssertedNative + " asserted.")); if (chapter3CheckpointSnapshot.AssertedNative != null && _roster.TryReadNativeLeak(chapter3CheckpointSnapshot.AssertedNative, out var leaked)) { if (leaked) { WarnOnce("native-leak-" + chapter3CheckpointSnapshot.AssertedNative, "Checkpoint native " + chapter3CheckpointSnapshot.AssertedNative + " lists an OC clone in its AssignedNPCs."); list.Add("checkpoints: native " + chapter3CheckpointSnapshot.AssertedNative + " lists an OC clone!"); } else { ClearWarn("native-leak-" + chapter3CheckpointSnapshot.AssertedNative); } } return list; } private static IEnumerable<string> DescribeEntry(Chapter3CheckpointStatusEntry entry, bool test) { int count = entry.Officers.Count; int value = entry.Officers.Count((Chapter3CheckpointOfficerEntry officer) => officer.BindState == Chapter3BindState.Bound); string value2 = (test ? " (test)" : string.Empty); yield return $"checkpoint {entry.Label}{value2}: {(entry.Spawned ? "spawned" : "missing")}, officers {count} held, {value} bound."; foreach (Chapter3CheckpointOfficerEntry officer in entry.Officers) { double? distanceM = officer.DistanceM; string value3 = ((!distanceM.HasValue) ? "distance unknown" : (distanceM.GetValueOrDefault().ToString("F1", CultureInfo.InvariantCulture) + " m from stand")); yield return $"checkpoint {entry.Label} officer {officer.RosterHandle}: {officer.BindState}, active {officer.ActiveBehaviour}, {value3}."; } } private IEnumerable<string> DescribeOfficerDiagnostics(Chapter3CheckpointStatusEntry entry) { if (entry.WorldHandle == null || TryReadSpawnedNative(entry.WorldHandle, out object native) != Chapter3CheckpointWorldStatus.Ok || native == null) { yield break; } foreach (Chapter3CheckpointOfficerEntry officer in entry.Officers) { if (officer.RosterHandle != -1 && _roster.TryReadBehaviourDiagnostics(officer.RosterHandle, native, out OcOfficerBehaviourDiagnostics diagnostics)) { string value = ((diagnostics.EnabledTopFirst.Count == 0) ? "none" : string.Join(", ", diagnostics.EnabledTopFirst.Select((OcOfficerBehaviourInfo info) => $"{info.Name} p{info.Priority}"))); yield return $"checkpoint {entry.Label} officer {officer.RosterHandle} stack: {value}; active {diagnostics.ActiveName}."; string value2 = diagnostics.Checkpoint.Kind switch { OcCheckpointKind.Ours => "OC", OcCheckpointKind.Native => "native", OcCheckpointKind.None => "none", _ => "unknown", }; yield return $"checkpoint {entry.Label} officer {officer.RosterHandle} checkpoint: enabled {diagnostics.Checkpoint.Enabled}, active {diagnostics.Checkpoint.Active}, assigned {diagnostics.Checkpoint.Assigned}, target {value2}."; diagnostics = null; } } } private static IReadOnlyList<Chapter3CheckpointStatusEntry> StatusEntries(Dictionary<string, ManagedCheckpoint> set) { return (from entry in set.Values.OrderBy<ManagedCheckpoint, string>((ManagedCheckpoint entry) => entry.Label, StringComparer.Ordinal) select new Chapter3CheckpointStatusEntry(entry.Label, entry.WorldHandle, Spawned: true, HeldOfficers(entry))).ToList(); } private void MaybePick(double? minutes, int? today) { if (!today.HasValue || !minutes.HasValue) { return; } if (_runtime.ReadCheckpointTarget().PickDay == today) { _needsPostLoadPick = false; } else if (_needsPostLoadPick || !(minutes.Value - (double)today.Value * 1440.0 < 360.0)) { Chapter3QaResult chapter3QaResult = PickCore(today.Value); if (chapter3QaResult.Ok) { _needsPostLoadPick = false; _log(chapter3QaResult.Message); } } } private Chapter3QaResult PickCore(int today) { if (_world.TryReadProductionActivity(out Release1ProductionActivitySnapshot activity) != Release1SmallCourtesyWorldReadStatus.Ready) { WarnOnce("pick-snapshot", "Checkpoint pick skipped: the production snapshot could not be read."); return new Chapter3QaResult(Ok: false, "Checkpoint pick skipped: the production snapshot could not be read."); } string lastVisitedCode = null; if (TryReadLastVisited(out string propertyCode) != Chapter3CheckpointWorldStatus.Ok || propertyCode == null) { WarnOnce("pick-lastvisited", "Checkpoint pick runs without the last-visited property; ties fall back to code order."); } else { lastVisitedCode = propertyCode; } string text = Chapter3CheckpointPlanner.PickTarget((from property in activity.Properties where (object)property != null select new Chapter3CheckpointScoredProperty(property.PropertyCode, property.Employees.Count, property.Stations.Count)).ToList(), lastVisitedCode); if (!_runtime.TryWriteCheckpointTarget(new Chapter3CheckpointTarget(text, today))) { WarnOnce("pick-write", "Checkpoint pick could not be recorded."); return new Chapter3QaResult(Ok: false, "Checkpoint pick could not be recorded."); } ClearWarn("pick-snapshot"); ClearWarn("pick-write"); if (text != null) { return new Chapter3QaResult(Ok: true, $"Checkpoint target picked: {text} (day {today})."); } return new Chapter3QaResult(Ok: true, $"Checkpoint pick day {today}: no owned property."); } private void Reconcile(double? minutes) { LocalPressureTier? localPressureTier = _chief.ReadTier(); if (!localPressureTier.HasValue) { return; } Chapter3CheckpointActiveSet desired; if (!_chief.IsProtected() && (localPressureTier == LocalPressureTier.Watched || localPressureTier == LocalPressureTier.Critical)) { Chapter3CheckpointTarget chapter3CheckpointTarget = _runtime.ReadCheckpointTarget(); if (chapter3CheckpointTarget.PropertyCode == null) { desired = new Chapter3CheckpointActiveSet(Array.Empty<string>(), null); } else { if (!TryResolveTargetPosition(chapter3CheckpointTarget.PropertyCode, out var targetX, out var targetZ)) { return; } List<Chapter3CheckpointNativeSite> list = new List<Chapter3CheckpointNativeSite>(); if (TryReadNativePositions(out IReadOnlyList<Chapter3NativeCheckpointPosition> positions) != Chapter3CheckpointWorldStatus.Ok) { WarnOnce("reconcile-natives", "Checkpoint reconcile runs without native positions; no native is asserted."); } else { list.AddRange(from site in positions where (object)site != null select new Chapter3CheckpointNativeSite(site.Location, site.X, site.Z)); } desired = Chapter3CheckpointPlanner.ActiveSet(targetX, targetZ, localPressureTier.Value, isProtected: false, Chapter3CheckpointSpots.Baked, list); } } else { desired = new Chapter3CheckpointActiveSet(Array.Empty<string>(), null); } _activeLabels = desired.OcLabels; AssertNative(desired.NativeLabel); double? obj; if (minutes.HasValue) { double valueOrDefault = minutes.GetValueOrDefault(); obj = Math.Floor(valueOrDefault); } else { obj = null; } double? num = obj; if (!num.HasValue || _lastStructuralMinute != num) { bool flag = false; foreach (string ocLabel in desired.OcLabels) { if (!_managed.ContainsKey(ocLabel)) { if (TrySpawnManaged(ocLabel)) { _lastStructuralMinute = num; } flag = true; break; } } if (!flag) { using List<string>.Enumerator enumerator2 = _managed.Keys.Where((string label) => !desired.OcLabels.Contains<string>(label, StringComparer.Ordinal)).OrderBy<string, string>((string label) => label, StringComparer.Ordinal).ToList() .GetEnumerator(); if (enumerator2.MoveNext()) { string current2 = enumerator2.Current; if (TryDespawnManaged(current2)) { _lastStructuralMinute = num; } } } } foreach (ManagedCheckpoint value in _managed.Values) { EnsureOfficers(value, minutes); } } private void AssertNative(string? label) { if (label == null) { _assertedNative = null; return; } if (TrySetNative(label, enabled: true) != Chapter3CheckpointWorldStatus.Ok) { WarnOnce("native-" + label, "Checkpoint native assert failed for " + label + "."); } else { ClearWarn("native-" + label); } _assertedNative = label; } private bool TrySpawnManaged(string label) { Chapter3CheckpointSpot chapter3CheckpointSpot = Chapter3CheckpointSpots.Baked.FirstOrDefault((Chapter3CheckpointSpot entry) => string.Equals(entry.Label, label, StringComparison.Ordinal)); if ((object)chapter3CheckpointSpot == null) { return false; } return TrySpawnInto(_managed, "checkpoint", label, chapter3CheckpointSpot); } private bool TrySpawnInto(Dictionary<string, ManagedCheckpoint> set, string ownerPrefix, string label, Chapter3CheckpointSpot spot) { if (TrySpawn(label, spot, out string handle) != Chapter3CheckpointWorldStatus.Ok || string.IsNullOrEmpty(handle)) { WarnOnce("spawn-" + label, "Checkpoint spawn failed for " + label + "."); return false; } ClearWarn("spawn-" + label); set[label] = new ManagedCheckpoint { Label = label, WorldHandle = handle, Spot = spot, OwnerPrefix = ownerPrefix, OfficerHandles = new int[2] { -1, -1 } }; return true; } private bool TryDespawnManaged(string label) { return TryRemoveFrom(_managed, label); } private bool TryRemoveFrom(Dictionary<string, ManagedCheckpoint> set, string label) { if (!set.TryGetValue(label, out ManagedCheckpoint value)) { return true; } ReleaseOfficers(value); if (TryDespawn(value.WorldHandle) != Chapter3CheckpointWorldStatus.Ok) { WarnOnce("despawn-" + label, "Checkpoint despawn failed for " + label + "."); return false; } ClearWarn("despawn-" + label); set.Remove(label); return true; } private void ReleaseOfficers(ManagedCheckpoint managed) { for (int i = 0; i < managed.OfficerHandles.Length; i++) { if (managed.OfficerHandles[i] != -1) { _roster.Release(managed.OfficerHandles[i]); } managed.OfficerHandles[i] = -1; managed.OfficerBindState[i] = Chapter3BindState.Binding; managed.OfficerActiveName[i] = "unknown"; managed.OfficerDistanceM[i] = null; managed.BindStartMinutes[i] = null; } } private void EnsureOfficers(ManagedCheckpoint managed, double? minutes) { if (managed.StandPoints == null) { if (TryReadStandPoints(managed.WorldHandle, out IReadOnlyList<Chapter3StandPoint> points) != Chapter3CheckpointWorldStatus.Ok || points.Count == 0) { WarnOnce("standpoints-" + managed.Label, "Checkpoint " + managed.Label + ": stand points unreadable; officers wait."); return; } ClearWarn("standpoints-" + managed.Label); managed.StandPoints = points; } int i; for (i = 0; i < 2; i++) { Chapter3StandPoint chapter3StandPoint = managed.StandPoints[i % managed.StandPoints.Count]; OcOfficerPosition ocOfficerPosition = new OcOfficerPosition(chapter3StandPoint.X, chapter3StandPoint.Y, chapter3StandPoint.Z, managed.Spot.YawDegrees); if (managed.OfficerHandles[i] != -1 && !_roster.Snapshot().Any((OcOfficerSlotSnapshot slot) => slot.Handle == managed.OfficerHandles[i] && slot.OwnerKey == $"{managed.OwnerPrefix}:{managed.Label}:{i}")) { managed.OfficerHandles[i] = -1; managed.OfficerBindState[i] = Chapter3BindState.Binding; managed.BindStartMinutes[i] = null; managed.OfficerActiveName[i] = "unknown"; managed.OfficerDistanceM[i] = null; } if (managed.OfficerHandles[i] == -1) { int handle; OcOfficerRequestStatus ocOfficerRequestStatus = _roster.TryRequest($"{managed.OwnerPrefix}:{managed.Label}:{i}", ocOfficerPosition, out handle); if (ocOfficerRequestStatus != OcOfficerRequestStatus.Accepted) { WarnOnce("officers-" + managed.Label, $"Checkpoint {managed.Label}: officer request refused ({ocOfficerRequestStatus})."); break; } ClearWarn("officers-" + managed.Label); managed.OfficerHandles[i] = handle; } if (managed.OfficerBindState[i] == Chapter3BindState.Bound) { continue; } if (TryReadSpawnedNative(managed.WorldHandle, out object native) != Chapter3CheckpointWorldStatus.Ok || native == null) { WarnOnce("bind-" + managed.Label, "Checkpoint " + managed.Label + ": the spawned checkpoint could not be read for binding."); continue; } OcOfficerBindStatus ocOfficerBindStatus = _roster.TryBindToCheckpoint(managed.OfficerHandles[i], native, ocOfficerPosition); double? num; switch (ocOfficerBindStatus) { case OcOfficerBindStatus.Bound: { ref double? reference = ref managed.BindStartMinutes[i]; num = reference; if (!num.HasValue) { reference = minutes; } ClearWarn("bind-" + managed.Label); break; } default: WarnOnce("bind-" + managed.Label, $"Checkpoint {managed.Label}: officer bind failed ({ocOfficerBindStatus})."); break; case OcOfficerBindStatus.SlotNotActive: break; } string activeBehaviour; bool checkpointActive; bool flag = _roster.TryReadBehaviourState(managed.OfficerHandles[i], out activeBehaviour, out checkpointActive); if (flag) { managed.OfficerActiveName[i] = activeBehaviour; } OcOfficerBoundState state; bool flag2 = _roster.TryReadBoundState(managed.OfficerHandles[i], native, out state); bool flag3 = flag2 && state.CheckpointKind == OcCheckpointKind.Ours; double?[] officerDistanceM; int num2; double value; if (_roster.TryReadClonePosition(managed.OfficerHandles[i], out var position)) { officerDistanceM = managed.OfficerDistanceM; num2 = i; if (flag2) { OcOfficerPosition? standPoint = state.StandPoint; if (standPoint.HasValue) { value = DistanceM(stand: standPoint.GetValueOrDefault(), clone: position); goto IL_04fd; } } value = DistanceM(position, chapter3StandPoint); goto IL_04fd; } goto IL_0507; IL_0507: if (flag2 && state.CheckpointKind == OcCheckpointKind.Native) { WarnOnce($"native-leak-{managed.Label}-{i}", $"Checkpoint {managed.Label} officer {i} sits on a native checkpoint."); } if (flag2 && state.CheckpointEnabled && flag && !checkpointActive && !string.IsNullOrWhiteSpace(activeBehaviour) && !activeBehaviour.Contains("checkpoint", StringComparison.OrdinalIgnoreCase)) { WarnOnce($"priority-{managed.Label}-{i}", $"Checkpoint {managed.Label} officer {i}: checkpoint enabled but {activeBehaviour} outranks the checkpoint."); } if (flag && checkpointActive && flag3) { num = managed.OfficerDistanceM[i]; if (num.HasValue) { double valueOrDefault = num.GetValueOrDefault(); if (valueOrDefault <= 3.0) { managed.OfficerBindState[i] = Chapter3BindState.Bound; ClearWarn($"timeout-{managed.Label}-{i}"); ClearWarn($"priority-{managed.Label}-{i}"); ClearWarn($"native-leak-{managed.Label}-{i}"); continue; } } } num = managed.BindStartMinutes[i]; if (!num.HasValue) { continue; } double valueOrDefault2 = num.GetValueOrDefault(); if (minutes.HasValue) { double valueOrDefault3 = minutes.GetValueOrDefault(); if (valueOrDefault3 - valueOrDefault2 >= 20.0) { WarnOnce($"timeout-{managed.Label}-{i}", $"Checkpoint {managed.Label} officer {i}: bind timed out; re-binding."); managed.BindStartMinutes[i] = valueOrDefault3; } } continue; IL_04fd: officerDistanceM[num2] = value; goto IL_0507; } } private bool TryResolveTargetPosition(string code, out float targetX, out float targetZ) { targetX = 0f; targetZ = 0f; if (TryReadOwnedPositions(out IReadOnlyList<Chapter3OwnedPropertyPosition> positions) != Chapter3CheckpointWorldStatus.Ok) { WarnOnce("reconcile-positions", "Checkpoint reconcile skipped: owned property positions could not be read."); return false; } Chapter3OwnedPropertyPosition chapter3OwnedPropertyPosition = positions.FirstOrDefault((Chapter3OwnedPropertyPosition entry) => (object)entry != null && string.Equals(entry.PropertyCode, code, StringComparison.Ordinal)); if ((object)chapter3OwnedPropertyPosition == null) { WarnOnce("target-missing-" + code, "Checkpoint reconcile skipped: the target " + code + " has no world position."); return false; } ClearWarn("reconcile-positions"); ClearWarn("target-missing-" + code); targetX = chapter3OwnedPropertyPosition.X; targetZ = chapter3OwnedPropertyPosition.Z; return true; } private void ClearLocal() { _managed.Clear(); _tests.Clear(); _warned.ClearAll(); _throttle.Clear(); _activeLabels = Array.Empty<string>(); _lastStructuralMinute = null; _assertedNative = null; _needsPostLoadPick = false; } private static IReadOnlyList<Chapter3CheckpointOfficerEntry> HeldOfficers(ManagedCheckpoint managed) { List<Chapter3CheckpointOfficerEntry> list = new List<Chapter3CheckpointOfficerEntry>(2); for (int i = 0; i < 2; i++) { if (managed.OfficerHandles[i] != -1) { list.Add(new Chapter3CheckpointOfficerEntry(managed.OfficerHandles[i], managed.OfficerBindState[i], managed.OfficerActiveName[i], managed.OfficerDistanceM[i])); } } return list; } private static double DistanceM(OcOfficerPosition clone, OcOfficerPosition stand) { float num = clone.X - stand.X; float num2 = clone.Y - stand.Y; float num3 = clone.Z - stand.Z; return Math.Sqrt(num * num + num2 * num2 + num3 * num3); } private static double DistanceM(OcOfficerPosition clone, Chapter3StandPoint stand) { float num = clone.X - stand.X; float num2 = clone.Y - stand.Y; float num3 = clone.Z - stand.Z; return Math.Sqrt(num * num + num2 * num2 + num3 * num3); } private void WarnOnce(string cause, string line) { _warned.Report(cause, line); } private void ClearWarn(string cause) { _warned.Clear(cause); } private bool IsHost() { try { return _checkpoints.IsHost; } catch { return false; } } private Chapter3CheckpointWorldStatus TryReadLastVisited(out string? propertyCode) { try { return _checkpoints.TryReadLastVisitedPropertyCode(out propertyCode); } catch { propertyCode = null; return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryReadOwnedPositions(out IReadOnlyList<Chapter3OwnedPropertyPosition> positions) { try { return _checkpoints.TryReadOwnedPropertyPositions(out positions); } catch { positions = Array.Empty<Chapter3OwnedPropertyPosition>(); return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryReadNativePositions(out IReadOnlyList<Chapter3NativeCheckpointPosition> positions) { try { return _checkpoints.TryReadNativeCheckpointPositions(out positions); } catch { positions = Array.Empty<Chapter3NativeCheckpointPosition>(); return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TrySpawn(string label, Chapter3CheckpointSpot spot, out string? handle) { try { return _checkpoints.TrySpawnCheckpoint(label, spot.X, spot.Y, spot.Z, spot.YawDegrees, out handle); } catch { handle = null; return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryDespawn(string handle) { try { return _checkpoints.TryDespawnCheckpoint(handle); } catch { return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryReadStandPoints(string handle, out IReadOnlyList<Chapter3StandPoint> points) { try { return _checkpoints.TryReadStandPoints(handle, out points); } catch { points = Array.Empty<Chapter3StandPoint>(); return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryReadSpawnedNative(string handle, out object? native) { try { return _checkpoints.TryReadSpawnedNative(handle, out native); } catch { native = null; return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TrySetNative(string location, bool enabled) { try { return _checkpoints.TrySetNativeCheckpoint(location, enabled); } catch { return Chapter3CheckpointWorldStatus.Failed; } } private Chapter3CheckpointWorldStatus TryReadCameraPose(out Chapter3CameraPose pose) { try { return _checkpoints.TryReadPlayerCameraPose(out pose); } catch { pose = new Chapter3CameraPose(0f, 0f, 0f, 0f); return Chapter3CheckpointWorldStatus.Failed; } } } public sealed class Chapter3DayClockAdapter : IChapter3DayClock, IDisposable { private readonly Action<string> _log; private Action? _dayPassHandler; private Action? _weekPassHandler; private bool _lifecycleSubscribed; private bool _disposed; public event Action? DayPassed; public event Action? WeekPassed; public Chapter3DayClockAdapter(Action<string>? log = null) { _log = log ?? OrganizedCrimeLog.Warning; SubscribeLifecycleEvents(); } public bool TryReadElapsedDa