using System; using System.Collections; 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 Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using BepInEx; using BepInEx.Logging; using HarmonyLib; using IAmYourBeastArchipelago.Archipelago; using IAmYourBeastArchipelago.Core; using IAmYourBeastArchipelago.Helpers; using IAmYourBeastArchipelago.Patches; using Objectives; using Progress; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("IAmYourBeastArchipelago")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("IAmYourBeastArchipelago")] [assembly: AssemblyTitle("IAmYourBeastArchipelago")] [assembly: AssemblyVersion("1.0.0.0")] namespace IAmYourBeastArchipelago { public static class PluginInfo { public const string PLUGIN_GUID = "IAmYourBeastArchipelago"; public const string PLUGIN_NAME = "IAmYourBeastArchipelago"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace IAmYourBeastArchipelago.Helpers { public class UIHelpers : MonoBehaviour { private static GameObject connectUIObject; private bool showWindow = true; private string host = "archipelago.gg:"; private string slotName = ""; private string password = ""; private static string statusMessage = ""; private static bool isError = false; private Rect windowRect = new Rect(20f, 20f, 270f, 270f); public static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if (((Scene)(ref scene)).name == "Start Screen") { if ((Object)(object)connectUIObject == (Object)null) { connectUIObject = new GameObject("APConnectWindow"); connectUIObject.AddComponent(); Object.DontDestroyOnLoad((Object)(object)connectUIObject); } connectUIObject.SetActive(true); } else if ((Object)(object)connectUIObject != (Object)null) { connectUIObject.SetActive(false); } } private void OnGUI() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (showWindow) { windowRect = GUI.Window(16720, windowRect, new WindowFunction(DrawWindow), "Archipelago Connect"); } } private void DrawWindow(int id) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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) GUILayout.Space(8f); GUILayout.Label("Host:Port", Array.Empty()); host = GUILayout.TextField(host, Array.Empty()); GUILayout.Label("Slot Name", Array.Empty()); slotName = GUILayout.TextField(slotName, Array.Empty()); GUILayout.Label("Password (optional)", Array.Empty()); password = GUILayout.TextField(password, Array.Empty()); GUILayout.Space(10f); if (ArchipelagoManager.IsConnected) { GUI.enabled = false; GUILayout.Button("Connected", Array.Empty()); GUI.enabled = true; } else if (GUILayout.Button("Connect", Array.Empty())) { AttemptConnect(); } if (!string.IsNullOrEmpty(statusMessage)) { Color color = GUI.color; GUI.color = (isError ? Color.red : Color.green); GUILayout.Label(statusMessage, Array.Empty()); GUI.color = color; } GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f)); } private void AttemptConnect() { if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(slotName)) { statusMessage = "Host and slot name are required."; isError = true; return; } try { ArchipelagoManager.Connect(host, slotName, password); if (ArchipelagoManager.IsConnected) { GameManager.instance.saveManager.InitializeSaveFiles(); ProgressTracker.PopulateProgress(); statusMessage = "Successfully connected to Archipelago"; isError = false; } else { statusMessage = "Could not connect to Archipelago"; isError = true; } } catch (Exception ex) { statusMessage = "Error: " + ex.Message; isError = true; } } public static void DisplayMessage(string message, bool error) { statusMessage = message; isError = error; } } public static class HintHelper { private static int hintIndex = -1; private static List> hints = new List> { () => $"You currently have {ProgressTracker.UnlockedCount} levels unlocked", () => $"You have completed {ProgressTracker.CompletedCount} levels out of the required {ArchipelagoManager.RequiredLevels}", () => $"You have completed {ProgressTracker.CompletedSRankCount} S ranks out of the required {ArchipelagoManager.RequiredSRanks}", () => $"You have completed {ProgressTracker.CompletedBonusObjectives} Bonus Objectives out of the required {ArchipelagoManager.RequiredBonusObjectives}" }; public static string GetCurrentHint() { hintIndex = (hintIndex + 1) % hints.Count; return hints[hintIndex](); } } } namespace IAmYourBeastArchipelago.Patches { [HarmonyPatch(typeof(LevelData), "SetLevelCompleted")] public static class LevelCompletePatch { private static void Postfix(LevelData __instance) { if (__instance != null) { ProgressTracker.SendLevelCheck(__instance); } } } [HarmonyPatch(typeof(LevelData), "SetSnowmanDestroyed")] public static class SnowmanDestroyedPatch { private static void Postfix(LevelData __instance) { if (__instance != null) { ProgressTracker.SendSnowmanCheck(__instance); } } } [HarmonyPatch(typeof(UILevelCompleteTimeScoreBar), "Update")] public static class GradeUpdatePatch { private static void Postfix() { LevelInformation information = GameManager.instance.levelController.GetInformationSetter().GetInformation(); if (!((Object)(object)information == (Object)null)) { LevelData levelData = GameManager.instance.progressManager.GetLevelData(information); if (levelData != null && levelData.GetCurrentGrade() >= 4) { ProgressTracker.SendMaxGradeCheck(levelData); } } } } [HarmonyPatch(typeof(LevelObjectiveManager), "TryAdvanceBonusObjective")] public static class BonusObjectiveAdvancePatch { private static void Prefix(LevelObjectiveManager __instance) { int currentBonusObjectiveIndex = __instance.GetCurrentBonusObjectiveIndex(); LevelObjective currentBonusObjective = __instance.GetCurrentBonusObjective(); if ((Object)(object)currentBonusObjective == (Object)null) { return; } LevelInformation information = GameManager.instance.levelController.GetInformationSetter().GetInformation(); if (!((Object)(object)information == (Object)null)) { LevelData levelData = GameManager.instance.progressManager.GetLevelData(information); if (levelData != null) { ProgressTracker.SendBonusObjectiveCheck(levelData, currentBonusObjectiveIndex); } } } } [HarmonyPatch(typeof(SceneInformation), "IsUnlocked")] public static class LevelUnlockGatePatch { private static void Postfix(SceneInformation __instance, ref bool __result) { if ((Object)(object)__instance == (Object)null) { __result = false; return; } LevelInformation val = (LevelInformation)(object)((__instance is LevelInformation) ? __instance : null); if (val == null) { __result = false; return; } ProgressManager val2 = GameManager.instance?.progressManager; if ((Object)(object)GameManager.instance == (Object)null && (Object)(object)val2 == (Object)null) { __result = false; return; } LevelData levelData = val2.GetLevelData(val); if (levelData == null) { __result = false; return; } ProgressTracker.LevelKey key = new ProgressTracker.LevelKey(levelData.GetCategory(), levelData.GetID()); __result = ProgressTracker.IsUnlocked(key); } } [HarmonyPatch(typeof(PlayerSpawner), "Spawn")] public static class ApplyPendingTrapsOnSpawnPatch { private static void Postfix() { Player val = GameManager.instance?.player; if (!((Object)(object)val == (Object)null)) { ArchipelagoTrapManager.ApplyPendingTraps(val); } } } [HarmonyPatch(typeof(PlayerArmManager), "EquipWeapon")] public static class DisarmPatch { private static void Prefix(WeaponPickup pickup) { ArchipelagoTrapManager.NotifyWeaponEquipped(pickup); } } public static class PlayerPatches { public static bool IsPlayerActivelyInLevel() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 Player val = GameManager.instance?.player; if ((Object)(object)val == (Object)null) { return false; } if ((Object)(object)val.GetHealthManager() == (Object)null) { return false; } if (val.GetHealthManager().IsDead()) { return false; } LevelController val2 = GameManager.instance?.levelController; if ((Object)(object)val2 == (Object)null) { return false; } if ((int)val2.GetLevelState() != 1) { return false; } return true; } } [HarmonyPatch(typeof(SaveSystem), "GetSaveStateFilePath")] public static class SaveLocationOverridePatch { private static string ApSaveFolder => Path.Combine(Application.persistentDataPath, "..", "ArchipelagoSaves"); private static bool Prefix(ref string __result) { Directory.CreateDirectory(ApSaveFolder); string path = (ArchipelagoManager.IsConnected ? ("IAYBSave_" + ArchipelagoManager.Seed + ".sav") : "IAYBSave_Dummy.sav"); __result = Path.Combine(ApSaveFolder, path); return false; } } [HarmonyPatch(typeof(UILevelSelectCategorySlide), "RefreshValues")] public static class CategoryUnlockUIPatch { private static void Postfix(UILevelSelectCategorySlide __instance) { FieldInfo fieldInfo = AccessTools.Field(typeof(UILevelSelectCategorySlide), "locked"); fieldInfo.SetValue(__instance, false); object? value = AccessTools.Field(typeof(UILevelSelectCategorySlide), "lockedAnchor").GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); object? value2 = AccessTools.Field(typeof(UILevelSelectCategorySlide), "unlockedAnchor").GetValue(__instance); GameObject val2 = (GameObject)((value2 is GameObject) ? value2 : null); if ((Object)(object)val != (Object)null) { val.SetActive(false); } if ((Object)(object)val2 != (Object)null) { val2.SetActive(true); } } } [HarmonyPatch(typeof(UIStartScreenRoot), "StartSceneTransition")] public static class SkipTutorialPatch { private static void Postfix() { UISceneTransitionFade val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { ((UISceneTransition)val).SetDestination("Scenes/UI/Menus/LevelSelect"); } } } [HarmonyPatch(typeof(UIStartScreenRoot), "StartGame")] public static class DisableStartPatch { private static bool Prefix() { if (ArchipelagoManager.IsConnected) { return true; } UIHelpers.DisplayMessage("You have to connect to an Archipelago session before staring the game.", error: true); return false; } } [HarmonyPatch(typeof(UIHintDisplay), "Start")] internal class UIHintDisplay_Start_Patch { private static void Postfix(UIHintDisplay __instance) { Traverse.Create((object)__instance).Field("timeBeforeRefresh").SetValue((object)5f); } } [HarmonyPatch(typeof(HintManager), "GetCurrentHint")] public static class HintOverridePatch { private static void Postfix(ref string __result) { __result = HintHelper.GetCurrentHint(); } } } namespace IAmYourBeastArchipelago.Archipelago { public static class ArchipelagoManager { private const string GameName = "IAmYourBeast"; public static ArchipelagoSession Session; public static bool IsConnected; public static Dictionary SlotData; public static string Seed; public static int RequiredLevels => (SlotData != null) ? Convert.ToInt32(SlotData["required_levels"]) : 0; public static int RequiredSRanks => (SlotData != null) ? Convert.ToInt32(SlotData["required_s_ranks"]) : 0; public static int RequiredBonusObjectives => (SlotData != null) ? Convert.ToInt32(SlotData["required_bonus_objectives"]) : 0; public static void Connect(string host, string slot, string password = null) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown Session = ArchipelagoSessionFactory.CreateSession(host, 38281); LoginResult val = Session.TryConnectAndLogin("IAmYourBeast", slot, (ItemsHandlingFlags)7, new Version(0, 6, 7), (string[])null, (string)null, password, true); if (!val.Successful) { LoginFailure val2 = (LoginFailure)val; return; } IsConnected = true; LoginSuccessful val3 = (LoginSuccessful)val; SlotData = val3.SlotData; Seed = Session.RoomState.Seed; InitializeListeners(); } public static void Disconnect() { if (Session != null) { Session.Socket.DisconnectAsync(); Session = null; } } public static void SendCheck(string name) { if (Session != null) { long locationIdFromName = Session.Locations.GetLocationIdFromName("IAmYourBeast", name); Session.Locations.CompleteLocationChecksAsync(new long[1] { locationIdFromName }); } } public static void InitializeListeners() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if (Session != null) { Session.Items.ItemReceived += new ItemReceivedHandler(OnItemReceived); } } private static void OnItemReceived(ReceivedItemsHelper helper) { while (helper.Any()) { ItemInfo val = helper.DequeueItem(); int num = (int)val.ItemId; if (num < 100) { HandleLevel(num, val.ItemName); } if (num < 200) { HandleTrap(val.ItemName); } if (num < 300) { HandleFiller(val.ItemName); } } } private static void HandleLevel(int apId, string name) { ProgressTracker.UnlockLevel(name); } private static void HandleTrap(string name) { switch (name) { case "Bleed Trap": ArchipelagoTrapManager.QueueTrap(TrapType.BleedTrap); break; case "Immobilizer Trap": ArchipelagoTrapManager.QueueTrap(TrapType.ImmobilizerTrap); break; case "Disarm Trap": ArchipelagoTrapManager.QueueTrap(TrapType.DisarmTrap); break; } } private static void HandleFiller(string name) { if (name == "Super Strength") { ArchipelagoTrapManager.QueueTrap(TrapType.SuperStrength); } } public static void SendGoalCompletion() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (Session != null) { StatusUpdatePacket val = new StatusUpdatePacket { Status = (ArchipelagoClientState)30 }; Session.Socket.SendPacketAsync((ArchipelagoPacketBase)(object)val); } } } public static class ItemTranslator { private static readonly Dictionary CategoryMap; private static readonly Dictionary ReverseCategoryMap; static ItemTranslator() { CategoryMap = new Dictionary { { "Story", "Main" }, { "Challenge", "Challenge" }, { "Support Group", "PostLaunchPack1" }, { "Cold Sweat", "WIP" } }; ReverseCategoryMap = new Dictionary(); foreach (KeyValuePair item in CategoryMap) { ReverseCategoryMap[item.Value] = item.Key; } } public static string ToArchipelagoCategory(string apCategory) { if (ReverseCategoryMap.TryGetValue(apCategory, out var value)) { return value; } return apCategory; } public static string ToGameCategory(string gameCategory) { if (CategoryMap.TryGetValue(gameCategory, out var value)) { return value; } return gameCategory; } public static (string category, int id) ParseApToLevelKey(string levelName) { string[] array = levelName.Split(new string[1] { " Level " }, StringSplitOptions.None); if (array.Length != 2) { return (category: "", id: -1); } if (!int.TryParse(array[1], out var result)) { return (category: "", id: -1); } if (!CategoryMap.TryGetValue(array[0], out var value)) { return (category: "", id: -1); } return (category: value, id: result); } public static string ParseLevelKeyToAp(ProgressTracker.LevelKey key) { return $"{ToArchipelagoCategory(key.category)} Level {key.id}"; } public static string ParseLevelKeyToMaxGrade(ProgressTracker.LevelKey key) { return ParseLevelKeyToAp(key) + " - S Rank"; } public static string ParseLevelKeyToSnowman(ProgressTracker.LevelKey key) { return ParseLevelKeyToAp(key) + " - Destroy Snowman"; } public static string ParseLevelKeyToBonusObjective(ProgressTracker.LevelKey key, int index) { return $"{ParseLevelKeyToAp(key)} - Bonus Objective {index + 1}"; } } public enum TrapType { BleedTrap, SuperStrength, DisarmTrap, ImmobilizerTrap } public static class ArchipelagoTrapManager { private static int pendingBleedTraps; private static bool pendingSuperStrength; private static int pendingDisarmTraps; private const float ImmobilizeDamage = 0.25f; private const float ImmobilizeDuration = 5f; private static ManualLogSource Log => Plugin.Log; public static void QueueTrap(TrapType trap) { if (PlayerPatches.IsPlayerActivelyInLevel()) { ApplyTrapNow(trap, GameManager.instance.player); return; } switch (trap) { case TrapType.BleedTrap: pendingBleedTraps++; break; case TrapType.SuperStrength: pendingSuperStrength = true; break; case TrapType.DisarmTrap: pendingDisarmTraps++; break; } } private static void ApplyTrapNow(TrapType trap, Player player) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) switch (trap) { case TrapType.SuperStrength: player.SetSuperStrength(true); break; case TrapType.BleedTrap: player.GetHealthManager().StartBleeding(false); break; case TrapType.DisarmTrap: { if (!player.GetArmScript().IsWeaponEquipped()) { pendingDisarmTraps++; break; } WeaponPickup equippedWeapon = player.GetArmScript().GetEquippedWeapon(); player.GetArmScript().TossWeapon(equippedWeapon); break; } case TrapType.ImmobilizerTrap: { PlayerMovement movementScript = player.GetMovementScript(); movementScript.TriggerTimedLockOn(player.GetPosition(), 0.25f, false); player.GetHealthManager().Damage(0.25f, true); movementScript.EndSprint(); PlayerImmobilizerSource source = player.GetImmobilizer().AddImmobilizer(((Component)Plugin.instanceRef).gameObject, true, false, false, false); movementScript.ResetUprightMovement(); ((MonoBehaviour)Plugin.instanceRef).StartCoroutine(ReleaseImmobilizerAfterDelay(player, source, 5f)); break; } } } public static void ApplyPendingTraps(Player player) { if (!((Object)(object)player == (Object)null)) { if (pendingSuperStrength) { player.SetSuperStrength(true); pendingSuperStrength = false; } if (pendingBleedTraps > 0) { int stacks = pendingBleedTraps; pendingBleedTraps = 0; ((MonoBehaviour)Plugin.instanceRef).StartCoroutine(ApplyStackedBleed(stacks)); } } } public static void NotifyWeaponEquipped(WeaponPickup pickup) { if (!((Object)(object)pickup == (Object)null) && pendingDisarmTraps > 0) { pickup.SetLimitedUse(); pendingDisarmTraps--; } } private static IEnumerator ApplyStackedBleed(int stacks) { for (int i = 0; i < stacks; i++) { Player player = GameManager.instance?.player; if ((Object)(object)player == (Object)null || (Object)(object)player.GetHealthManager() == (Object)null || player.GetHealthManager().IsDead()) { break; } player.GetHealthManager().StartBleeding(false); yield return (object)new WaitForSeconds(10f); } } private static IEnumerator ReleaseImmobilizerAfterDelay(Player player, PlayerImmobilizerSource source, float delay) { yield return (object)new WaitForSeconds(delay); Player currentPlayer = GameManager.instance?.player; if ((Object)(object)currentPlayer == (Object)(object)player && (Object)(object)player.GetImmobilizer() != (Object)null) { player.GetImmobilizer().ReduceImmobilizer(source); } } } } namespace IAmYourBeastArchipelago.Core { [BepInPlugin("com.yourname.iayb.archipelago", "IAYB Archipelago Base", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static Plugin instanceRef; private void Awake() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; instanceRef = this; SceneManager.sceneLoaded += UIHelpers.OnSceneLoaded; new Harmony("ap.patch").PatchAll(); } private void Start() { ArchipelagoSession session = ArchipelagoManager.Session; } } public static class ProgressTracker { public record struct LevelKey(string category, int id); public class LevelState { public bool sentLevelCheck = false; public bool sentMaxGradeCheck = false; public bool sentSnowmanCheck = false; public int sentBonusObjectivesUpTo = -1; } public static HashSet unlockedLevels = new HashSet(); public static Dictionary levelStates = new Dictionary(); public static Dictionary AllLevels = new Dictionary(); public static int UnlockedCount => unlockedLevels.Count; public static int CompletedCount => AllLevels.Count((KeyValuePair kv) => kv.Value.GetLevelCompleted()); public static int CompletedSRankCount => AllLevels.Count((KeyValuePair kv) => HasSRank(kv.Value)); public static int CompletedBonusObjectives => AllLevels.Sum((KeyValuePair kv) => BonusCompletedCount(kv.Value)); private static LevelState GetOrCreateState(LevelKey key) { if (!levelStates.TryGetValue(key, out var value)) { value = new LevelState(); levelStates[key] = value; } return value; } private static int BonusCompletedCount(LevelData level) { int currentBonusObjectiveIndex = level.GetCurrentBonusObjectiveIndex(); if (currentBonusObjectiveIndex == -1) { return 0; } return currentBonusObjectiveIndex; } public static void UnlockLevel(string name) { var (text, num) = ItemTranslator.ParseApToLevelKey(name); Plugin.Log.LogInfo((object)$"Unlocking level: {text} - {num}"); LevelKey item = new LevelKey(text, num); unlockedLevels.Add(item); } public static bool IsUnlocked(LevelKey key) { return unlockedLevels.Contains(key); } public static void SendLevelCheck(LevelData level) { if (level == null) { return; } LevelKey key = new LevelKey(level.GetCategory(), level.GetID()); LevelState orCreateState = GetOrCreateState(key); if (!orCreateState.sentLevelCheck) { orCreateState.sentLevelCheck = true; ArchipelagoManager.SendCheck(ItemTranslator.ParseLevelKeyToAp(key)); if (CheckCompletion()) { ArchipelagoManager.SendGoalCompletion(); } } } public static void SendMaxGradeCheck(LevelData level) { if (level == null) { return; } LevelKey key = new LevelKey(level.GetCategory(), level.GetID()); LevelState orCreateState = GetOrCreateState(key); if (!orCreateState.sentMaxGradeCheck) { orCreateState.sentMaxGradeCheck = true; ArchipelagoManager.SendCheck(ItemTranslator.ParseLevelKeyToMaxGrade(key)); if (CheckCompletion()) { ArchipelagoManager.SendGoalCompletion(); } } } public static void SendSnowmanCheck(LevelData level) { if (level != null) { LevelKey key = new LevelKey(level.GetCategory(), level.GetID()); LevelState orCreateState = GetOrCreateState(key); if (!orCreateState.sentSnowmanCheck) { orCreateState.sentSnowmanCheck = true; ArchipelagoManager.SendCheck(ItemTranslator.ParseLevelKeyToSnowman(key)); } } } public static void SendBonusObjectiveCheck(LevelData level, int bonusIndex) { if (level == null) { return; } LevelKey key = new LevelKey(level.GetCategory(), level.GetID()); if (bonusIndex != level.GetCurrentBonusObjectiveIndex()) { return; } LevelState orCreateState = GetOrCreateState(key); if (bonusIndex > orCreateState.sentBonusObjectivesUpTo) { orCreateState.sentBonusObjectivesUpTo = bonusIndex; ArchipelagoManager.SendCheck(ItemTranslator.ParseLevelKeyToBonusObjective(key, bonusIndex)); if (CheckCompletion()) { ArchipelagoManager.SendGoalCompletion(); } } } public static bool CheckCompletion() { FillAllLevels(); if (CompletedCount >= ArchipelagoManager.RequiredLevels && CompletedSRankCount >= ArchipelagoManager.RequiredSRanks && CompletedBonusObjectives >= ArchipelagoManager.RequiredBonusObjectives) { return true; } return false; } public static bool HasSRank(LevelData level) { return level.GetCurrentGrade() >= 4; } public static void PopulateProgress() { FillAllLevels(); InitializeLevelStates(); } private static void InitializeLevelStates() { Plugin.Log.LogInfo((object)"Attemptint to initialize Level States"); levelStates.Clear(); foreach (KeyValuePair allLevel in AllLevels) { LevelKey key = allLevel.Key; LevelData value = allLevel.Value; LevelState orCreateState = GetOrCreateState(key); orCreateState.sentLevelCheck = value.GetLevelCompleted(); orCreateState.sentMaxGradeCheck = HasSRank(value); orCreateState.sentSnowmanCheck = value.GetSnowmanDestroyed(); orCreateState.sentBonusObjectivesUpTo = value.GetCurrentBonusObjectiveIndex(); } } public static void FillAllLevels() { ProgressManager val = GameManager.instance?.progressManager; if ((Object)(object)val == (Object)null) { } LevelData[] allLevelData = val.GetAllLevelData(); Plugin.Log.LogInfo((object)$"All levels: {allLevelData.Length}"); AllLevels.Clear(); LevelData[] array = allLevelData; foreach (LevelData val2 in array) { LevelKey key = new LevelKey(val2.GetCategory(), val2.GetID()); AllLevels[key] = val2; } } } }