using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using ChainedPuzzles; using CustomCheckpoints.Data; using CustomCheckpoints.Extensions; using CustomCheckpoints.Manager; using CustomCheckpoints.Utility; using GTFO.API; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Microsoft.CodeAnalysis; using TheArchive.Core; using TheArchive.Core.Attributes; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Components; using TheArchive.Core.FeaturesAPI.Settings; using TheArchive.Core.Localization; using TheArchive.Interfaces; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("CustomCheckpoints")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: AssemblyInformationalVersion("0.1.0+40b602f6c0481751f0013ffe2d085d8eba8bb77a")] [assembly: AssemblyProduct("CustomCheckpoints")] [assembly: AssemblyTitle("CustomCheckpoints")] [assembly: AssemblyVersion("0.1.0.0")] 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; } } } namespace CustomCheckpoints { [BepInPlugin("dev.mbooster.CustomCheckpoints", "CustomCheckpoints", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class CustomCheckpoints : BasePlugin { public const string ModName = "CustomCheckpoints"; public const string Author = "mbooster"; public const string Guid = "dev.mbooster.CustomCheckpoints"; public const string Version = "0.1.0"; public override void Load() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) GameDataAPI.OnGameDataInitialized += CheckpointConfigManager.ValidateConfig; new Harmony("dev.mbooster.CustomCheckpoints").PatchAll(); LevelAPI.OnBuildStart += Execute; } private void Execute() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown bool flag = default(bool); try { CheckpointConfigManager.ValidateConfig(); int num = CustomCheckpointManager.ReplaceDoorsForLevel(RundownDataManager.GetDoorData(CheckpointConfigManager.GetConfig())); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(19, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Added "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" checkpoints."); } Log.Logger.LogInfo(val); } catch (Exception ex) { ManualLogSource log = ((BasePlugin)this).Log; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(40, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error occurred during door replacement: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } log.LogError(val2); } } } [ArchiveModule("dev.mbooster.CustomCheckpoints", "CustomCheckpoints", "0.1.0")] public class CustomCheckpointsArchiveModule : IArchiveModule { public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } public void Init() { } } [GeneratedCode("VersionInfoGenerator", "2.0.0+git50a4b1a-master")] [CompilerGenerated] internal static class VersionInfo { public const string RootNamespace = "CustomCheckpoints"; public const string Version = "0.1.0"; public const string VersionPrerelease = null; public const string VersionMetadata = "git40b602f-dirty-master"; public const string SemVer = "0.1.0"; public const string GitRevShort = "40b602f-dirty"; public const string GitRevLong = "40b602f6c0481751f0013ffe2d085d8eba8bb77a-dirty"; public const string GitBranch = "master"; public const string GitTag = null; public const bool GitIsDirty = true; } } namespace CustomCheckpoints.Utility { public class JsonSerializer { private readonly JsonSerializerOptions _options = new JsonSerializerOptions { AllowTrailingCommas = true, IncludeFields = true, PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, WriteIndented = true }; public JsonSerializer() { _options.Converters.Add(new JsonStringEnumConverter()); } public T Read(string path) where T : new() { string json = File.ReadAllText(path); T val = Deserialize(json); if (val == null) { throw new JsonException("Deserialization returned null."); } return val; } public void Write(string path, string json) { File.WriteAllText(path, json); } public string Serialize(object value) { return System.Text.Json.JsonSerializer.Serialize(value, _options); } public T? Deserialize(string json) { return System.Text.Json.JsonSerializer.Deserialize(json, _options); } } public static class Log { public static readonly ManualLogSource Logger = Logger.CreateLogSource("CustomCheckpoints"); public static void LogObject(object? obj, string prefix = "", int depth = 0) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (depth > 3 || obj == null) { bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(8, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(prefix); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" is NULL"); } Logger.LogInfo(val); return; } Type type = obj.GetType(); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (PropertyInfo propertyInfo in properties) { try { object value = propertyInfo.GetValue(obj); LogValue(propertyInfo.Name, value, propertyInfo.PropertyType, prefix, depth); } catch { } } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { try { object value2 = fieldInfo.GetValue(obj); LogValue(fieldInfo.Name, value2, fieldInfo.FieldType, prefix, depth); } catch { } } } private static void LogValue(string name, object? value, Type type, string prefix, int depth) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown if (type == typeof(IntPtr) || type == typeof(UIntPtr)) { return; } string? text = type.Namespace; if ((text != null && text.StartsWith("Il2CppInterop")) || type.Name.Contains("Il2CppMethodInfo")) { return; } string text2 = prefix + name; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val; if (value == null) { val = new BepInExInfoLogInterpolatedStringHandler(7, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" = NULL"); } Logger.LogInfo(val); return; } if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { val = new BepInExInfoLogInterpolatedStringHandler(3, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" = "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(value); } Logger.LogInfo(val); return; } string? text3 = type.Namespace; if (text3 != null && text3.StartsWith("UnityEngine")) { val = new BepInExInfoLogInterpolatedStringHandler(3, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" = "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(value); } Logger.LogInfo(val); return; } val = new BepInExInfoLogInterpolatedStringHandler(4, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(type.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("):"); } Logger.LogInfo(val); LogObject(value, text2 + ".", depth + 1); } } } namespace CustomCheckpoints.Patches { [HarmonyPatch(typeof(ChainedPuzzleManager), "CreatePuzzleInstance", new Type[] { typeof(ChainedPuzzleDataBlock), typeof(LG_Area), typeof(Vector3), typeof(Transform) })] public static class CreatePuzzleInstancePatch { public static void Prefix(ref ChainedPuzzleDataBlock data, LG_Area sourceArea) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown if (data == null) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CreatePuzzleInstance: null datablock for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((sourceArea != null) ? ((Object)sourceArea).name : null); } Log.Logger.LogWarning(val); data = GetCheckpointScanReplacement(); } } private static ChainedPuzzleDataBlock GetCheckpointScanReplacement() { return ((IEnumerable)GameDataBlockBase.GetAllBlocks()).FirstOrDefault((Func)((ChainedPuzzleDataBlock b) => ((GameDataBlockBase)(object)b).name.ToLower().Contains("checkpoint"))) ?? CreateCheckpointBlock(); } private static ChainedPuzzleDataBlock CreateCheckpointBlock() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00ec: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown Il2CppArrayBase allBlocks = GameDataBlockBase.GetAllBlocks(); int count = allBlocks.Count; uint num = 0u; for (int i = 0; i < count; i++) { if (((GameDataBlockBase)(object)allBlocks[i]).persistentID > num) { num = ((GameDataBlockBase)(object)allBlocks[i]).persistentID; } } ChainedPuzzleComponent val = new ChainedPuzzleComponent { PuzzleType = 19u }; List val2 = new List(); val2.Add(val); ChainedPuzzleDataBlock val3 = new ChainedPuzzleDataBlock { PublicAlarmName = "Alarm", TriggerAlarmOnActivate = false, SurvivalWaveSettings = 1u, SurvivalWavePopulation = 1u, SurvivalWaveAreaDistance = 2, DisableSurvivalWaveOnComplete = true, UseRandomPositions = false, WantedDistanceFromStartPos = 0f, WantedDistanceBetweenPuzzleComponents = 1f, ChainedPuzzle = val2, OnlyShowHUDWhenPlayerIsClose = false, AlarmSoundStart = 3339129407u, AlarmSoundStop = 42633153u }; ((GameDataBlockBase)val3).name = "Checkpoint"; ((GameDataBlockBase)val3).internalEnabled = true; ((GameDataBlockBase)val3).persistentID = num + 1; ChainedPuzzleDataBlock val4 = val3; GameDataBlockBase.AddBlock(val4, -1); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val5 = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("Injected checkpoint scan block with ID "); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(((GameDataBlockBase)(object)val4).persistentID); } Log.Logger.LogInfo(val5); return val4; } } } namespace CustomCheckpoints.Manager { public static class CheckpointConfigManager { private static readonly global::CustomCheckpoints.Utility.JsonSerializer Serializer = new global::CustomCheckpoints.Utility.JsonSerializer(); private const string CheckpointsConfigFileName = "CustomCheckpoints.doors.json"; private static byte[]? _configHash; private static List? _config; public static List? GetConfig() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown bool flag = default(bool); try { if (_config != null && _configHash == GetConfigHash()) { return _config.GetSorted(); } string configFilepath = GetConfigFilepath(); List list = Serializer.Read>(configFilepath); CacheConfig(list); return list.GetSorted(); } catch (Exception ex) when (((ex is DirectoryNotFoundException || ex is FileNotFoundException) ? 1 : 0) != 0) { return null; } catch (JsonException ex2) { BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(54, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("CustomCheckpoints.doors.json"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" configuration is invalid. See exception for details: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex2.Message); } Log.Logger.LogWarning(val); return new List(); } catch (Exception ex3) { BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(26, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Unexpected error occured: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex3.Message); } Log.Logger.LogError(val2); throw; } } public static void ValidateConfig() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown List config = GetConfig(); if (config == null) { Log.Logger.LogInfo((object)"No existing configuration detected, creating new checkpoint configuration..."); SaveConfig(RundownDataManager.RetrieveData()); return; } bool flag = default(bool); if (config.Count == 0) { BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(80, 0, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Config exists, but no door data was found, resetting checkpoint configuration..."); } Log.Logger.LogWarning(val); ResetConfig(); } if (HasReadonlyDataChanged(config)) { BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(69, 0, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Readonly data has been changed, resetting checkpoint configuration..."); } Log.Logger.LogWarning(val); ResetConfig(); } } public static void SaveConfig(List rundowns) { string json = Serializer.Serialize(rundowns); string configFilepath = GetConfigFilepath(); Serializer.Write(configFilepath, json); CacheConfig(rundowns); } private static bool HasReadonlyDataChanged(List config) { List sorted = RundownDataManager.OriginalData.GetSorted(); return !config.SequenceEqual(sorted); } private static string GetConfigFilepath() { return Path.Combine(Paths.ConfigPath, "CustomCheckpoints.doors.json"); } private static void ResetConfig() { SaveConfig(TryRestoreConfig(_config, RundownDataManager.OriginalData, out List rundowns) ? rundowns : RundownDataManager.OriginalData); } private static bool TryRestoreConfig(List? configToRestore, List newConfig, out List rundowns) { //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown rundowns = newConfig; if (configToRestore == null || configToRestore.Count == 0) { return false; } int num = 0; int num2 = 0; foreach (RundownData rundown in newConfig) { RundownData rundownData = configToRestore.FirstOrDefault((RundownData r) => r.Id == rundown.Id); if (rundownData == null) { continue; } foreach (ExpeditionData expedition in rundown.Expeditions) { ExpeditionData expeditionData = rundownData.Expeditions.FirstOrDefault((ExpeditionData e) => e.Id == expedition.Id); if (expeditionData == null) { continue; } foreach (SecurityDoorData door in expedition.Doors) { num2++; SecurityDoorData securityDoorData = expeditionData.Doors.FirstOrDefault((SecurityDoorData d) => d.Id == door.Id); if (securityDoorData != null) { door.IsCheckpoint = securityDoorData.IsCheckpoint; num++; } } } } bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(34, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restored "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" door checkpoint states."); } Log.Logger.LogInfo(val); return num > 0; } private static void CacheConfig(List config) { _configHash = GetConfigHash(); _config = config; } private static byte[] GetConfigHash() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown try { string configFilepath = GetConfigFilepath(); using MD5 mD = MD5.Create(); using FileStream inputStream = File.OpenRead(configFilepath); return mD.ComputeHash(inputStream); } catch (Exception ex) { bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(26, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unexpected error occured: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } Log.Logger.LogError(val); throw; } } } public static class CustomCheckpointManager { public static int ReplaceDoorsForLevel(Dictionary> doorData) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown int num = 0; ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition; string key = activeExpedition.LevelLayoutData.GetHashCode().ToString(); doorData.TryGetValue(key, out List value); if (value == null || value.Count == 0) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(50, 0, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("No door data could be found for the current level."); } Log.Logger.LogWarning(val); return 0; } num += ReplaceDoors(activeExpedition.LevelLayoutData, value, Layer.Main); if (activeExpedition.SecondaryLayerEnabled) { num += ReplaceDoors(activeExpedition.SecondaryLayout, value, Layer.Secondary); } if (activeExpedition.ThirdLayerEnabled) { num += ReplaceDoors(activeExpedition.ThirdLayout, value, Layer.Third); } return num; } private static int ReplaceDoors(uint layoutId, List doors, Layer layer) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown int num = 0; LevelLayoutDataBlock block = GameDataBlockBase.GetBlock(layoutId); if (block == null) { return 0; } List zones = block.Zones; Il2CppArrayBase items = zones._items; int size = zones._size; bool flag = default(bool); for (int i = 0; i < size; i++) { ExpeditionZoneData val = items[i]; string doorId = val.GetDoorId(layer); if (val.ChainedPuzzleToEnter == 0) { continue; } SecurityDoorData securityDoorData = doors.FirstOrDefault((SecurityDoorData d) => d.Id == doorId); if (securityDoorData == null) { continue; } if (securityDoorData.IsCheckpoint) { BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(23, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Adding a checkpoint to "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(securityDoorData.Name); } Log.Logger.LogInfo(val2); num++; } val.IsCheckpointDoor = securityDoorData.IsCheckpoint; items[i] = val; } return num; } } public static class RundownDataManager { public static readonly List OriginalData = RetrieveData(); public static List RetrieveData() { List list = new List(); Il2CppArrayBase allBlocks = GameDataBlockBase.GetAllBlocks(); int count = allBlocks.Count; for (int i = 0; i < count; i++) { RundownDataBlock obj = allBlocks[i]; List list2 = new List(); uint persistentID = ((GameDataBlockBase)(object)obj).persistentID; string name = ((GameDataBlockBase)(object)obj).name; List[] tiers = obj.GetTiers(); foreach (List val in tiers) { if (val == null) { continue; } Il2CppArrayBase items = val._items; int size = val._size; for (int k = 0; k < size; k++) { ExpeditionInTierData val2 = items[k]; if (val2 != null && val2.Enabled) { List list3 = new List(); string expeditionId = val2.GetExpeditionId(k); AddAllDoorsFromLayoutId(val2.LevelLayoutData, Layer.Main, list3); if (val2.SecondaryLayerEnabled) { AddAllDoorsFromLayoutId(val2.SecondaryLayout, Layer.Secondary, list3); } if (val2.ThirdLayerEnabled) { AddAllDoorsFromLayoutId(val2.ThirdLayout, Layer.Third, list3); } if (list3.Count != 0) { ExpeditionData item = new ExpeditionData(expeditionId, val2.LevelLayoutData, list3); list2.Add(item); } } } } if (list2.Count != 0) { RundownData item2 = new RundownData(persistentID, name, list2); list.Add(item2); } } return list; } public static Dictionary> GetDoorData(List config) { return (from d in config.SelectMany((RundownData r) => r.Expeditions) where d.Doors.Count > 0 select d).ToDictionary((ExpeditionData e) => e.LayoutId.GetHashCode().ToString(), (ExpeditionData e) => e.Doors); } private static void AddAllDoorsFromLayoutId(uint layoutId, Layer layer, List doors) { LevelLayoutDataBlock block = GameDataBlockBase.GetBlock(layoutId); if (block != null) { int zoneAliasStart = block.ZoneAliasStart; AddDoorsFromLayout(block, layer, doors, zoneAliasStart); } } private static void AddDoorsFromLayout(LevelLayoutDataBlock layout, Layer layer, List doors, int startAlias) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected I4, but got Unknown Il2CppArrayBase items = layout.Zones._items; int size = layout.Zones._size; for (int i = 0; i < size; i++) { ExpeditionZoneData val = items[i]; if ((int)val.LocalIndex != 0) { string doorId = val.GetDoorId(layer); string text = $"Door to zone {startAlias + val.LocalIndex}"; string value = ((val.ChainedPuzzleToEnter == 0) ? "no alarm" : (((GameDataBlockBase)(object)GameDataBlockBase.GetBlock(val.ChainedPuzzleToEnter))?.name ?? "unknown alarm")); string description = $"{text} ({layer}) — {value}"; SecurityDoorData item = new SecurityDoorData(doorId, text, description, val.IsCheckpointDoor); doors.Add(item); } } } } } namespace CustomCheckpoints.Features { [EnableFeatureByDefault] [DoNotSaveToConfig] public class CustomCheckpointMenu : Feature { public override string Name => "Custom Checkpoints"; public override string Description => "Configure which security doors are converted into checkpoints."; public override FeatureGroup Group => FeatureGroups.GetOrCreateTopLevelGroup("Custom Checkpoints", (GroupLocalization)null); [FeatureConfig] public static MenuSettings Settings { get; set; } = new MenuSettings(); public override void OnGameDataInitialized() { RebuildMenuData(); } private static void RebuildMenuData() { CheckpointConfigManager.ValidateConfig(); List config = CheckpointConfigManager.GetConfig(); if (config != null) { Settings.Rebuild(config); } } public override void OnFeatureSettingChanged(FeatureSetting setting) { foreach (FeatureSettingsHelper settingsHelper in ((Feature)this).SettingsHelpers) { ((object)settingsHelper).GetType().GetProperty("IsDirty")?.SetValue(settingsHelper, false); } } } public class DoorMenuItem { private SecurityDoorData? _door; private List? _config; private bool _isCheckpoint; public FLabel Door { get; set; } = new FLabel(); public FLabel Description { get; set; } = new FLabel(); [FSDescription("Set this to 'On' if it should be a checkpoint.")] public bool Checkpoint { get { return _door?.IsCheckpoint ?? _isCheckpoint; } set { _isCheckpoint = value; if (_door != null && _door.IsCheckpoint != value) { _door.IsCheckpoint = value; if (_config != null) { CheckpointConfigManager.SaveConfig(_config); } } } } public DoorMenuItem() { }//IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown public DoorMenuItem(SecurityDoorData door, List config) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown _door = door; _config = config; _isCheckpoint = door.IsCheckpoint; Door = new FLabel("" + door.Name + "", (string)null); Description = new FLabel("" + door.Description + "", (string)null); } } public class ExpeditionMenuItem { public FLabel Expedition { get; set; } = new FLabel(); [FSDisplayName("Security Doors")] public List Doors { get; set; } = new List(); public ExpeditionMenuItem() { }//IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown public ExpeditionMenuItem(ExpeditionData expedition, List config) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown Expedition = new FLabel("" + expedition.Id + "", (string)null); Doors = expedition.Doors.Select((SecurityDoorData door) => new DoorMenuItem(door, config)).ToList(); } } public class MenuSettings { [FSDisplayName("Rundowns")] [FSDescription("Choose checkpoint doors by rundown, expedition, and security door.")] public List Rundowns { get; set; } = new List(); public void Rebuild(List config) { Rundowns = config.Select((RundownData rundown) => new RundownMenuItem(rundown, config)).ToList(); } } public class RundownMenuItem { public FLabel Rundown { get; set; } = new FLabel(); [FSDisplayName("Expeditions")] public List Expeditions { get; set; } = new List(); public RundownMenuItem() { }//IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown public RundownMenuItem(RundownData rundown, List config) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown Rundown = new FLabel("" + rundown.Name + "", (string)null); Expeditions = rundown.Expeditions.Select((ExpeditionData expedition) => new ExpeditionMenuItem(expedition, config)).ToList(); } } } namespace CustomCheckpoints.Extensions { public static class DataBlockExtensions { public static List?[] GetTiers(this RundownDataBlock? dataBlock) { return new List[5] { (dataBlock != null) ? dataBlock.TierA : null, (dataBlock != null) ? dataBlock.TierB : null, (dataBlock != null) ? dataBlock.TierC : null, (dataBlock != null) ? dataBlock.TierD : null, (dataBlock != null) ? dataBlock.TierE : null }; } public static string GetDoorId(this ExpeditionZoneData zone, Layer layerName) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) return $"{Enum.GetName(typeof(Layer), layerName)}_{zone.LocalIndex}"; } public static string GetExpeditionId(this ExpeditionInTierData exp, int index) { return exp.GetShortName(index) + " " + StripRichText(exp.Descriptive.PublicName); } private static string StripRichText(string input) { if (string.IsNullOrEmpty(input)) { return input; } return Regex.Replace(input, "<.*?>", string.Empty); } } public static class RundownDataExtensions { public static List GetSorted(this List rundowns) { return (from r in rundowns orderby GetRundownSortKey(r.Name), r.Name select r).ToList(); } private static int GetRundownSortKey(string name) { Match match = Regex.Match(name, "Rundown (\\d+)\\.0"); if (match.Success) { return int.Parse(match.Groups[1].Value); } return int.MaxValue; } } } namespace CustomCheckpoints.Data { public class ExpeditionData : IEquatable { public string Id { get; init; } public uint LayoutId { get; init; } public List Doors { get; init; } public ExpeditionData(string id, uint layoutId, List doors) { Id = id; LayoutId = layoutId; Doors = doors; } public bool Equals(ExpeditionData? other) { if (other == null) { return false; } if (Id == other.Id && LayoutId == other.LayoutId) { return Doors.SequenceEqual(other.Doors); } return false; } public override bool Equals(object? obj) { return Equals(obj as ExpeditionData); } public override int GetHashCode() { return HashCode.Combine(Id, LayoutId); } } public enum Layer { Main, Secondary, Third } public class RundownData : IEquatable { public uint Id { get; init; } public string Name { get; init; } public List Expeditions { get; init; } public RundownData(uint id, string name, List expeditions) { Id = id; Name = name; Expeditions = expeditions; } public bool Equals(RundownData? other) { if (other == null) { return false; } if (Id == other.Id && Name == other.Name) { return Expeditions.SequenceEqual(other.Expeditions); } return false; } public override bool Equals(object? obj) { return Equals(obj as RundownData); } public override int GetHashCode() { return HashCode.Combine(Id, Name); } } public class SecurityDoorData : IEquatable { public string Id { get; init; } public string Name { get; init; } public string Description { get; init; } public bool IsCheckpoint { get; set; } public SecurityDoorData(string id, string name, string description, bool isCheckpoint) { Id = id; Name = name; Description = description; IsCheckpoint = isCheckpoint; } public bool Equals(SecurityDoorData? other) { if (other == null) { return false; } if (Id == other.Id && Name == other.Name) { return Description == other.Description; } return false; } public override bool Equals(object? obj) { return Equals(obj as SecurityDoorData); } public override int GetHashCode() { return HashCode.Combine(Id, Name, Description); } } }