Decompiled source of AtlyssArchipelago v1.3.7
AtlyssArchipelagoWIP.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Data; using System.Data.SqlTypes; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.WebSockets; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.BounceFeatures.DeathLink; using Archipelago.MultiClient.Net.Colors; using Archipelago.MultiClient.Net.ConcurrentCollection; using Archipelago.MultiClient.Net.Converters; using Archipelago.MultiClient.Net.DataPackage; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Exceptions; using Archipelago.MultiClient.Net.Extensions; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.MessageLog.Parts; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Bson; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq.JsonPath; using Newtonsoft.Json.Schema; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [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: AssemblyVersion("0.0.0.0")] namespace AtlyssArchipelagoWIP { [HarmonyPatch(typeof(Player), "Player_OnDeath")] internal class PlayerDeathPatch { private static void Postfix(Player __instance) { AtlyssArchipelagoPlugin instance = AtlyssArchipelagoPlugin.Instance; if (instance._dlService != null && instance.cfgDeathlink.Value) { if (AtlyssArchipelagoPlugin.reactingToDeathLink > 0) { AtlyssArchipelagoPlugin.reactingToDeathLink--; return; } DeathLink deathLink = new DeathLink(__instance._nickname, __instance._nickname + " was defeated."); instance._dlService.SendDeathLink(deathLink); } } } [HarmonyPatch(typeof(ChatBehaviour), "Send_ChatMessage")] public static class ChatBehaviourPatch { private static bool Prefix(ChatBehaviour __instance, string _message) { try { if (!string.IsNullOrEmpty(_message) && _message.StartsWith("/")) { string[] array = new string[6] { "/release", "/collect", "/hint", "/help", "/players", "/status" }; bool flag = false; string[] array2 = array; foreach (string value in array2) { if (_message.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (flag) { if ((Object)(object)AtlyssArchipelagoPlugin.Instance != (Object)null) { AtlyssArchipelagoPlugin.Instance.HandleArchipelagoCommand(_message); } __instance._focusedInChat = false; return false; } } return true; } catch (Exception ex) { ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogError((object)("[AtlyssAP] Chat patch error: " + ex.Message)); } return true; } } } [HarmonyPatch(typeof(FriendlyNPC_Hitbox), "OnTriggerEnter")] public static class AngelaRudePatch { private static void Postfix(FriendlyNPC_Hitbox __instance) { try { if ((Object)(object)AtlyssArchipelagoPlugin.Instance == (Object)null || !AtlyssArchipelagoPlugin.Instance.connected) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(FriendlyNPC_Hitbox), "_achievementTag"); if (fieldInfo == null) { return; } string text = (string)fieldInfo.GetValue(__instance); if (!(text != "ATLYSS_ACHIEVEMENT_11")) { ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogInfo((object)"[AtlyssAP] Angela 'Rude!' hitbox triggered!"); } AtlyssArchipelagoPlugin.Instance.SendCheckByName("Rude!"); AtlyssArchipelagoPlugin.Instance.SendAPChatMessage("Found <color=#FFFF00>Rude!</color>!"); } } catch (Exception ex) { ManualLogSource staticLogger2 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger2 != null) { staticLogger2.LogError((object)("[AtlyssAP] Angela patch error: " + ex.Message)); } } } } [HarmonyPatch(typeof(GameManager), "Locate_Item")] public static class LocateItemPatch { private static ScriptableItem _fallbackDummyItem = null; private static Sprite _customAPSprite = null; private static Dictionary<string, ScriptableItem> _apItemCache = new Dictionary<string, ScriptableItem>(); private static void Postfix(string _tag, ref ScriptableItem __result) { try { if ((Object)(object)__result != (Object)null || string.IsNullOrEmpty(_tag) || !_tag.StartsWith("[AP]")) { return; } if (_apItemCache.TryGetValue(_tag, out var value)) { __result = value; return; } string text = ExtractItemName(_tag); if (string.IsNullOrEmpty(text)) { AtlyssArchipelagoPlugin.StaticLogger.LogWarning((object)("[AtlyssAP] Could not extract item name from: " + _tag)); __result = GetCustomAPItem("Unknown Item"); _apItemCache[_tag] = __result; return; } ScriptableItem val = FindRealItem(text); if ((Object)(object)val != (Object)null) { _apItemCache[_tag] = val; __result = val; AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Using real item '" + val._itemName + "' icon for: " + _tag)); } else { __result = GetCustomAPItem(text); _apItemCache[_tag] = __result; AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Using custom AP icon for non-ATLYSS item: " + _tag)); } } catch (Exception ex) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Locate_Item patch error: " + ex.Message)); __result = GetCustomAPItem("Unknown Item"); } } public static string ExtractItemName(string apItemName) { string text = apItemName.Replace("[AP] ", ""); int num = text.LastIndexOf(" ("); if (num > 0) { return text.Substring(0, num); } return text; } private static ScriptableItem FindRealItem(string itemName) { if (!AtlyssArchipelagoPlugin.ItemNameMapping.TryGetValue(itemName, out var value)) { return null; } string text = value; int num = value.LastIndexOf('_'); if (num >= 0 && num < value.Length - 1) { text = value.Substring(num + 1); } ScriptableItem val = GameManager._current.Locate_Item(text); if ((Object)(object)val != (Object)null) { return val; } if (text.Contains(" (") && text.EndsWith(")")) { string text2 = text[..text.LastIndexOf(" (")]; val = GameManager._current.Locate_Item(text2); if ((Object)(object)val != (Object)null) { return val; } } string text3 = text.Replace(" ", ""); val = GameManager._current.Locate_Item(text3); if ((Object)(object)val != (Object)null) { return val; } return null; } private static ScriptableItem GetCustomAPItem(string itemName = "Archipelago Item") { ScriptableItem fallbackDummy = GetFallbackDummy(); if ((Object)(object)fallbackDummy != (Object)null) { ScriptableItem val = Object.Instantiate<ScriptableItem>(fallbackDummy); val._itemName = itemName; Sprite val2 = LoadCustomAPSprite(); if ((Object)(object)val2 != (Object)null) { TrySetItemIcon(val, val2); if ((Object)(object)_customAPSprite == (Object)null) { AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)"[AtlyssAP] Loaded custom Archipelago icon successfully!"); } _customAPSprite = val2; } else { AtlyssArchipelagoPlugin.StaticLogger.LogWarning((object)"[AtlyssAP] Failed to load custom icon, using base item icon"); } return val; } AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] Could not create custom AP item - no base item available"); return null; } private static Sprite LoadCustomAPSprite() { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_customAPSprite != (Object)null) { return _customAPSprite; } try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); string text = null; string[] array = manifestResourceNames; foreach (string text2 in array) { if (text2.EndsWith("Archipelago.png-256x256_q95.png")) { text = text2; break; } } if (string.IsNullOrEmpty(text)) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] Could not find Archipelago.png-256x256_q95.png in embedded resources"); AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Available resources: " + string.Join(", ", manifestResourceNames))); return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] Failed to open embedded resource stream"); return null; } byte[] array2 = new byte[stream.Length]; stream.Read(array2, 0, array2.Length); Texture2D val = new Texture2D(2, 2); if (!ImageConversion.LoadImage(val, array2)) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] Failed to load image data into texture"); return null; } Sprite result = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)$"[AtlyssAP] Successfully loaded custom AP sprite: {((Texture)val).width}x{((Texture)val).height}"); return result; } catch (Exception ex) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Error loading custom sprite: " + ex.Message)); return null; } } private static void TrySetItemIcon(ScriptableItem item, Sprite sprite) { try { string[] array = new string[6] { "_icon", "_itemIcon", "_sprite", "_itemSprite", "icon", "sprite" }; Type type = ((object)item).GetType(); string[] array2 = array; foreach (string text in array2) { FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(Sprite)) { field.SetValue(item, sprite); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Set item icon using field: " + text)); return; } } AtlyssArchipelagoPlugin.StaticLogger.LogWarning((object)"[AtlyssAP] Could not find icon field on ScriptableItem"); } catch (Exception ex) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Error setting item icon: " + ex.Message)); } } private static ScriptableItem GetFallbackDummy() { if ((Object)(object)_fallbackDummyItem == (Object)null) { _fallbackDummyItem = GameManager._current.Locate_Item("Bunbag"); if ((Object)(object)_fallbackDummyItem == (Object)null) { _fallbackDummyItem = GameManager._current.Locate_Item("Wood Sword"); } if ((Object)(object)_fallbackDummyItem == (Object)null) { _fallbackDummyItem = GameManager._current.Locate_Item("Leather Top"); } } return _fallbackDummyItem; } } [HarmonyPatch(typeof(SettingsManager), "Close_SettingsMenu")] internal class SettingsMenuClosePatch { private static void Prefix(SettingsManager __instance) { if (Object.op_Implicit((Object)(object)GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab"))) { AtlyssArchipelagoPlugin instance = AtlyssArchipelagoPlugin.Instance; Toggle component = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab/_backdrop_gameTab/Scroll View_gameTab/Viewport_gameTab/Content_gameTab/_cell_fadeGameFeed/Toggle_fadeGameFeed").GetComponent<Toggle>(); if (component.isOn != instance.cfgDeathlink.Value) { instance.ToggleDeathLink(component.isOn); } instance.cfgDeathlink.Value = component.isOn; } } } [HarmonyPatch(typeof(SettingsManager), "Open_SettingsMenu")] internal class SettingsMenuOpenPatch { private static void Prefix(SettingsManager __instance) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Unknown result type (might be due to invalid IL or missing references) //IL_0561: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab"))) { return; } Scene sceneByName = SceneManager.GetSceneByName("01_rootScene"); if (((Scene)(ref sceneByName)).isLoaded) { bool flag = false; if (!Object.op_Implicit((Object)(object)GameObject.Find("_GameUI_MainMenu"))) { flag = true; ((Component)((Component)MainMenuManager._current).gameObject.transform.parent).gameObject.SetActive(true); } AtlyssArchipelagoPlugin instance = AtlyssArchipelagoPlugin.Instance; GameObject val = Object.Instantiate<GameObject>(GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_gameSettings")); ((Object)val).name = "_dolly_apSettingsTab"; val.transform.SetParent(GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu").transform, false); Transform val2 = val.transform.Find("_backdrop_gameTab/Scroll View_gameTab/Viewport_gameTab/Content_gameTab"); ((Component)((Component)val2).transform.Find("_header_gameSettings/Text")).GetComponent<Text>().text = "Archipelago Settings"; Transform child = ((Component)val2).transform.GetChild(1); Transform child2 = ((Component)val2).transform.GetChild(2); Transform child3 = ((Component)val2).transform.GetChild(3); Transform child4 = ((Component)val2).transform.GetChild(21); ((Component)child4).GetComponentInChildren<Text>().text = "DeathLink"; ((Component)child4).GetComponent<SettingsCell>()._setToggle = null; Object.Destroy((Object)(object)((Component)child).transform.GetChild(1)); Object.Destroy((Object)(object)((Component)child2).transform.GetChild(1)); Object.Destroy((Object)(object)((Component)child3).transform.GetChild(1)); ((Component)child).GetComponentInChildren<Text>().text = "Archipelago Server"; ((Component)child2).GetComponentInChildren<Text>().text = "Slot Name"; ((Component)child3).GetComponentInChildren<Text>().text = "Password"; ((Component)((Component)val2).transform.Find("_header_nametagSettings/Text")).GetComponent<Text>().text = "Press F5 at any time to connect!"; for (int num = val2.childCount - 3; num >= 5; num--) { GameObject gameObject = ((Component)val2.GetChild(num)).gameObject; Object.Destroy((Object)(object)gameObject); } Transform val3 = Object.Instantiate<Transform>(GameObject.Find("_GameUI_MainMenu").transform.Find("_characterSelectMenu/Canvas_characterSelect/_dolly_characterManagement/_input_@nickname")); ((Object)val3).name = "Input_apServer"; Transform val4 = Object.Instantiate<Transform>(GameObject.Find("_GameUI_MainMenu").transform.Find("_characterSelectMenu/Canvas_characterSelect/_dolly_characterManagement/_input_@nickname")); ((Object)val4).name = "Input_apSlot"; Transform val5 = Object.Instantiate<Transform>(GameObject.Find("_GameUI_MainMenu").transform.Find("_characterSelectMenu/Canvas_characterSelect/_dolly_characterManagement/_input_@nickname")); ((Object)val5).name = "Input_apPassword"; ((Component)val3).gameObject.SetActive(true); ((Component)val4).gameObject.SetActive(true); ((Component)val5).gameObject.SetActive(true); Object.Destroy((Object)(object)((Component)val3).transform.Find("_backdrop_globalNicknameTooltip")); Object.Destroy((Object)(object)((Component)val4).transform.Find("_backdrop_globalNicknameTooltip")); Object.Destroy((Object)(object)((Component)val5).transform.Find("_backdrop_globalNicknameTooltip")); ((Component)val3).transform.SetParent(((Component)child).transform, false); ((Component)val4).transform.SetParent(((Component)child2).transform, false); ((Component)val5).transform.SetParent(((Component)child3).transform, false); ((Component)val3).transform.localPosition = new Vector3(180f, 0f, 0f); ((Component)val4).transform.localPosition = new Vector3(180f, 0f, 0f); ((Component)val5).transform.localPosition = new Vector3(180f, 0f, 0f); instance.apServer = ((Component)val3).GetComponent<InputField>(); instance.apSlot = ((Component)val4).GetComponent<InputField>(); instance.apPassword = ((Component)val5).GetComponent<InputField>(); instance.apDeathlink = ((Component)((Component)child4).transform.Find("Toggle_fadeGameFeed")).gameObject.GetComponent<Toggle>(); ((Text)instance.apServer.placeholder).text = "archipelago.gg:38281"; ((Text)instance.apSlot.placeholder).text = "ATLYSSPlayer"; ((Text)instance.apPassword.placeholder).text = "supersecret"; instance.apServer.text = instance.cfgServer.Value; instance.apSlot.text = instance.cfgSlot.Value; instance.apPassword.text = instance.cfgPassword.Value; instance.apDeathlink.isOn = instance.cfgDeathlink.Value; GameObject val6 = Object.Instantiate<GameObject>(GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_tabButtons/Button_gameTab")); ((Object)val6).name = "Button_apTab"; val6.GetComponentInChildren<Text>().text = "Archipelago"; GameObject val7 = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_tabButtons"); val6.transform.SetParent(val7.transform, false); ((HorizontalOrVerticalLayoutGroup)val7.GetComponent<HorizontalLayoutGroup>()).childScaleWidth = true; ((HorizontalOrVerticalLayoutGroup)val7.GetComponent<HorizontalLayoutGroup>()).childControlWidth = true; ((LayoutGroup)val7.GetComponent<HorizontalLayoutGroup>()).padding.right = 8; val7.SetActive(false); instance.ReenableSettingsTabs(); ((UnityEvent)val6.GetComponent<Button>().onClick).AddListener((UnityAction)delegate { __instance.Set_SettingMenuSelectionIndex(4); }); if (flag) { ((Component)((Component)MainMenuManager._current).gameObject.transform.parent).gameObject.SetActive(false); } } } } [HarmonyPatch(typeof(SettingsManager), "Set_SettingMenuSelectionIndex")] internal class SettingsMenuTabsPatch { private static void Postfix(SettingsManager __instance, int _index) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0080: Unknown result type (might be due to invalid IL or missing references) Scene sceneByName = SceneManager.GetSceneByName("01_rootScene"); if (((Scene)(ref sceneByName)).isLoaded) { if (_index == 4) { MenuElement component = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab").GetComponent<MenuElement>(); RectTransform component2 = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab/_backdrop_gameTab/Scroll View_gameTab/Viewport_gameTab/Content_gameTab").GetComponent<RectTransform>(); GameObject val = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_selectHighlight_tabButtons"); component.isEnabled = true; component2.anchoredPosition = Vector2.zero; val.transform.position = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_tabButtons/Button_apTab").transform.position; } else { MenuElement component3 = GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_apSettingsTab").GetComponent<MenuElement>(); component3.isEnabled = false; } } } } [HarmonyPatch(typeof(NetNPC), "Init_ShopkeepListing")] public static class ShopInventoryPatch { private static void Postfix(NetNPC __instance) { try { if (!AtlyssArchipelagoPlugin.Instance.connected || AtlyssArchipelagoPlugin.Instance._shopSanity == null || !AtlyssArchipelagoPlugin.Instance._shopSanity.IsInitialized) { return; } string name = ((Object)((Component)__instance).gameObject).name; string[] array = new string[12] { "_npc_Sally", "_npc_Skrit", "_npc_sallyWorker_frankie_01", "_npc_Craig", "_npc_dyeMerchant", "_npc_Tesh", "_npc_Nesh", "_npc_Rikko", "_npc_Cotoo", "_npc_Ruka", "_npc_fisher", "_npc_madStatue" }; bool flag = false; string[] array2 = array; foreach (string text in array2) { if (name == text) { flag = true; break; } } if (!flag) { return; } AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] AP merchant shop opened: " + name + " - injecting items")); AtlyssArchipelagoPlugin.Instance._shopSanity.InjectAPShopItems(__instance); try { ShopkeepManager current = ShopkeepManager._current; if (!((Object)(object)current != (Object)null)) { return; } MethodInfo method = typeof(ShopkeepManager).GetMethod("Begin_ShopkeepListing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 1) { method.Invoke(current, new object[1] { __instance }); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)"[AtlyssAP] Refreshed shop display via Begin_ShopkeepListing(npc)"); } else if (parameters.Length == 0) { method.Invoke(current, null); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)"[AtlyssAP] Refreshed shop display via Begin_ShopkeepListing()"); } return; } string[] array3 = new string[6] { "Refresh_ShopListing", "Init_ShopListing", "Update_ShopListing", "Build_ShopListing", "Begin_ShopListing", "Set_ShopListing" }; string[] array4 = array3; foreach (string text2 in array4) { MethodInfo method2 = typeof(ShopkeepManager).GetMethod(text2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { ParameterInfo[] parameters2 = method2.GetParameters(); if (parameters2.Length == 1 && parameters2[0].ParameterType == typeof(NetNPC)) { method2.Invoke(current, new object[1] { __instance }); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Refreshed shop display via " + text2 + "(npc)")); break; } if (parameters2.Length == 0) { method2.Invoke(current, null); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Refreshed shop display via " + text2 + "()")); break; } } } } catch (Exception ex) { AtlyssArchipelagoPlugin.StaticLogger.LogWarning((object)("[AtlyssAP] Could not refresh shop display: " + ex.Message)); } } catch (Exception ex2) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Shop patch error: " + ex2.Message)); AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Stack trace: " + ex2.StackTrace)); } } } [HarmonyPatch(typeof(ShopkeepManager), "Init_PurchaseItem")] public static class ShopPurchasePatch { private static bool Prefix(ScriptableItem _scriptableItem, int _quantity, string _key, ShopListDataEntry _setEntry) { try { if (!AtlyssArchipelagoPlugin.Instance.connected) { return true; } if (AtlyssArchipelagoPlugin.Instance._shopSanity == null) { return true; } if (!AtlyssArchipelagoPlugin.Instance._shopSanity.IsInitialized) { return true; } string itemName = _setEntry._shopStruct._itemName; if (!itemName.StartsWith("[AP]")) { return true; } AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)("[AtlyssAP] Intercepting purchase of: " + itemName)); Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] Player not found for purchase!"); return false; } PlayerInventory component = ((Component)mainPlayer).GetComponent<PlayerInventory>(); if ((Object)(object)component == (Object)null) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)"[AtlyssAP] PlayerInventory not found!"); return false; } int num = _setEntry._shopStruct._dedicatedValue; if (_setEntry._shopStruct._useDedicatedValue) { num = _setEntry._shopStruct._dedicatedValue; } else if ((Object)(object)_scriptableItem != (Object)null) { num = _scriptableItem._vendorCost; } if (component._heldCurrency < num) { ErrorPromptTextManager.current.Init_ErrorPrompt("Not enough Crowns"); AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)"[AtlyssAP] Player cannot afford AP item"); return false; } component.Network_heldCurrency -= num; AtlyssArchipelagoPlugin.StaticLogger.LogInfo((object)$"[AtlyssAP] Deducted {num} crowns from player"); component.Play_PurchaseSound(); AtlyssArchipelagoPlugin.Instance._shopSanity.HandleAPItemPurchase(itemName, _key); return false; } catch (Exception ex) { AtlyssArchipelagoPlugin.StaticLogger.LogError((object)("[AtlyssAP] Shop purchase patch error: " + ex.Message)); return true; } } } public class SpikePatch { [HarmonyPatch(typeof(File), "ReadAllText", new Type[] { typeof(string) })] public static class File_ReadAllText_Patch { private static bool Prefix(ref string path, ref string __result) { try { if (!ShouldRedirect()) { return true; } if (path.EndsWith("atl_itemBank") && !path.Contains("_ap")) { string aPMasterBankPath = ArchipelagoSpikeStorage.GetAPMasterBankPath(); if (File.Exists(aPMasterBankPath)) { __result = File.ReadAllText(aPMasterBankPath); ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogInfo((object)"[AtlyssAP] Redirected Spike load: MASTER bank -> AP MASTER bank"); } return false; } __result = "{ \"_heldItemStorage\": [] }"; return false; } if (path.Contains("atl_itemBank_") && !path.Contains("_ap_")) { string fileName = Path.GetFileName(path); for (int i = 1; i <= 7; i++) { if (!(fileName == $"atl_itemBank_{i:D2}")) { continue; } string aPBankPath = ArchipelagoSpikeStorage.GetAPBankPath(i); if (File.Exists(aPBankPath)) { __result = File.ReadAllText(aPBankPath); ManualLogSource staticLogger2 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger2 != null) { staticLogger2.LogInfo((object)$"[AtlyssAP] Redirected Spike load: bank {i} -> AP bank {i}"); } return false; } __result = "{ \"_heldItemStorage\": [] }"; return false; } } return true; } catch (Exception ex) { ManualLogSource staticLogger3 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger3 != null) { staticLogger3.LogError((object)("[AtlyssAP] Error in File.ReadAllText patch: " + ex.Message)); } return true; } } } [HarmonyPatch(typeof(File), "WriteAllText", new Type[] { typeof(string), typeof(string) })] public static class File_WriteAllText_Patch { private static bool Prefix(ref string path, string contents) { try { if (!ShouldRedirect()) { return true; } if (path.EndsWith("atl_itemBank") && !path.Contains("_ap")) { string aPMasterBankPath = ArchipelagoSpikeStorage.GetAPMasterBankPath(); string directoryName = Path.GetDirectoryName(aPMasterBankPath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(aPMasterBankPath, contents); ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogInfo((object)"[AtlyssAP] Redirected Spike save: MASTER bank -> AP MASTER bank"); } return false; } if (path.Contains("atl_itemBank_") && !path.Contains("_ap_")) { string fileName = Path.GetFileName(path); for (int i = 1; i <= 7; i++) { if (fileName == $"atl_itemBank_{i:D2}") { string aPBankPath = ArchipelagoSpikeStorage.GetAPBankPath(i); File.WriteAllText(aPBankPath, contents); ManualLogSource staticLogger2 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger2 != null) { staticLogger2.LogInfo((object)$"[AtlyssAP] Redirected Spike save: bank {i} -> AP bank {i}"); } return false; } } } return true; } catch (Exception ex) { ManualLogSource staticLogger3 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger3 != null) { staticLogger3.LogError((object)("[AtlyssAP] Error in File.WriteAllText patch: " + ex.Message)); } return true; } } } [HarmonyPatch(typeof(ItemStorageManager), "Create_StorageEntry")] public static class CreateStorageEntry_SafetyPatch { private static bool Prefix(ItemStorageManager __instance, ItemData _itemData, ScriptableItem _scriptItem, int _index, int _slotNumber) { try { FieldInfo field = typeof(ItemStorageManager).GetField("_currentStorageEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.GetValue(__instance) is Array array && (_slotNumber < 0 || _slotNumber >= array.Length)) { ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogWarning((object)($"[AtlyssAP] Skipping storage entry: slot {_slotNumber} out of bounds " + $"(max {array.Length}) for item '{_itemData?._itemName}'")); } return false; } return true; } catch (Exception ex) { ManualLogSource staticLogger2 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger2 != null) { staticLogger2.LogError((object)("[AtlyssAP] Create_StorageEntry safety check error: " + ex.Message)); } return true; } } } private static bool ShouldRedirect() { if ((Object)(object)AtlyssArchipelagoPlugin.Instance != (Object)null && AtlyssArchipelagoPlugin.Instance.connected) { return true; } if (ArchipelagoSpikeStorage.IsAPSessionActive()) { return true; } return false; } public static void InitializeAPStorage() { try { ArchipelagoSpikeStorage.SetAPSessionActive(); if (!ArchipelagoSpikeStorage.AreAPBanksInitialized()) { ArchipelagoSpikeStorage.InitializeAPBanks(); ManualLogSource staticLogger = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger != null) { staticLogger.LogInfo((object)"[AtlyssAP] Initialized AP Spike storage (separate from vanilla)"); } } else { int totalItemCount = ArchipelagoSpikeStorage.GetTotalItemCount(); ManualLogSource staticLogger2 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger2 != null) { staticLogger2.LogInfo((object)$"[AtlyssAP] AP Spike storage loaded ({totalItemCount} items stored)"); } } } catch (Exception ex) { ManualLogSource staticLogger3 = AtlyssArchipelagoPlugin.StaticLogger; if (staticLogger3 != null) { staticLogger3.LogError((object)("[AtlyssAP] Failed to initialize AP storage: " + ex.Message)); } } } } [BepInPlugin("com.azrael.atlyss.ap", "Atlyss Archipelago", "1.3.1")] public class AtlyssArchipelagoPlugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <EnableSettingsTabs>d__134 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public AtlyssArchipelagoPlugin <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnableSettingsTabs>d__134(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: <>1__state = -1; GameObject.Find("_SettingsManager/Canvas_SettingsMenu/_dolly_settingsMenu/_dolly_tabButtons").SetActive(true); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private bool _goalSent = false; private float _lastAllQuestsCheck = 0f; private bool _allQuestsDiagLogged = false; public static readonly Dictionary<string, long> AllLocationNameToId = new Dictionary<string, long> { { "A Warm Welcome", 1L }, { "Communing Catacombs", 2L }, { "Diva Must Die", 3L }, { "The Keep Within", 4L }, { "Tethering Grove", 5L }, { "The Glyphik Booklet", 6L }, { "Cleaning Terrace", 7L }, { "Ancient Beings", 8L }, { "Wicked Wizboars", 9L }, { "Spiraling In The Grove", 10L }, { "Hell In The Grove", 11L }, { "Nulversa Magica", 12L }, { "Finding Ammagon", 13L }, { "The Colossus", 14L }, { "Night Spirits", 15L }, { "Ridding Slimes", 16L }, { "Huntin' Hogs", 17L }, { "Purging the Grove", 18L }, { "Cleansing the Grove", 19L }, { "Nulversa Viscera", 20L }, { "Call of Fury", 21L }, { "Mastery of Strength", 22L }, { "Beckoning Foes", 23L }, { "Ghostly Goods", 24L }, { "Makin' a Mekspear", 25L }, { "Makin' a Wizwand", 26L }, { "Makin' a Vile Blade", 27L }, { "Makin' a Golem Chestpiece", 28L }, { "Makin' a Ragespear", 29L }, { "Makin' a Monolith Chestpiece", 30L }, { "Makin' a Firebreath Blade", 31L }, { "Makin' a Follycannon", 32L }, { "Summore' Spectral Powder!", 33L }, { "Makin' More Mekspears", 34L }, { "Makin' More Wizwands", 35L }, { "Makin' More Vile Blades", 36L }, { "Summore' Golem Chestpieces", 37L }, { "Makin' More Ragespears", 38L }, { "Summore' Monolith Chestpieces", 39L }, { "Nulversa, Greenversa!", 40L }, { "Summore' Firebreath Blades", 41L }, { "Makin' More Follycannons", 42L }, { "Focusin' in", 43L }, { "Mastery of Dexterity", 44L }, { "Whatta' Rush!", 45L }, { "The Voice of Zuulneruda", 46L }, { "Killing Tomb", 47L }, { "Purging the Undead", 48L }, { "Rattlecage Rage", 49L }, { "Consumed Madness", 50L }, { "Eradicating the Undead", 51L }, { "Reviling the Rageboars", 52L }, { "Gatling Galius", 53L }, { "Reviling more Rageboars", 54L }, { "Facing Foes", 55L }, { "The Gall of Galius", 56L }, { "Dense Ingots", 57L }, { "Amberite Ingots", 58L }, { "Sapphite Ingots", 59L }, { "Reach Level 2", 60L }, { "Reach Level 4", 61L }, { "Reach Level 6", 62L }, { "Reach Level 8", 63L }, { "Reach Level 10", 64L }, { "Reach Level 12", 65L }, { "Reach Level 14", 66L }, { "Reach Level 16", 67L }, { "Reach Level 18", 68L }, { "Reach Level 20", 69L }, { "Reach Level 22", 70L }, { "Reach Level 24", 71L }, { "Reach Level 26", 72L }, { "Reach Level 28", 73L }, { "Reach Level 30", 74L }, { "Reach Level 32", 75L }, { "Buy Item #1 from Sally's Nook", 76L }, { "Buy Item #2 from Sally's Nook", 77L }, { "Buy Item #3 from Sally's Nook", 78L }, { "Buy Item #4 from Sally's Nook", 79L }, { "Buy Item #5 from Sally's Nook", 80L }, { "Buy Item #1 from Skrit's Sikrit Market", 81L }, { "Buy Item #2 from Skrit's Sikrit Market", 82L }, { "Buy Item #3 from Skrit's Sikrit Market", 83L }, { "Buy Item #4 from Skrit's Sikrit Market", 84L }, { "Buy Item #5 from Skrit's Sikrit Market", 85L }, { "Buy Item #1 from Frankie's Goods", 86L }, { "Buy Item #2 from Frankie's Goods", 87L }, { "Buy Item #3 from Frankie's Goods", 88L }, { "Buy Item #4 from Frankie's Goods", 89L }, { "Buy Item #5 from Frankie's Goods", 90L }, { "Buy Item #1 from Dye Merchant", 91L }, { "Buy Item #2 from Dye Merchant", 92L }, { "Buy Item #3 from Dye Merchant", 93L }, { "Buy Item #4 from Dye Merchant", 94L }, { "Buy Item #5 from Dye Merchant", 95L }, { "Buy Item #1 from Tesh's Wares", 96L }, { "Buy Item #2 from Tesh's Wares", 97L }, { "Buy Item #3 from Tesh's Wares", 98L }, { "Buy Item #4 from Tesh's Wares", 99L }, { "Buy Item #5 from Tesh's Wares", 100L }, { "Buy Item #1 from Nesh's Wares", 101L }, { "Buy Item #2 from Nesh's Wares", 102L }, { "Buy Item #3 from Nesh's Wares", 103L }, { "Buy Item #4 from Nesh's Wares", 104L }, { "Buy Item #5 from Nesh's Wares", 105L }, { "Buy Item #1 from Rikko's Treasures", 106L }, { "Buy Item #2 from Rikko's Treasures", 107L }, { "Buy Item #3 from Rikko's Treasures", 108L }, { "Buy Item #4 from Rikko's Treasures", 109L }, { "Buy Item #5 from Rikko's Treasures", 110L }, { "Buy Item #1 from Cotoo's Treasures", 111L }, { "Buy Item #2 from Cotoo's Treasures", 112L }, { "Buy Item #3 from Cotoo's Treasures", 113L }, { "Buy Item #4 from Cotoo's Treasures", 114L }, { "Buy Item #5 from Cotoo's Treasures", 115L }, { "Buy Item #1 from Ruka's Furnace", 116L }, { "Buy Item #2 from Ruka's Furnace", 117L }, { "Buy Item #3 from Ruka's Furnace", 118L }, { "Buy Item #4 from Ruka's Furnace", 119L }, { "Buy Item #5 from Ruka's Furnace", 120L }, { "Buy Item #1 from Torta's Fishing Shack", 121L }, { "Buy Item #2 from Torta's Fishing Shack", 122L }, { "Buy Item #3 from Torta's Fishing Shack", 123L }, { "Buy Item #4 from Torta's Fishing Shack", 124L }, { "Buy Item #5 from Torta's Fishing Shack", 125L }, { "Buy Item #1 from Mad Statue's Gift", 126L }, { "Buy Item #2 from Mad Statue's Gift", 127L }, { "Buy Item #3 from Mad Statue's Gift", 128L }, { "Buy Item #4 from Mad Statue's Gift", 129L }, { "Buy Item #5 from Mad Statue's Gift", 130L }, { "Fishing Lv. 1", 131L }, { "Fishing Lv. 2", 132L }, { "Fishing Lv. 3", 133L }, { "Fishing Lv. 4", 134L }, { "Fishing Lv. 5", 135L }, { "Fishing Lv. 6", 136L }, { "Fishing Lv. 7", 137L }, { "Fishing Lv. 8", 138L }, { "Fishing Lv. 9", 139L }, { "Fishing Lv. 10", 140L }, { "Mining Lv. 1", 141L }, { "Mining Lv. 2", 142L }, { "Mining Lv. 3", 143L }, { "Mining Lv. 4", 144L }, { "Mining Lv. 5", 145L }, { "Mining Lv. 6", 146L }, { "Mining Lv. 7", 147L }, { "Mining Lv. 8", 148L }, { "Mining Lv. 9", 149L }, { "Mining Lv. 10", 150L }, { "A New Journey", 151L }, { "Clearing Catacombs (1-6)", 152L }, { "Clearing Catacombs (6-12)", 153L }, { "Becoming a Fighter", 154L }, { "Becoming a Mystic", 155L }, { "Becoming a Bandit", 156L }, { "Clearing Catacombs (12-18)", 157L }, { "Clearing Grove (15-20)", 158L }, { "Clearing Grove (20-25)", 159L }, { "Judgement", 160L }, { "Corrupted Arcana", 161L }, { "Holier than Thou", 162L }, { "Altered Vision", 163L }, { "Scaling the Tower", 164L }, { "Rude!", 165L }, { "Fashion Sense", 166L }, { "Trout Master", 167L }, { "Skill Student", 168L } }; private static readonly Dictionary<string, long> AllQuestToLocation = new Dictionary<string, long> { { "A Warm Welcome", 1L }, { "Communing Catacombs", 2L }, { "Diva Must Die", 3L }, { "The Keep Within", 4L }, { "Tethering Grove", 5L }, { "The Glyphik Booklet", 6L }, { "Cleaning Terrace", 7L }, { "Ancient Beings", 8L }, { "Wicked Wizboars", 9L }, { "Spiraling In The Grove", 10L }, { "Hell In The Grove", 11L }, { "Nulversa Magica", 12L }, { "Finding Ammagon", 13L }, { "The Colossus", 14L }, { "Night Spirits", 15L }, { "Ridding Slimes", 16L }, { "Huntin' Hogs", 17L }, { "Purging the Grove", 18L }, { "Cleansing the Grove", 19L }, { "Nulversa Viscera", 20L }, { "Call of Fury", 21L }, { "Mastery of Strength", 22L }, { "Beckoning Foes", 23L }, { "Ghostly Goods", 24L }, { "Makin' a Mekspear", 25L }, { "Makin' a Wizwand", 26L }, { "Makin' a Vile Blade", 27L }, { "Makin' a Golem Chestpiece", 28L }, { "Makin' a Ragespear", 29L }, { "Makin' a Monolith Chestpiece", 30L }, { "Makin' a Firebreath Blade", 31L }, { "Makin' a Follycannon", 32L }, { "Summore' Spectral Powder!", 33L }, { "Makin' More Mekspears", 34L }, { "Makin' More Wizwands", 35L }, { "Makin' More Vile Blades", 36L }, { "Summore' Golem Chestpieces", 37L }, { "Makin' More Ragespears", 38L }, { "Summore' Monolith Chestpieces", 39L }, { "Nulversa, Greenversa!", 40L }, { "Summore' Firebreath Blades", 41L }, { "Makin' More Follycannons", 42L }, { "Focusin' in", 43L }, { "Mastery of Dexterity", 44L }, { "Whatta' Rush!", 45L }, { "The Voice of Zuulneruda", 46L }, { "Killing Tomb", 47L }, { "Purging the Undead", 48L }, { "Rattlecage Rage", 49L }, { "Consumed Madness", 50L }, { "Eradicating the Undead", 51L }, { "Reviling the Rageboars", 52L }, { "Gatling Galius", 53L }, { "Reviling more Rageboars", 54L }, { "Facing Foes", 55L }, { "The Gall of Galius", 56L }, { "Dense Ingots", 57L }, { "Amberite Ingots", 58L }, { "Sapphite Ingots", 59L } }; public static readonly Dictionary<string, string> ItemNameMapping = new Dictionary<string, string> { { "Bunbag Pack", "(lv-0) STATUSCONSUMABLE_Bunbag" }, { "Bunjar Pack", "(lv-0) STATUSCONSUMABLE_Bunjar" }, { "Bunpot Pack", "(lv-0) STATUSCONSUMABLE_Bunpot" }, { "Regen Potion Pack", "(lv-10) STATUSCONSUMABLE_Regen Potion" }, { "Regen Vial Pack", "(lv-0) STATUSCONSUMABLE_Regen Vial" }, { "Magiclove Pack", "(lv-0) STATUSCONSUMABLE_Magiclove" }, { "Magiflower Pack", "(lv-0) STATUSCONSUMABLE_Magiflower" }, { "Magileaf Pack", "(lv-0) STATUSCONSUMABLE_Magileaf" }, { "Stamstar", "(lv-0) STATUSCONSUMABLE_Stamstar" }, { "Agility Potion Pack", "(lv-10) STATUSCONSUMABLE_Agility Potion" }, { "Agility Vial Pack", "(lv-0) STATUSCONSUMABLE_Agility Vial" }, { "Bolster Potion Pack", "(lv-10) STATUSCONSUMABLE_Bolster Potion" }, { "Bolster Vial Pack", "(lv-0) STATUSCONSUMABLE_Bolster Vial" }, { "Wisdom Potion Pack", "(lv-10) STATUSCONSUMABLE_Wisdom Potion" }, { "Wisdom Vial Pack", "(lv-0) STATUSCONSUMABLE_Wisdom Vial" }, { "Tome of Greater Experience", "(lv-0) STATUSCONSUMABLE_Tome of Greater Experience" }, { "Tome of Experience", "(lv-0) STATUSCONSUMABLE_Tome of Experience" }, { "Tome of Lesser Experience", "(lv-0) STATUSCONSUMABLE_Tome of Lesser Experience" }, { "Carrot Cake Pack", "(lv-0) STATUSCONSUMABLE_Carrot Cake" }, { "Minchroom Juice Pack", "(lv-0) STATUSCONSUMABLE_Minchroom Juice" }, { "Spectral Powder Pack", "(lv-0) STATUSCONSUMABLE_Spectral Powder" }, { "Geistlord Badge", "TRADEITEM_Geistlord Badge" }, { "Coldgeist Badge", "TRADEITEM_Coldgeist Badge" }, { "Earthcore Badge", "TRADEITEM_Earthcore Badge" }, { "Windcore Badge", "TRADEITEM_Windcore Badge" }, { "Iron Cluster", "TRADEITEM_Iron Cluster" }, { "Copper Cluster", "TRADEITEM_Copper Cluster" }, { "Mithril Cluster", "TRADEITEM_Mithril Cluster" }, { "Dense Ingot", "TRADEITEM_Dense Ingot" }, { "Sapphite Ingot", "TRADEITEM_Sapphite Ingot" }, { "Amberite Ingot", "TRADEITEM_Amberite Ingot" }, { "Soul Pearl", "TRADEITEM_Soul Pearl" }, { "Experience Bond", "TRADEITEM_Experience Bond" }, { "Crypt Blade", "(lv-2) WEAPON_Crypt Blade (Sword, Strength)" }, { "Femur Club", "(lv-2) WEAPON_Femur Club (Sword, Strength)" }, { "Ironbark Sword", "(lv-2) WEAPON_Ironbark Sword (Sword, Strength)" }, { "Slimecrust Blade", "(lv-2) WEAPON_Slimecrust Blade (Sword, Strength)" }, { "Gilded Sword", "(lv-4) WEAPON_Gilded Sword (Sword, Strength)" }, { "Splitbark Club", "(lv-4) WEAPON_Splitbark Club (Sword, Strength)" }, { "Demicrypt Blade", "(lv-6) WEAPON_Demicrypt Blade (Sword, Strength)" }, { "Dense Mace", "(lv-6) WEAPON_Dense Mace (Sword, Strength)" }, { "Iron Sword", "(lv-6) WEAPON_Iron Sword (Sword, Strength)" }, { "Dawn Mace", "(lv-8) WEAPON_Dawn Mace (Sword, Strength)" }, { "Rude Blade", "(lv-8) WEAPON_Rude Blade (Sword, Strength)" }, { "Vile Blade", "(lv-8) WEAPON_Vile Blade (Sword, Strength)" }, { "Amberite Sword", "(lv-12) WEAPON_Amberite Sword (Sword, Strength)" }, { "Nethercrypt Blade", "(lv-12) WEAPON_Nethercrypt Blade (Sword, Strength)" }, { "Coldgeist Blade", "(lv-16) WEAPON_Coldgeist Blade (Sword, Strength)" }, { "Mithril Sword", "(lv-16) WEAPON_Mithril Sword (Sword, Strength)" }, { "Serrated Blade", "(lv-16) WEAPON_Serrated Blade (Sword, Strength)" }, { "Nulrok Mace", "(lv-20) WEAPON_Nulrok Mace (Sword, Strength)" }, { "Firebreath Blade", "(lv-22) WEAPON_Firebreath Blade (Sword, Strength)" }, { "Valdur Blade", "(lv-24) WEAPON_Valdur Blade (Sword, Strength)" }, { "Fier Blade", "(lv-26) WEAPON_Fier Blade (Sword, Strength)" }, { "Slimek Axehammer", "(lv-4) WEAPON_Slimek Axehammer (Hammer, Strength)" }, { "Dense Hammer", "(lv-6) WEAPON_Dense Hammer (Hammer, Strength)" }, { "Iron Axehammer", "(lv-6) WEAPON_Iron Axehammer (Hammer, Strength)" }, { "Crypt Pounder", "(lv-8) WEAPON_Crypt Pounder (Hammer, Strength)" }, { "Quake Pummeler", "(lv-18) WEAPON_Quake Pummeler (Hammer, Strength)" }, { "Mini Geist Scythe", "(lv-4) WEAPON_Mini Geist Scythe (Greatblade, Strength)" }, { "Geist Scythe", "(lv-6) WEAPON_Geist Scythe (Greatblade, Strength)" }, { "Stone Greatblade", "(lv-8) WEAPON_Stone Greatblade (Greatblade, Strength)" }, { "Amberite Warstar", "(lv-12) WEAPON_Amberite Warstar (Greatblade, Strength)" }, { "Dolkin's Axe", "(lv-12) WEAPON_Dolkin's Axe (Greatblade, Strength)" }, { "Poltergeist Scythe", "(lv-14) WEAPON_Poltergeist Scythe (Greatblade, Strength)" }, { "Coldgeist Punisher", "(lv-16) WEAPON_Coldgeist Punisher (Greatblade, Strength)" }, { "Deadwood Axe", "(lv-16) WEAPON_Deadwood Axe (Greatblade, Strength)" }, { "Mithril Greatsword", "(lv-16) WEAPON_Mithril Greatsword (Greatblade, Strength)" }, { "Deathknight Runeblade", "(lv-22) WEAPON_Deathknight Runeblade (Greatblade, Strength)" }, { "Ryzer Greataxe", "(lv-26) WEAPON_Ryzer Greataxe (Greatblade, Strength)" }, { "Dense Spear", "(lv-6) WEAPON_Dense Spear (Polearm, Strength)" }, { "Iron Spear", "(lv-6) WEAPON_Iron Spear (Polearm, Strength)" }, { "Cryptsinge Halberd", "(lv-8) WEAPON_Cryptsinge Halberd (Polearm, Strength)" }, { "Mekspear", "(lv-8) WEAPON_Mekspear (Polearm, Strength)" }, { "Amberite Halberd", "(lv-12) WEAPON_Amberite Halberd (Polearm, Strength)" }, { "Necroroyal Halberd", "(lv-12) WEAPON_Necroroyal Halberd (Polearm, Strength)" }, { "Sinner Bardiche", "(lv-12) WEAPON_Sinner Bardiche (Polearm, Strength)" }, { "Mithril Halberd", "(lv-16) WEAPON_Mithril Halberd (Polearm, Strength)" }, { "Ragespear", "(lv-16) WEAPON_Ragespear (Polearm, Strength)" }, { "Serrated Spear", "(lv-16) WEAPON_Serrated Spear (Polearm, Strength)" }, { "Sapphite Spear", "(lv-18) WEAPON_Sapphite Spear (Polearm, Strength)" }, { "Nulrok Spear", "(lv-20) WEAPON_Nulrok Spear (Polearm, Strength)" }, { "Cyrotribe Spear", "(lv-22) WEAPON_Cryotribe Spear (Polearm, Strength)" }, { "Flametribe Spear", "(lv-22) WEAPON_Flametribe Spear (Polearm, Strength)" }, { "Marrow Bauble", "(lv-2) WEAPON_Marrow Bauble (Scepter, Mind)" }, { "Splitbark Scepter", "(lv-2) WEAPON_Splitbark Scepter (Scepter, Mind)" }, { "Demicrypt Bauble", "(lv-6) WEAPON_Demicrypt Bauble (Scepter, Mind)" }, { "Iron Scepter", "(lv-6) WEAPON_Iron Scepter (Scepter, Mind)" }, { "Cryo Cane", "(lv-8) WEAPON_Cryo Cane (Scepter, Mind)" }, { "Slime Diva Baton", "(lv-8) WEAPON_Slime Diva Baton (Scepter, Mind)" }, { "Pyre Cane", "(lv-12) WEAPON_Pyre Cane (Scepter, Mind)" }, { "Wizwand", "(lv-12) WEAPON_Wizwand (Scepter, Mind)" }, { "Nethercrypt Bauble", "(lv-14) WEAPON_Nethercrypt Bauble (Scepter, Mind)" }, { "Aquapetal Staff", "(lv-16) WEAPON_Aquapetal Staff (Scepter, Mind)" }, { "Flamepetal Staff", "(lv-16) WEAPON_Flamepetal Staff (Scepter, Mind)" }, { "Mithril Scepter", "(lv-16) WEAPON_Mithril Scepter (Scepter, Mind)" }, { "Sapphite Scepter", "(lv-18) WEAPON_Sapphite Scepter (Scepter, Mind)" }, { "Voalstark Wand", "(lv-24) WEAPON_Voalstark Wand (Scepter, Mind)" }, { "Cryptcall Bell", "(lv-8) WEAPON_Cryptcall Bell (Magic Bell, Mind)" }, { "Iron Bell", "(lv-8) WEAPON_Iron Bell (Magic Bell, Mind)" }, { "Coldgeist Frostcaller", "(lv-16) WEAPON_Coldgeist Frostcaller (Magic Bell, Mind)" }, { "Mithril Bell", "(lv-16) WEAPON_Mithril Bell (Magic Bell, Mind)" }, { "Colossus Tone", "(lv-18) WEAPON_Colossus Tone (Magic Bell, Mind)" }, { "Sapphite Bell", "(lv-18) WEAPON_Sapphite Bell (Magic Bell, Mind)" }, { "Sliemcrust Katars", "(lv-2) WEAPON_Slimecrust Katars (Katars, Dexterity)" }, { "Cryptsinge Katars", "(lv-4) WEAPON_Cryptsinge Katars (Katars, Dexterity)" }, { "Slimek Shivs", "(lv-4) WEAPON_Slimek Shivs (Katars, Dexterity)" }, { "Deathgel Shivs", "(lv-6) WEAPON_Deathgel Shivs (Katars, Dexterity)" }, { "Dense Katars", "(lv-6) WEAPON_Dense Katars (Katars, Dexterity)" }, { "Iron Katars", "(lv-8) WEAPON_Iron Katars (Katars, Dexterity)" }, { "Runic Katars", "(lv-10) WEAPON_Runic Katars (Katars, Dexterity)" }, { "Geistlord Claws", "(lv-12) WEAPON_Geistlord Claws (Katars, Dexterity)" }, { "Hellsludge Shivs", "(lv-14) WEAPON_Hellsludge Shivs (Katars, Dexterity)" }, { "Mithril Katars", "(lv-14) WEAPON_Mithril Katars (Katars, Dexterity)" }, { "Frostbite Claws", "(lv-16) WEAPON_Frostbite Claws (Katars, Dexterity)" }, { "Serrated Knuckles", "(lv-16) WEAPON_Serrated Knuckles (Katars, Dexterity)" }, { "Rummok Bladerings", "(lv-18) WEAPON_Rummok Bladerings (Katars, Dexterity)" }, { "Sapphite Katars", "(lv-18) WEAPON_Sapphite Katars (Katars, Dexterity)" }, { "Golemfist Katars", "(lv-20) WEAPON_Golemfist Katars (Katars, Dexterity)" }, { "Crypt Bow", "(lv-2) WEAPON_Crypt Bow (Bow, Dexterity)" }, { "Demicrypt Bow", "(lv-6) WEAPON_Demicrypt Bow (Bow, Dexterity)" }, { "Iron Bow", "(lv-6) WEAPON_Iron Bow (Bow, Dexterity)" }, { "Mekspike Bow", "(lv-8) WEAPON_Mekspike Bow (Bow, Dexterity)" }, { "Menace Bow", "(lv-8) WEAPON_Menace Bow (Bow, Dexterity)" }, { "Petrified Bow", "(lv-12) WEAPON_Petrified Bow (Bow, Dexterity)" }, { "Mithril Bow", "(lv-14) WEAPON_Mithril Bow (Bow, Dexterity)" }, { "Necroroyal Bow", "(lv-14) WEAPON_Necroroyal Bow (Bow, Dexterity)" }, { "Colgeist Bow", "(lv-16) WEAPON_Coldgeist Bow (Bow, Dexterity)" }, { "Serrated Longbow", "(lv-16) WEAPON_Serrated Longbow (Bow, Dexterity)" }, { "Torrentius Longbow", "(lv-24) WEAPON_Torrentius Longbow (Bow, Dexterity)" }, { "Amberite Boomstick", "(lv-12) WEAPON_Amberite Boomstick (Shotgun, Dexterity)" }, { "Magitek Burstgun", "(lv-20) WEAPON_Magitek Burstgun (Shotgun, Dexterity)" }, { "Follycannon", "(lv-26) WEAPON_Follycannon (Shotgun, Dexterity)" }, { "Agility Ears", "(lv-1) HELM_Agility Ears" }, { "Festive Hat", "(lv-1) HELM_Festive Hat" }, { "Fishin Hat", "(lv-1) HELM_Fishin Hat" }, { "Leather Cap", "(lv-1) HELM_Leather Cap" }, { "Newfold Halo", "(lv-1) HELM_Newfold Halo" }, { "Orefinder Hat", "(lv-1) HELM_Orefinder Hat" }, { "Spooky Hat", "(lv-1) HELM_Spooky Hat" }, { "Top Hat", "(lv-1) HELM_Top Hat" }, { "Wizard Hat", "(lv-1) HELM_Wizard Hat" }, { "Acolyte Hood", "(lv-4) HELM_Acolyte Hood" }, { "Cryptsinge Halo", "(lv-4) HELM_Cryptsinge Halo" }, { "Initial Spectacles", "(lv-4) HELM_Initiate Spectacles" }, { "Demicrypt Halo", "(lv-6) HELM_Demicrypt Halo" }, { "Desne Helm", "(lv-6) HELM_Dense Helm" }, { "Diva Crown", "(lv-6) HELM_Diva Crown" }, { "Iron Halo", "(lv-6) HELM_Iron Halo" }, { "Necromancer Hood", "(lv-8) HELM_Necromancer Hood" }, { "Geistlord Crown", "(lv-10) HELM_Geistlord Crown" }, { "Journeyman Spectacles", "(lv-10) HELM_Journeyman Spectacles" }, { "Amberite Helm", "(lv-12) HELM_Amberite Helm" }, { "Focus Circlet", "(lv-12) HELM_Focus Circlet" }, { "Magistrate Circlet", "(lv-12) HELM_Magistrate Circlet" }, { "Rage Circlet", "(lv-12) HELM_Rage Circlet" }, { "Focusi Glasses", "(lv-14) HELM_Focusi Glasses" }, { "Nethercrypt Halo", "(lv-14) HELM_Nethercrypt Halo" }, { "Carbuncle Hat", "(lv-16) HELM_Carbuncle Hat" }, { "Geistlord Eye", "(lv-16) HELM_Geistlord Eye" }, { "Glyphgrift Halo", "(lv-16) HELM_Glyphgrift Halo" }, { "Jestercast Memory", "(lv-16) HELM_Jestercast Memory" }, { "Knightguard Halo", "(lv-16) HELM_Knightguard Halo" }, { "Mithril Halo", "(lv-16) HELM_Mithril Halo" }, { "Sapphite Mindhat", "(lv-18) HELM_Sapphite Mindhat" }, { "Dire Helm", "(lv-22) HELM_Dire Helm" }, { "Druidic Halo", "(lv-22) HELM_Druidic Halo" }, { "Guardel Helm", "(lv-22) HELM_Guardel Helm" }, { "Leathen Cap", "(lv-22) HELM_Leathen Cap" }, { "Boarus Helm", "(lv-24) HELM_Boarus Helm" }, { "Deathknight Helm", "(lv-24) HELM_Deathknight Helm" }, { "Emerock Halo", "(lv-24) HELM_Emerock Halo" }, { "Wizlad Hood", "(lv-24) HELM_Wizlad Hood" }, { "Boarus Torment", "(lv-26) HELM_Boarus Torment" }, { "Initiate Cloak", "(lv-2) CAPE_Initiate Cloak" }, { "Slimewoven Cloak", "(lv-4) CAPE_Slimewoven Cloak" }, { "Nokket Cloak", "(lv-6) CAPE_Nokket Cloak" }, { "Rugged Cloak", "(lv-6) CAPE_Rugged Cloak" }, { "Regazuul Cape", "(lv-10) CAPE_Regazuul Cape" }, { "Flux Cloak", "(lv-12) CAPE_Flux Cloak" }, { "Cozy Cloak", "(lv-14) CAPE_Cozy Cloak" }, { "Nethercrypt Cloak", "(lv-14) CAPE_Nethercrypt Cloak" }, { "Cobblerage Cloak", "(lv-16) CAPE_Cobblerage Cloak" }, { "Deathward Cape", "(lv-16) CAPE_Deathward Cape" }, { "Forlorn Cloak", "(lv-16) CAPE_Forlorn Cloak" }, { "Meshlink Cape", "(lv-16) CAPE_Meshlink Cape" }, { "Sagecaller Cape", "(lv-16) CAPE_Sagecaller Cape" }, { "Roudon Cape", "(lv-18) CAPE_Roudon Cape" }, { "Blueversa Cape", "(lv-20) CAPE_Blueversa Cape" }, { "Greenversa Cape", "(lv-20) CAPE_Greenversa Cape" }, { "Nulversa Cape", "(lv-20) CAPE_Nulversa Cape" }, { "Redversa Cape", "(lv-20) CAPE_Redversa Cape" }, { "Windgolem Cloak", "(lv-22) CAPE_Windgolem Cloak" }, { "Mekwar Drape", "(lv-24) CAPE_Mekwar Drape" }, { "Aero Top", "(lv-1) CHESTPIECE_Aero Top" }, { "Bunhost Garb", "(lv-1) CHESTPIECE_Bunhost Garb" }, { "Festive Coat", "(lv-1) CHESTPIECE_Festive Coat" }, { "Fisher Overalls", "(lv-1) CHESTPIECE_Fisher Overalls" }, { "Leather Top", "(lv-1) CHESTPIECE_Leather Top" }, { "Necro Marrow", "(lv-1) CHESTPIECE_Necro Marrow" }, { "Noble Shirt", "(lv-1) CHESTPIECE_Noble Shirt" }, { "Nutso Top", "(lv-1) CHESTPIECE_Nutso Top" }, { "Orefinder Vest", "(lv-1) CHESTPIECE_Orefinder Vest" }, { "Ritualist Garb", "(lv-1) CHESTPIECE_Ritualist Garb" }, { "Sagecloth Top", "(lv-1) CHESTPIECE_Sagecloth Top" }, { "Silken Top", "(lv-1) CHESTPIECE_Silken Top" }, { "Spooky Garment", "(lv-1) CHESTPIECE_Spooky Garment" }, { "Vampiric Coat", "(lv-1) CHESTPIECE_Vampiric Coat" }, { "Ghostly Tabard", "(lv-2) CHESTPIECE_Ghostly Tabard" }, { "Poacher Cloth", "(lv-2) CHESTPIECE_Poacher Cloth" }, { "Ragged Shirt", "(lv-2) CHESTPIECE_Ragged Shirt" }, { "Slimecrust Chest", "(lv-2) CHESTPIECE_Slimecrust Chest" }, { "Worn Robe", "(lv-2) CHESTPIECE_Worn Robe" }, { "Cryptsinge Chest", "(lv-4) CHESTPIECE_Cryptsinge Chest" }, { "Journeyman Vest", "(lv-4) CHESTPIECE_Journeyman Vest" }, { "Slimek Chest", "(lv-4) CHESTPIECE_Slimek Chest" }, { "Dense Chestpiece", "(lv-6) CHESTPIECE_Dense Chestpiece" }, { "Trodd Tunic", "(lv-6) CHESTPIECE_Trodd Tunic" }, { "Iron Chestplate", "(lv-7) CHESTPIECE_Iron Chestpiece" }, { "Tattered Battlerobe", "(lv-8) CHESTPIECE_Tattered Battlerobe" }, { "Apprentice Robe", "(lv-10) CHESTPIECE_Apprentice Robe" }, { "Duelist Garb", "(lv-10) CHESTPIECE_Duelist Garb" }, { "Skywrill Tabard", "(lv-10) CHESTPIECE_Skywrill Tabard" }, { "Sleeper's Robe", "(lv-10) CHESTPIECE_Sleeper's Robe" }, { "Warrior Chest", "(lv-10) CHESTPIECE_Warrior Chest" }, { "Amberite Breastplate", "(lv-12) CHESTPIECE_Amberite Breastplate" }, { "Golem Chestpiece", "(lv-12) CHESTPIECE_Golem Chestpiece" }, { "Lord Breastplate", "(lv-12) CHESTPIECE_Lord Breastplate" }, { "Nethercrypt Tabard", "(lv-12) CHESTPIECE_Nethercrypt Tabard" }, { "Reapsow Garb", "(lv-12) CHESTPIECE_Reapsow Garb" }, { "Witchlock Robe", "(lv-12) CHESTPIECE_Witchlock Robe" }, { "Chainmail Guard", "(lv-14) CHESTPIECE_Chainmail Guard" }, { "Ornamented Battlerobe", "(lv-14) CHESTPIECE_Ornamented Battlerobe" }, { "Carbuncle Robe", "(lv-16) CHESTPIECE_Carbuncle Robe" }, { "Chainscale Chest", "(lv-16) CHESTPIECE_Chainscale Chest" }, { "Gemveil Raiment", "(lv-16) CHESTPIECE_Gemveil Raiment" }, { "King Breastplate", "(lv-16) CHESTPIECE_King Breastplate" }, { "Mercenary Vestment", "(lv-16) CHESTPIECE_Mercenary Vestment" }, { "Mithril Chestpiece", "(lv-16) CHESTPIECE_Mithril Chestpiece" }, { "Reaper Gi", "(lv-16) CHESTPIECE_Reaper Gi" }, { "Witchwizard Robe", "(lv-16) CHESTPIECE_Witchwizard Robe" }, { "Berserker Chestpiece", "(lv-18) CHESTPIECE_Berserker Chestpiece" }, { "Fuguefall Duster", "(lv-18) CHESTPIECE_Fuguefall Duster" }, { "Magilord Overalls", "(lv-18) CHESTPIECE_Magilord Overalls" }, { "Monolith Chestpiece", "(lv-18) CHESTPIECE_Monolith Chestpiece" }, { "Sapphite Guard", "(lv-18) CHESTPIECE_Sapphite Guard" }, { "Druidic Robe", "(lv-20) CHESTPIECE_Druidic Robe" }, { "Emerock Chestpiece", "(lv-20) CHESTPIECE_Emerock Chestpiece" }, { "Fortified Chestpiece", "(lv-20) CHESTPIECE_Fortified Vestment" }, { "Roudon Chestpiece", "(lv-20) CHESTPIECE_Roudon Chestpiece" }, { "Earthbind Tabard", "(lv-22) CHESTPIECE_Earthbind Tabard" }, { "Gemveil Breastplate", "(lv-22) CHESTPIECE_Gemveil Breastplate" }, { "Roudon Robe", "(lv-22) CHESTPIECE_Roudon Robe" }, { "Ruggrok Vest", "(lv-22) CHESTPIECE_Ruggrok Vest" }, { "Excecutioner Vestment", "(lv-24) CHESTPIECE_Executioner Vestment" }, { "Fender Garb", "(lv-24) CHESTPIECE_Fender Garb" }, { "Wizlad Robe", "(lv-24) CHESTPIECE_Wizlad Robe" }, { "Aero Pants", "(lv-1) LEGGINGS_Aero Pants" }, { "Bunhost Leggings", "(lv-1) LEGGINGS_Bunhost Leggings" }, { "Festive Trouser", "(lv-1) LEGGINGS_Festive Trousers" }, { "Leather Britches", "(lv-1) LEGGINGS_Leather Britches" }, { "Necro Caustics", "(lv-1) LEGGINGS_Necro Caustics" }, { "Noble Pants", "(lv-1) LEGGINGS_Noble Pants" }, { "Nutso Pants", "(lv-1) LEGGINGS_Nutso Pants" }, { "Orefinder", "(lv-1) LEGGINGS_Orefinder Trousers" }, { "Ritualist Straps", "(lv-1) LEGGINGS_Ritualist Straps" }, { "Sagecloth Shorts", "(lv-1) LEGGINGS_Sagecloth Shorts" }, { "Silken Loincloth", "(lv-1) LEGGINGS_Silken Loincloth" }, { "Vampiric Leggings", "(lv-1) LEGGINGS_Vampiric Leggings" }, { "Ghostly Legwraps", "(lv-2) LEGGINGS_Ghostly Legwraps" }, { "Journeyman Shorts", "(lv-2) LEGGINGS_Journeyman Shorts" }, { "Slimecrust Leggings", "(lv-2) LEGGINGS_Slimecrust Leggings" }, { "Journeyman Leggings", "(lv-4) LEGGINGS_Journeyman Leggings" }, { "Slimek Leggings", "(lv-4) LEGGINGS_Slimek Leggings" }, { "Dense Leggings", "(lv-6) LEGGINGS_Dense Leggings" }, { "Sash Leggings", "(lv-8) LEGGINGS_Sash Leggings" }, { "Warrior Leggings", "(lv-10) LEGGINGS_Warrior Leggings" }, { "Amberite Leggings", "(lv-12) LEGGINGS_Amberite Leggings" }, { "Chainmail Leggings", "(lv-12) LEGGINGS_Chainmail Leggings" }, { "Darkcloth Pants", "(lv-12) LEGGINGS_Darkcloth Pants" }, { "Lord Greaves", "(lv-12) LEGGINGS_Lord Greaves" }, { "Reapsow Pants", "(lv-12) LEGGINGS_Reapsow Pants" }, { "Witchlock Loincloth", "(lv-12) LEGGINGS_Witchlock Loincloth" }, { "King Greaves", "(lv-16) LEGGINGS_King Greaves" }, { "Mercenary Leggings", "(lv-16) LEGGINGS_Mercenary Leggings" }, { "Reaper Leggings", "(lv-16) LEGGINGS_Reaper Leggings" }, { "Stridebond Pants", "(lv-16) LEGGINGS_Stridebond Pants" }, { "Witchwizard Garterbelt", "(lv-16) LEGGINGS_Witchwizard Garterbelt" }, { "Berserker Leggings", "(lv-18) LEGGINGS_Berserker Leggings" }, { "Fuguefall Pants", "(lv-18) LEGGINGS_Fuguefall Pants" }, { "Magilord Boots", "(lv-18) LEGGINGS_Magilord Boots" }, { "Sapphite Leggings", "(lv-18) LEGGINGS_Sapphite Leggings" }, { "Jadewall Trousers", "(lv-20) LEGGINGS_Jadewail Trousers" }, { "Temrak Britches", "(lv-20) LEGGINGS_Temrak Britches" }, { "Eschek Greaves", "(lv-22) LEGGINGS_Eschek Greaves" }, { "Gemveil Leggings", "(lv-22) LEGGINGS_Gemveil Leggings" }, { "Executioner Leggings", "(lv-24) LEGGINGS_Executioner Leggings" }, { "Fender Leggings", "(lv-24) LEGGINGS_Fender Leggings" }, { "Crypt Buckler", "(lv-4) SHIELD_Crypt Buckler" }, { "Slimek Shield", "(lv-4) SHIELD_Slimek Shield" }, { "Demicrypt Buckler", "(lv-6) SHIELD_Demicrypt Buckler" }, { "Dense Shield", "(lv-6) SHIELD_Dense Shield" }, { "Iron Shield", "(lv-6) SHIELD_Iron Shield" }, { "Iris Shield", "(lv-8) SHIELD_Iris Shield" }, { "Omen Shield", "(lv-8) SHIELD_Omen Shield" }, { "Amberite Shield", "(lv-12) SHIELD_Amberite Shield" }, { "Slabton Shield", "(lv-12) SHIELD_Slabton Shield" }, { "Mithril Shield", "(lv-14) SHIELD_Mithril Shield" }, { "Nethercrypt Shield", "(lv-14) SHIELD_Nethercrypt Shield" }, { "Rustweary Shield", "(lv-16) SHIELD_Rustweary Shield" }, { "Rustwise Shield", "(lv-16) SHIELD_Rustwise Shield" }, { "Sapphite Shield", "(lv-18) SHIELD_Sapphite Shield" }, { "Rigor Buckler", "(lv-20) SHIELD_Rigor Buckler" }, { "Daemon Shield", "(lv-22) SHIELD_Daemon Shield" }, { "Irisun Shield", "(lv-22) SHIELD_Irisun Shield" }, { "Old Ring", "(lv-1) RING_Old Ring" }, { "Ring of Ambition", "(lv-1) RING_Ring Of Ambition" }, { "Nograd's Amulet", "(lv-2) RING_Nograd's Amulet" }, { "The One Ring", "(lv-2) RING_The One Ring" }, { "Ambersquire Ring", "(lv-6) RING_Ambersquire Ring" }, { "Emeraldfocus Ring", "(lv-6) RING_Emeraldfocus Ring" }, { "Sapphireweave Ring", "(lv-6) RING_Sapphireweave Ring" }, { "Edon's Pendant", "(lv-8) RING_Edon's Pendant" }, { "Geistlord Ring", "(lv-12) RING_Geistlord Ring" }, { "Students Ring", "(lv-12) RING_Students Ring" }, { "Pearlpond Ring", "(lv-14) RING_Pearlpond Ring" }, { "Slitherwraith Ring", "(lv-14) RING_Slitherwraith Ring" }, { "Geistlord Band", "(lv-16) RING_Geistlord Band" }, { "Jadetrout Ring", "(lv-16) RING_Jadetrout Ring" }, { "Orbos Ring", "(lv-16) RING_Orbos Ring" }, { "Valor Ring", "(lv-16) RING_Valor Ring" }, { "Earthwoken Ring", "(lv-18) RING_Earthwoken Ring" }, { "Noji Talisman", "(lv-20) RING_Noji Talisman" }, { "Valdur Effigy", "(lv-24) RING_Valdur Effigy" }, { "Glyphik Booklet", "(lv-26) RING_Glyphik Booklet" }, { "Tessellated Drive", "(lv-26) RING_Tessellated Drive" }, { "Aqua Muchroom Cap", "TRADEITEM_Aqua Muchroom Cap" }, { "Barknaught Face", "TRADEITEM_Barknaught Face" }, { "Blightwood Log", "TRADEITEM_Blightwood Log" }, { "Blightwood Stick", "TRADEITEM_Blightwood Stick" }, { "Blue Minchroom Cap", "TRADEITEM_Blue Minchroom Cap" }, { "Boomboar Gear", "TRADEITEM_Boomboar Gear" }, { "Boomboar Head", "TRADEITEM_Boomboar Head" }, { "Boomboar Pouch", "TRADEITEM_Boomboar Pouch" }, { "Burnrose", "TRADEITEM_Burnrose" }, { "Carbuncle Foot", "TRADEITEM_Carbuncle Foot" }, { "Cursed Note", "TRADEITEM_Cursed Note" }, { "Deadwood Log", "TRADEITEM_Deadwood Log" }, { "Deathgel Core", "TRADEITEM_Deathgel Core" }, { "Deathknight Gauntlet", "TRADEITEM_Deathknight Gauntlet" }, { "Demigolem Core", "TRADEITEM_Demigolem Core" }, { "Demigolem Gem", "TRADEITEM_Demigolem Gem" }, { "Diva Necklace", "TRADEITEM_Diva Necklace" }, { "Firebreath Gland", "TRADEITEM_Firebreath Gland" }, { "Fluxfern", "TRADEITEM_Fluxfern" }, { "Gale Muchroom Cap", "TRADEITEM_Gale Muchroom Cap" }, { "Geist Collar", "TRADEITEM_Geist Collar" }, { "Ghostdust", "TRADEITEM_Ghostdust" }, { "Golem Core", "TRADEITEM_Golem Core" }, { "Golem Gem", "TRADEITEM_Golem Gem" }, { "Green Lipstick", "TRADEITEM_Green Lipstick" }, { "Hellsludge Core", "TRADEITEM_Hellsludge Core" }, { "Maw Eye", "TRADEITEM_Maw Eye" }, { "Mekboar Head", "TRADEITEM_Mekboar Head" }, { "Mekboar Spear", "TRADEITEM_Mekboar Spear" }, { "Monolith Core", "TRADEITEM_Monolith Core" }, { "Monolith Gem", "TRADEITEM_Monolith Gem" }, { "Mouth Bittertooth", "TRADEITEM_Mouth Bittertooth" }, { "Mouth Eye", "TRADEITEM_Mouth Eye" }, { "Rageboar Head", "TRADEITEM_Rageboar Head" }, { "Rageboar Spear", "TRADEITEM_Rageboar Spear" }, { "Red Minchroom Cap", "TRADEITEM_Red Minchroom Cap" }, { "Rock", "TRADEITEM_Rock" }, { "Slime Core", "TRADEITEM_Slime Core" }, { "Slime Diva Ears", "TRADEITEM_Slime Diva Ears" }, { "Slime Ears", "TRADEITEM_Slime Ears" }, { "Slimek Core", "TRADEITEM_Slimek Core" }, { "Slimek Ears", "TRADEITEM_Slimek Ears" }, { "Slimek Eye", "TRADEITEM_Slimek Eye" }, { "Vinethorn", "TRADEITEM_Vinethorn" }, { "Vout Antennae", "TRADEITEM_Vout Antennae" }, { "Vout Wing", "TRADEITEM_Vout Wing" }, { "Warboar Axe", "TRADEITEM_Warboar Axe" }, { "Warboar Head", "TRADEITEM_Warboar Head" }, { "Wizboar Head", "TRADEITEM_Wizboar Head" }, { "Wizboar Scepter", "TRADEITEM_Wizboar Scepter" }, { "Amberite Ore", "TRADEITEM_Amberite Ore" }, { "Dense Ore", "TRADEITEM_Dense Ore" }, { "Sapphite Ore", "TRADEITEM_Sapphite Ore" }, { "Coal", "TRADEITEM_Coal" }, { "Big Wan", "TRADEITEM_Big Wan" }, { "Bittering Katfish", "TRADEITEM_Bittering Katfish" }, { "Bonefish", "TRADEITEM_Bonefish" }, { "Smiling Wrellfish", "TRADEITEM_Smiling Wrellfish" }, { "Squangfish", "TRADEITEM_Squangfish" }, { "Sugeel", "TRADEITEM_Sugeel" }, { "Sugshrimp", "TRADEITEM_Sugshrimp" }, { "Windtail Fish", "TRADEITEM_Windtail Fish" }, { "Old Boot", "TRADEITEM_Old Boot" }, { "Agility Stone", "TRADEITEM_Agility Stone" }, { "Angela's Tear", "TRADEITEM_Angela's Tear" }, { "Epic Carrot", "TRADEITEM_Epic Carrot" }, { "Flux Stone", "TRADEITEM_Flux Stone" }, { "Illusion Stone", "TRADEITEM_Illusion Stone" }, { "Might Stone", "TRADEITEM_Might Stone" }, { "Starlight Gem", "TRADEITEM_Starlight Gem" }, { "Black Dye", "TRADEITEM_Black Dye" }, { "Blue Dye", "TRADEITEM_Blue Dye" }, { "Brown Dye", "TRADEITEM_Brown Dye" }, { "Cyan Dye", "TRADEITEM_Cyan Dye" }, { "Green Dye", "TRADEITEM_Green Dye" }, { "Grey Dye", "TRADEITEM_Grey Dye" }, { "Lime Dye", "TRADEITEM_Lime Dye" }, { "Orange Dye", "TRADEITEM_Orange Dye" }, { "Pink Dye", "TRADEITEM_Pink Dye" }, { "Purple Dye", "TRADEITEM_Purple Dye" }, { "Red Dye", "TRADEITEM_Red Dye" }, { "White Dye", "TRADEITEM_White Dye" }, { "Yellow Dye", "TRADEITEM_Yellow Dye" }, { "Crowns (Small)", "CURRENCY_250" }, { "Crowns (Medium)", "CURRENCY_500" }, { "Crowns (Large)", "CURRENCY_1000" }, { "Crowns (Huge)", "CURRENCY_2500" }, { "Tome of Naivety", "(lv-0) STATUSCONSUMABLE_Tome of Naivety" }, { "Tome of Unlearning", "(lv-0) STATUSCONSUMABLE_Tome of Unlearning" }, { "Test Chestpiece", "(lv-1) CHESTPIECE_Test Chestpiece" }, { "Test Pants", "(lv-1) LEGGINGS_Test Pants" }, { "Test Ring", "(lv-1) RING_Test Ring" } }; public static readonly Dictionary<string, List<List<string>>> ProgressiveItemTiers = new Dictionary<string, List<List<string>>> { { "Progressive Any Weapon", new List<List<string>> { new List<string> { "Crypt Buckler", "Slimek Shield" }, new List<string> { "Demicrypt Buckler", "Dense Shield", "Iron Shield" }, new List<string> { "Iris Shield", "Omen Shield", "Rustweary Shield", "Rustwise Shield" }, new List<string> { "Amberite Shield", "Slabton Shield", "Mithril Shield", "Nethercrypt Shield" }, new List<string> { "Rigor Buckler", "Sapphite Shield", "Daemon Shield", "Irisun Shield" } } }, { "Progressive Any Helmet", new List<List<string>> { new List<string> { "Agility Ears", "Leather Cap", "Newfold Halo" }, new List<string> { "Acolyte Hood", "Cryptsinge Halo", "Initial Spectacles", "Demicrypt Halo", "Desne Helm", "Diva Crown" }, new List<string> { "Iron Halo", "Necromancer Hood", "Journeyman Spectacles" }, new List<string> { "Geistlord Crown", "Geistlord Eye", "Amberite Helm", "Focus Circlet", "Magistrate Circlet", "Rage Circlet", "Focusi Glasses", "Nethercrypt Halo" }, new List<string> { "Carbuncle Hat", "Glyphgrift Halo", "Jestercast Memory", "Knightguard Halo", "Mithril Halo", "Wizlad Hood", "Dire Helm", "Druidic Halo", "Guardel Helm", "Leathen Cap" }, new List<string> { "Sapphite Mindhat", "Boarus Helm", "Deathknight Helm", "Emerock Halo", "Boarus Torment" } } }, { "Progressive Any Cape", new List<List<string>> { new List<string> { "Initiate Cloak", "Slimewoven Cloak", "Nokket Cloak", "Rugged Cloak" }, new List<string> { "Cobblerage Cloak", "Forlorn Cloak", "Flux Cloak", "Meshlink Cape", "Cozy Cloak", "Nethercrypt Cloak" }, new List<string> { "Regazuul Cape", "Deathward Cape", "Sagecaller Cape" }, new List<string> { "Windgolem Cloak", "Roudon Cape", "Blueversa Cape", "Greenversa Cape", "Nulversa Cape", "Redversa Cape" }, new List<string> { "Mekwar Drape" } } }, { "Progressive Any Chest Piece", new List<List<string>> { new List<string> { "Aero Top", "Leather Top", "Necro Marrow", "Nutso Top", "Sagecloth Top", "Ghostly Tabard", "Poacher Cloth", "Ragged Shirt", "Slimecrust Chest", "Worn Robe" }, new List<string> { "Cryptsinge Chest", "Journeyman Vest", "Slimek Chest", "Apprentice Robe", "Dense Chestpiece", "Trodd Tunic", "Tattered Battlerobe" }, new List<string> { "Iron Chestplate", "Duelist Garb", "Amberite Breastplate", "Nethercrypt Tabard", "Chainmail Guard", "Skywrill Tabard", "Sleeper's Robe", "Warrior Chest", "Golem Chestpiece", "Carbuncle Robe" }, new List<string> { "Ornamented Battlerobe", "Chainscale Chest", "Mercenary Vestment", "Mithril Chestpiece", "Druidic Robe" }, new List<string> { "Gemveil Raiment", "Monolith Chestpiece", "Sapphite Guard", "Fortified Chestpiece", "Roudon Chestpiece", "Roudon Robe", "Earthbind Tabard", "Gemveil Breastplate", "Ruggrok Vest", "Emerock Chestpiece" } } }, { "Progressive Any Leggings", new List<List<string>> { new List<string> { "Aero Pants", "Leather Britches", "Necro Caustics", "Nutso Pants", "Sagecloth Shorts", "Ghostly Legwraps", "Slimecrust Leggings" }, new List<string> { "Journeyman Shorts", "Journeyman Leggings", "Slimek Leggings", "Dense Leggings", "Sash Leggings" }, new List<string> { "Amberite Leggings", "Chainmail Leggings", "Darkcloth Pants", "Warrior Leggings" }, new List<string> { "Mercenary Leggings", "Stridebond Pants" }, new List<string> { "Sapphite Leggings", "Jadewall Trousers", "Temrak Britches", "Eschek Greaves", "Gemveil Leggings" } } }, { "Progressive Any Trinket", new List<List<string>> { new List<string> { "Old Ring", "Ring of Ambition", "Test Ring" }, new List<string> { "Nograd's Amulet", "Students Ring" }, new List<string> { "The One Ring", "Geistlord Ring", "Geistlord Band" }, new List<string> { "Ambersquire Ring", "Emeraldfocus Ring", "Pearlpond Ring", "Slitherwraith Ring", "Jadetrout Ring" }, new List<string> { "Sapphireweave Ring", "Edon's Pendant", "Orbos Ring", "Valor Ring" }, new List<string> { "Earthwoken Ring", "Noji Talisman", "Valdur Effigy", "Glyphik Booklet", "Tessellated Drive" } } }, { "Progressive Fighter Weapon", new List<List<string>> { new List<string> { "Crypt Blade", "Femur Club", "Ironbark Sword", "Slimecrust Blade", "Gilded Sword", "Splitbark Club", "Slimek Axehammer", "Mini Geist Scythe", "Geist Scythe" }, new List<string> { "Demicrypt Blade", "Dense Mace", "Iron Sword", "Dense Hammer", "Iron Axehammer", "Crypt Pounder", "Stone Greatblade", "Dense Spear", "Iron Spear", "Cryptsinge Halberd", "Dawn Mace", "Rude Blade" }, new List<string> { "Vile Blade", "Poltergeist Scythe", "Mekspear", "Amberite Sword", "Nethercrypt Blade", "Amberite Warstar", "Dolkin's Axe", "Amberite Halberd", "Necroroyal Halberd", "Sinner Bardiche" }, new List<string> { "Coldgeist Blade", "Mithril Sword", "Serrated Blade", "Mithril Halberd", "Ragespear", "Serrated Spear" }, new List<string> { "Nulrok Mace", "Coldgeist Punisher", "Nulrok Spear", "Firebreath Blade", "Quake Pummeler", "Deadwood Axe", "Mithril Greatsword", "Sapphite Spear", "Valdur Blade", "Deathknight Runeblade", "Cyrotribe Spear", "Flametribe Spear" }, new List<string> { "Fier Blade", "Ryzer Greataxe" } } }, { "Progressive Fighter Chest Piece", new List<List<string>> { new List<string> { "Lord Breastplate" }, new List<string> { "King Breastplate", "Berserker Chestpiece" }, new List<string> { "Excecutioner Vestment" } } }, { "Progressive Fighter Leggings", new List<List<string>> { new List<string> { "Lord Greaves" }, new List<string> { "King Greaves", "Berserker Leggings" }, new List<string> { "Executioner Leggings" } } }, { "Progressive Mystic Weapon", new List<List<string>> { new List<string> { "Marrow Bauble", "Splitbark Scepter", "Demicrypt Bauble", "Cryptcall Bell" }, new List<string> { "Iron Scepter", "Cryo Cane", "Slime Diva Baton", "Iron Bell" }, new List<string> { "Pyre Cane", "Wizwand", "Nethercrypt Bauble", "Aquapetal Staff", "Flamepetal Staff", "Coldgeist Frostcaller" }, new List<string> { "Mithril Scepter", "Mithril Bell" }, new List<string> { "Sapphite Scepter", "Colossus Tone", "Voalstark Wand", "Sapphite Bell" } } }, { "Progressive Mystic Helmet", new List<List<string>> { new List<string> { "Wizlad Hood" } } }, { "Progressive Mystic Chest Piece", new List<List<string>> { new List<string> { "Witchlock Robe" }, new List<string> { "Witchwizard Robe", "Magilord Overalls" }, new List<string> { "Wizlad Robe" } } }, { "Progressive Mystic Leggings", new List<List<string>> { new List<string> { "Witchlock Loincloth" }, new List<string> { "Witchwizard Garterbelt", "Magilord Boots" } } }, { "Progressive Bandit Weapon", new List<List<string>> { new List<string> { "Sliemcrust Katars", "Cryptsinge Katars", "Slimek Shivs", "Crypt Bow" }, new List<string> { "Deathgel Shivs", "Dense Katars", "Iron Katars", "Runic Katars", "Demicrypt Bow", "Iron Bow", "Mekspike Bow", "Menace Bow" }, new List<string> { "Geistlord Claws", "Hellsludge Shivs", "Petrified Bow", "Necroroyal Bow" }, new List<string> { "Mithril Katars", "Frostbite Claws", "Serrated Knuckles", "Mithril Bow", "Colgeist Bow", "Serrated Longbow", "Amberite Boomstick" }, new List<string> { "Rummok Bladerings", "Sapphite Katars", "Golemfist Katars", "Torrentius Longbow", "Magitek Burstgun" }, new List<string> { "Follycannon" } } }, { "Progressive Bandit Chest Piece", new List<List<string>> { new List<string> { "Reapsow Garb" }, new List<string> { "Reaper Gi", "Fuguefall Duster" }, new List<string> { "Fender Garb" } } }, { "Progressive Bandit Leggings", new List<List<string>> { new List<string> { "Reapsow Pants" }, new List<string> { "Reaper Leggings", "Fuguefall Pants" }, new List<string> { "Fender Leggings" } } } }; private readonly HashSet<string> _completedAchievements = new HashSet<string>(); private string _lastKnownClass = ""; private int _lastKnownClassTier = 0; private bool _subclassWarningLogged = false; private bool _newJourneySent = false; private bool _achievementScanDone = false; private static readonly Dictionary<string, string[]> BOSS_TO_ACHIEVEMENTS = new Dictionary<string, string[]> { { "Lord Zuulneruda", new string[2] { "Clearing Catacombs (1-6)", "Clearing Catacombs (6-12)" } }, { "Lord Kaluuz", new string[1] { "Clearing Catacombs (12-18)" } }, { "Colossus", new string[1] { "Clearing Grove (15-20)" } }, { "Valdur", new string[1] { "Clearing Grove (20-25)" } } }; private static readonly Dictionary<string, string[]> BOSS_TO_QUESTS = new Dictionary<string, string[]> { { "Lord Zuulneruda", new string[2] { "Communing Catacombs", "The Voice of Zuulneruda" } }, { "Lord Kaluuz", new string[1] { "Consumed Madness" } }, { "Colossus", new string[1] { "The Colossus" } }, { "Valdur", new string[2] { "Hell In The Grove", "Spiraling In The Grove" } }, { "Slime Diva", new string[1] { "Diva Must Die" } } }; private static readonly Dictionary<string, int[]> BOSS_TO_GOALS = new Dictionary<string, int[]> { { "Slime Diva", new int[2] { 0, 6 } }, { "Lord Zuulneruda", new int[2] { 1, 6 } }, { "Colossus", new int[2] { 2, 6 } }, { "Lord Kaluuz", new int[2] { 4, 6 } }, { "Valdur", new int[1] { 5 } } }; private readonly HashSet<string> _killedBossNames = new HashSet<string>(); private MapInstance _lastTrackedMap = null; private CreepSpawner _trackedBossSpawner = null; private Creep _trackedBoss = null; private string _trackedBossName = null; private bool _bossDeathFired = false; private FileSystemWatcher _profileWatcher = null; private bool _pendingAlteredVision = false; private DateTime _watcherStartedAt = DateTime.MinValue; private string _baselineAppearanceJson = null; private string _activeProfilePath = null; private static readonly HashSet<string> NoviceSkills = new HashSet<string> { "Adrenaline", "Alacrity", "Aquos", "Crya", "Curis", "Dexterity Mastery", "Fira", "Focus Shot", "Gust", "Inner Focus", "Leg Up", "Life Tap", "Mind Mastery", "Multi Bash", "Recall", "Recovery", "Rock Toss", "Siphon Leech", "Spire", "Spread Shot", "Strength Mastery", "Sturdy", "Talus" }; private static Harmony _harmony; private static GameObject scriptHolder; private static PortalUnlocks portalLocker; private static readonly FieldInfo maxOnscreenMessages = AccessTools.Field(typeof(ChatBehaviour), "_maxGameLogicLines"); public ConfigEntry<string> cfgServer; public ConfigEntry<string> cfgSlot; public ConfigEntry<string> cfgPassword; public ConfigEntry<bool> cfgDeathlink; private ConfigEntry<bool> cfgAutoConnect; public InputField apServer; public InputField apSlot; public InputField apPassword; public Toggle apDeathlink; public ArchipelagoSession _session; public bool connected; private bool connecting; private Dictionary<string, object> slotData = new Dictionary<string, object>(); public DeathLinkService _dlService; public static int reactingToDeathLink; private int goalOption = 0; public bool randomPortalsEnabled = false; public int mainClassOption = 0; public int secondaryClassOption = 0; private bool shopSanityEnabled = false; private int progressivePortalCount = 0; private Dictionary<string, int> _progressiveCounts = new Dictionary<string, int>(); public readonly ConcurrentQueue<Action> _mainThreadQueue = new ConcurrentQueue<Action>(); private Dictionary<string, bool> _portalItemsReceived = new Dictionary<string, bool> { { "Sanctum Portal", false }, { "Outer Sanctum Portal", false }, { "Arcwood Pass Portal", false }, { "Effold Terrace Portal", false }, { "Tuul Valley Portal", false }, { "Catacombs Portal", false }, { "Cresent Road Portal", false }, { "Tuul Enclave Portal", false }, { "Luvora Garden Portal", false }, { "Cresent Keep Portal", false }, { "Bularr Fortress Portal", false }, { "Cresent Grove lvl 1 Portal", false }, { "Cresent Grove lvl 2 Portal", false }, { "Gate of the Moon Portal", false }, { "Wall of the Stars Portal", false }, { "Redwoud Portal", false }, { "Trial of the Stars Portal", false } }; private Dictionary<string, string> _portalScenes = new Dictionary<string, string> { { "Sanctum Portal", "Assets/Scenes/00_zone_forest/_zone00_sanctum.unity" }, { "Outer Sanctum Portal", "Assets/Scenes/00_zone_forest/_zone00_outerSanctum.unity" }, { "Arcwood Pass Portal", "Assets/Scenes/00_zone_forest/_zone00_arcwoodPass.unity" }, { "Effold Terrace Portal", "Assets/Scenes/00_zone_forest/_zone00_effoldTerrace.unity" }, { "Tuul Valley Portal", "Assets/Scenes/00_zone_forest/_zone00_tuulValley.unity" }, { "Catacombs Portal", "Assets/Scenes/map_dungeon00_sanctumCatacombs.unity" }, { "Cresent Road Portal", "Assets/Scenes/00_zone_forest/_zone00_crescentRoad.unity" }, { "Tuul Enclave Portal", "Assets/Scenes/00_zone_forest/_zone00_tuulEnclave.unity" }, { "Luvora Garden Portal", "Assets/Scenes/00_zone_forest/_zone00_luvoraGarden.unity" }, { "Cresent Keep Portal", "Assets/Scenes/00_zone_forest/_zone00_crescentKeep.unity" }, { "Bularr Fortress Portal", "Assets/Scenes/00_zone_forest/_zone00_bularFortress.unity" }, { "Cresent Grove lvl 1 Portal", "Assets/Scenes/map_dungeon01_crescentGrove.unity" }, { "Cresent Grove lvl 2 Portal", "Assets/Scenes/map_dungeon01_crescentGrove.unity" }, { "Gate of the Moon Portal", "Assets/Scenes/00_zone_forest/_zone00_gateOfTheMoon.unity" }, { "Wall of the Stars Portal", "Assets/Scenes/00_zone_forest/_zone00_wallOfTheStars.unity" }, { "Redwoud Portal", "Assets/Scenes/map_zone00_redwoud.unity" }, { "Trial of the Stars Portal", "Assets/Scenes/00_zone_forest/_zone00_trialOfTheStars.unity" } }; private List<string> _progressivePortalOrder = new List<string> { "Outer Sanctum Portal", "Arcwood Pass Portal", "Catacombs Portal", "Effold Terrace Portal", "Tuul Valley Portal", "Cresent Road Portal", "Luvora Garden Portal", "Cresent Keep Portal", "Tuul Enclave Portal", "Cresent Grove lvl 1 Portal", "Bularr Fortress Portal" }; public ArchipelagoShopSanity _shopSanity; private readonly HashSet<long> _reportedChecks = new HashSet<long>(); private int _lastLevel = 0; private int _previousFishingLevel = 0; private int _previousMiningLevel = 0; private HashSet<string> _completedQuests = new HashSet<string>(); private bool _questDebugLogged = false; private string currentSessionId = ""; private const string SESSION_STATE_FILE = "ap_session_state.dat"; private int _totalItemsReceived = 0; private int _savedItemCount = 0; public static AtlyssArchipelagoPlugin Instance { get; private set; } public static ManualLogSource StaticLogger { get; private set; } public void SendAPChatMessage(string message) { try { Player mainPlayer = Player._mainPlayer; if (!((Object)(object)mainPlayer == (Object)null)) { ChatBehaviour chatBehaviour = mainPlayer._chatBehaviour; if (!((Object)(object)chatBehaviour == (Object)null)) { maxOnscreenMessages.SetValue(chatBehaviour, 50); chatBehaviour.Init_GameLogicMessage("<color=#00ff00>[Archipelago]</color> " + message); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to send chat message: " + ex.Message)); } } public void HandleArchipelagoCommand(string message) { if (!connected || _session == null) { SendAPChatMessage("<color=#FF4444>Not connected to Archipelago!</color>"); return; } string text = message.TrimStart(new char[1] { '/' }).Trim(); string[] array = text.Split(new char[1] { ' ' }, 2); string text2 = array[0].ToLower(); string text3 = ((array.Length > 1) ? array[1] : ""); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] Command received: " + text2 + " " + text3)); switch (text2) { case "release": HandleReleaseCommand(); return; case "collect": HandleCollectCommand(); return; case "hint": HandleHintCommand(text3); return; case "help": HandleHelpCommand(); return; case "players": HandlePlayersCommand(); return; case "status": HandleStatusCommand(); return; } try { _session.Socket.SendPacket(new SayPacket { Text = message }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] Sent command to server: " + message)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to send command: " + ex.Message)); SendAPChatMessage("<color=#FF4444>Unknown command: /" + text2 + "</color>"); } } private void HandleReleaseCommand() { try { _session.Say("!release"); SendAPChatMessage("<color=#FFFF00>Release requested!</color>"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[AtlyssAP] Release command executed"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to execute release: " + ex.Message)); SendAPChatMessage("<color=#FF4444>Failed to execute release</color>"); } } private void HandleCollectCommand() { try { _session.Say("!collect"); SendAPChatMessage("<color=#FFFF00>Collect requested!</color>"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[AtlyssAP] Collect command executed"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to execute collect: " + ex.Message)); SendAPChatMessage("<color=#FF4444>Failed to execute collect</color>"); } } private void HandleHintCommand(string args) { try { if (string.IsNullOrWhiteSpace(args)) { SendAPChatMessage("<color=#FFFF00>Usage: /hint [item name]</color>"); return; } SendAPChatMessage("<color=#FFFF00>Requesting hint for: " + args + "</color>"); _session.Say("!hint " + args); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] Hint requested for: " + args)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to request hint: " + ex.Message)); } } private void HandleHelpCommand() { SendAPChatMessage("<color=#FFFF00>Archipelago Commands:</color>"); SendAPChatMessage("/release - Release remaining items"); SendAPChatMessage("/collect - Collect items from others"); SendAPChatMessage("/hint [item] - Request hint"); SendAPChatMessage("/players - List connected players"); SendAPChatMessage("/status - Show completion status"); SendAPChatMessage("/help - Show this message"); } private void HandlePlayersCommand() { try { IEnumerable<PlayerInfo> allPlayers = _session.Players.AllPlayers; int num = 0; foreach (PlayerInfo item in allPlayers) { num++; } SendAPChatMessage($"<color=#FFFF00>Connected Players ({num}):</color>"); foreach (PlayerInfo item2 in allPlayers) { string text = item2.Name ?? $"Player {item2.Slot}"; string text2 = item2.Game ?? "Unknown"; SendAPChatMessage("- " + text + " (" + text2 + ")"); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] Listed {num} players"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to list players: " + ex.Message)); SendAPChatMessage("<color=#FF4444>Failed to get player list</color>"); } } private void HandleStatusCommand() { try { int count = _reportedChecks.Count; int count2 = AllLocationNameToId.Count; int count3 = AllQuestToLocation.Count; float num = ((count2 > 0) ? ((float)count / (float)count2 * 100f) : 0f); SendAPChatMessage($"<color=#FFFF00>Progress: {count}/{count2} ({num:F1}%)</color>"); SendAPChatMessage($"Level milestones: {_lastLevel}/32"); SendAPChatMessage($"Quest completions: {_completedQuests.Count}/{count3}"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] Status: {count}/{count2} locations checked"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to show status: " + ex.Message)); } } private void OnPacketReceived(ArchipelagoPacketBase packet) { if (packet is PrintJsonPacket && !(packet is ItemPrintJsonPacket) && !(packet is HintPrintJsonPacket)) { SendAPChatMessage(packet.ToJObject().SelectToken("data[0].text")?.Value<string>() ?? ""); } else if (packet is HintPrintJsonPacket) { if (packet.ToJObject()["receiving"].ToObject<int>() == _session.Players.ActivePlayer.Slot) { NetworkItem networkItem = packet.ToJObject()["item"].ToObject<NetworkItem>(); string alias = _session.Players.GetPlayerInfo(networkItem.Player).Alias; string itemName = _session.Items.GetItemName(networkItem.Item); string locationNameFromId = _session.Locations.GetLocationNameFromId(networkItem.Location, _session.Players.GetPlayerInfo(networkItem.Player).Game); string message = "Your <color=#FFFF00>" + itemName + "</color> is at <color=#FFFF00>" + locationNameFromId + "</color> in <color=#00FFFF>" + alias + "'s</color> world."; SendAPChatMessage(message); } else if (packet.ToJObject()["item"].ToObject<NetworkItem>().Player == _session.Players.ActivePlayer.Slot) { NetworkItem networkItem2 = packet.ToJObject()["item"].ToObject<NetworkItem>(); string alias2 = _session.Players.GetPlayerInfo(networkItem2.Player).Alias; string itemName2 = _session.Items.GetItemName(networkItem2.Item, _session.Players.GetPlayerInfo(networkItem2.Player).Game); string locationNameFromId2 = _session.Locations.GetLocationNameFromId(networkItem2.Location); string message = "<color=#00FFFF>" + alias2 + "'s</color> <color=#FFFF00>" + itemName2 + "</color> is at your <color=#FFFF00>" + locationNameFromId2 + "</color>"; SendAPChatMessage(message); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[AtlyssAP] Received a hint for a different player. Ignoring."); } } } private void OnDeathLinkReceived(DeathLink dl) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) reactingToDeathLink = 2; try { Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[AtlyssAP] Player not found for DeathLink! Possibly on Main Menu?"); reactingToDeathLink = 0; return; } mainPlayer._playerZoneType = (ZoneType)1; mainPlayer._statusEntity.Subtract_Health(10000); ((BaseUnityPlugin)this).Logger.LogMessage((object)"[AtlyssAP] DeathLink Received!"); string text = ((!string.IsNullOrWhiteSpace(dl.Cause)) ? (dl.Cause + ".") : ("Killed by " + dl.Source + " on Archipelago.")); try { GameObject.Find("_GameUI_InGame").GetComponent<ErrorPromptTextManager>().Init_ErrorPrompt(text); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to display DeathLink message: " + ex.Message)); } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to process DeathLink: " + ex2.Message)); reactingToDeathLink = 0; } } public void ToggleDeathLink(bool enabled) { if (_session != null && _dlService != null && _session.Socket.Connected) { if (enabled) { _dlService.EnableDeathLink(); } else { _dlService.DisableDeathLink(); } } } private void PollForLevelChanges() { try { Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { return; } PlayerStats component = ((Component)mainPlayer).GetComponent<PlayerStats>(); if ((Object)(object)component == (Object)null) { return; } int network_currentLevel = component.Network_currentLevel; if (network_currentLevel != _lastLevel) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] Level changed: {_lastLevel} -> {network_currentLevel}"); if (network_currentLevel >= 2 && network_currentLevel <= 32 && network_currentLevel % 2 == 0) { string text = $"Reach Level {network_currentLevel}"; SendCheckByName(text); SendAPChatMessage("Found <color=#FFFF00>" + text + "</color>! Sent item to another player!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] " + text + " milestone!")); } _lastLevel = network_currentLevel; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Error polling level: " + ex.Message)); } } private void PollForSkillLevelChanges() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) try { Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { return; } PlayerStats pStats = mainPlayer._pStats; if ((Object)(object)pStats == (Object)null) { return; } for (int i = 0; i < pStats._syncProfessions.Count; i++) { ProfessionStruct val = pStats._syncProfessions[i]; ScriptableProfession val2 = null; if ((Object)(object)GameManager._current != (Object)null && (Object)(object)GameManager._current._statLogics != (Object)null && i < GameManager._current._statLogics._scriptableProfessions.Length) { val2 = GameManager._current._statLogics._scriptableProfessions[i]; } if ((Object)(object)val2 == (Object)null) { continue; } if (val2._professionName == "Fishing") { int professionLvl = val._professionLvl; if (professionLvl > _previousFishingLevel) { for (int j = _previousFishingLevel + 1; j <= professionLvl; j++) { string text = $"Fishing Lv. {j}"; SendCheckByName(text); SendAPChatMessage("Found <color=#FFFF00>" + text + "</color>! Sent item to another player!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] " + text + " reached!")); } _previousFishingLevel = professionLvl; } } if (!(val2._professionName == "Mining")) { continue; } int professionLvl2 = val._professionLvl; if (professionLvl2 > _previousMiningLevel) { for (int k = _previousMiningLevel + 1; k <= professionLvl2; k++) { string text2 = $"Mining Lv. {k}"; SendCheckByName(text2); SendAPChatMessage("Found <color=#FFFF00>" + text2 + "</color>! Sent item to another player!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] " + text2 + " reached!")); } _previousMiningLevel = professionLvl2; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Error checking skill levels: " + ex.Message)); } } private void PollForQuestCompletions() { try { Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { return; } PlayerQuesting component = ((Component)mainPlayer).GetComponent<PlayerQuesting>(); if ((Object)(object)component == (Object)null || component._finishedQuests == null) { return; } if (!_questDebugLogged && component._finishedQuests.Count > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] DEBUG: Found {component._finishedQuests.Count} completed quests"); foreach (string key2 in component._finishedQuests.Keys) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] DEBUG: Completed quest: '" + key2 + "'")); } _questDebugLogged = true; } foreach (KeyValuePair<string, long> item in AllQuestToLocation) { string key = item.Key; long value = item.Value; if (component._finishedQuests.ContainsKey(key) && !_completedQuests.Contains(key)) { SendCheckById(value); _completedQuests.Add(key); SendAPChatMessage("Found <color=#FFFF00>" + key + "</color>! Sent item to another player!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[AtlyssAP] Quest completed: " + key)); } } CheckGoalCompletion(mainPlayer, component); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Error polling quests: " + ex.Message)); } } public void SendCheckById(long locationId) { if (!connected || _session == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"[AtlyssAP] Not connected; cannot send check."); } else { if (_reportedChecks.Contains(locationId)) { return; } try { if (_session != null && _session.Socket != null && _session.Socket.Connected) { _session.Locations.CompleteLocationChecks(locationId); _reportedChecks.Add(locationId); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] Sent check ID: {locationId}"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to send check: " + ex.Message)); } } } public void SendCheckByName(string locationName) { if (AllLocationNameToId.TryGetValue(locationName, out var value)) { SendCheckById(value); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[AtlyssAP] Location not found in DataTables: '" + locationName + "'")); } } public void SendGoalComplete() { if (_goalSent || _session == null) { return; } try { _session.Socket.SendPacket(new StatusUpdatePacket { Status = ArchipelagoClientState.ClientGoal }); _goalSent = true; SendAPChatMessage("<color=#FFD700>GOAL COMPLETE!</color> <color=#FFFF00>You have achieved your objective!</color>"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AtlyssAP] Goal met (option {goalOption}) — sent ClientGoal to server."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[AtlyssAP] Failed to send goal completion: " + ex.Message)); } } private bool CheckAllQuestsFromProfile() { try { float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastAllQuestsCheck < 5f) { return false; } _lastAllQuestsCheck = unscaledTime; string activeProfilePath = _activeProfilePath; if (string.IsNul