using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BasicItemSync.Data; using BasicItemSync.Modules.Hooks; using BasicItemSync.Modules.Network.Client; using BasicItemSync.Modules.Network.Server; using BepInEx; using GenericVariableExtension; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SSMP.Api.Client; using SSMP.Api.Client.Networking; using SSMP.Api.Command; using SSMP.Api.Command.Server; using SSMP.Api.Server; using SSMP.Api.Server.Networking; using SSMP.Logging; using SSMP.Networking.Packet; using SSMP.Networking.Packet.Data; using Silksong.AssetHelper.ManagedAssets; using TeamCherry.Localization; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BasicItemSync")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+40f31cdea2e2ed2295a61cb322755c47cd9a53b8")] [assembly: AssemblyProduct("BasicItemSync")] [assembly: AssemblyTitle("BasicItemSync")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/BobbyTheCatfish/BasicItemSync")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BasicItemSync { internal class Common { internal const string AddonName = "BasicItemSync"; internal const string AddonVersion = "0.1.0"; internal const int AddonApiVersion = 1; public static bool FlagAllowed(SyncServerSettings settings, FlagType flag) { if (ServerAddon.Settings.KillSwitch) { return false; } return flag switch { FlagType.Ability => settings.SyncAbilities, FlagType.Map => settings.SyncMaps, FlagType.Pin => settings.SyncPins, FlagType.Bellshrine => settings.SyncProgression, FlagType.Boss => settings.SyncBattles, FlagType.Arena => settings.SyncBattles, FlagType.Progression => settings.SyncProgression, FlagType.Flea => settings.SyncCollectables, FlagType.Collectable => settings.SyncCollectables, FlagType.Bellway => settings.SyncTransit, FlagType.Ventrica => settings.SyncTransit, FlagType.Currency => settings.SyncCurrency, FlagType.Mask => settings.SyncUpgrades, FlagType.Spool => settings.SyncUpgrades, FlagType.Pouch => settings.SyncUpgrades, FlagType.CraftingKit => settings.SyncUpgrades, FlagType.Quest => settings.SyncQuests, FlagType.Tool => settings.SyncTools, FlagType.Crest => settings.SyncTools, FlagType.SilkHeart => settings.SyncUpgrades, FlagType.Needle => settings.SyncUpgrades, FlagType.Bench => settings.SyncProgression, FlagType.Shortcut => true, FlagType.QuestItem => settings.SyncQuestItems, FlagType.DoNotSync => false, _ => false, }; } } internal enum FlagType { Ability, Map, Pin, Bellshrine, Boss, Arena, Progression, Collectable, Bellway, Ventrica, Mask, Spool, Pouch, CraftingKit, SilkHeart, Needle, Quest, QuestItem, Tool, Crest, Currency, Shortcut, Bench, Flea, DoNotSync } internal enum InternalCurrencyType { Rosary, ShellShard } internal enum Packets { BoolPlayerData, IntPlayerData, FloatPlayerData, Collectable, Currency, Quest, Tool, Upgrade, Settings, PersistentBool, PersistentInt } internal class FakeLogger : ILogger { public void Debug(string message) { } public void Error(string message) { } public void Info(string message) { } public void Message(string message) { } public void Warn(string message) { } } internal static class Log { private static ILogger logger = (ILogger)(object)new FakeLogger(); public static void SetLogger(ILogger log) { logger = log; } public static void LogInfo(params object[] data) { foreach (object obj in data) { logger.Info(obj.ToString()); } } public static void LogWarning(params object[] data) { foreach (object obj in data) { logger.Warn(obj.ToString()); } } public static void LogError(params object[] data) { foreach (object obj in data) { logger.Error(obj.ToString()); } } public static void LogFatal(params object[] data) { foreach (object obj in data) { logger.Error(obj.ToString()); } } public static void LogDebug(params object[] data) { } public static void LogMessage(params object[] data) { foreach (object obj in data) { logger.Message(obj.ToString()); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("io.github.bobbythecatfish.basicitemsync", "BasicItemSync", "0.1.0")] public class SyncPlugin : BaseUnityPlugin { private static List CurrentFrameActions = new List(); private static List NextFrameActions = new List(); internal static SyncPlugin Instance; internal static Dictionary> Collectables = new Dictionary>(); public const string Id = "io.github.bobbythecatfish.basicitemsync"; public static string Name => "BasicItemSync"; public static string Version => "0.1.0"; private void Awake() { Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " (io.github.bobbythecatfish.basicitemsync) has loaded!")); ClientAddon.RegisterAddon((ClientAddon)(object)new ClientAddon()); ServerAddon.RegisterAddon((ServerAddon)(object)new ServerAddon()); Collectables["Mask"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Heart Piece.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Spool"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Silk Spool.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Pouch"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Tool Pouch Pickup.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["CraftKit"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Tool Kit Pickup.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["SilkHeart"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Silk Heart.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Needle"] = ManagedAsset.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Needle Upgrade.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } internal static void AddNextFrameAction(Action action) { NextFrameActions.Add(action); } private void Update() { if (CurrentFrameActions.Count > 0) { Action[] array = CurrentFrameActions.ToArray(); CurrentFrameActions.Clear(); Action[] array2 = array; for (int i = 0; i < array2.Length; i++) { array2[i](); } } } private void LateUpdate() { if (NextFrameActions.Count > 0) { CurrentFrameActions = NextFrameActions.ToList(); NextFrameActions.Clear(); } } } } namespace BasicItemSync.Modules { internal class BattleManager { private static BattleScene? GetBattleScene(string keyName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); DeactivateIfPlayerdataTrue val2 = default(DeactivateIfPlayerdataTrue); for (int i = 0; i < rootGameObjects.Length; i++) { BattleScene[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren(); foreach (BattleScene val in componentsInChildren) { if (((Component)val).TryGetComponent(ref val2) && val2.boolName == keyName) { return val; } } } return null; } public static void DefeatBattleScene(string keyName) { if (keyName == "defeatedBellBeast") { OnBellBeast(); return; } BattleScene battleScene = GetBattleScene(keyName); if (!Object.op_Implicit((Object)(object)battleScene)) { return; } if (battleScene.started) { int currentWave = battleScene.currentWave; battleScene.DoEndBattle(); HealthManager[] componentsInChildren = ((Component)battleScene.waves[currentWave]).gameObject.GetComponentsInChildren(); foreach (HealthManager val in componentsInChildren) { if (!val.isDead) { val.Die((float?)0f, (AttackTypes)0, true); } } } else { ((Component)battleScene).gameObject.SetActive(false); } } private static void OnBellBeast() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name == "Bone_05") { Scene sceneByName = SceneManager.GetSceneByName("Bone_05_boss"); if (((Scene)(ref sceneByName)).IsValid()) { GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { rootGameObjects[i].SetActive(false); } } } else if (((Scene)(ref activeScene)).name == "Memory_Silk_Heart_BellBeast") { SceneLoadInfo val = new SceneLoadInfo { SceneName = "Bone_05", EntryGateName = "bot1", PreventCameraFadeOut = true, WaitForSceneTransitionCameraFade = false, Visualization = (SceneLoadVisualizations)0, AlwaysUnloadUnusedAssets = true, IsFirstLevelForPlayer = false }; HeroController.instance.SetSilkRegenBlocked(false); PlayerData.instance.disableInventory = false; ToolItemManager.SetIsInCutscene(false); GameManager.instance.BeginSceneTransition(val); } } } internal class ClientState { public static string LastItem = ""; public static string LastPlayerData = ""; public static string LastPersistent = ""; public static int LastCurrency = 0; public static FlagType LastUpgrade = FlagType.Currency; public static bool WasItemReceived(string item) { return LastItem == item; } public static bool WasCurrencyReceived(int amount) { return LastCurrency == amount; } public static bool WasUpgradeReceived(FlagType flagType) { return LastUpgrade == flagType; } public static bool WasPersistentReceived(string key) { return LastPersistent == key; } } internal class ObjectHelper { public static PersistentItem? FindPersistent(string sceneName, string id) where T : IEquatable { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { PersistentItem[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren>(); foreach (PersistentItem val in componentsInChildren) { if (val.ItemData.ID == id && val.ItemData.SceneName == sceneName) { return val; } } } return null; } } internal class PersistentHandler { public static void SetPersistentIntData(string sceneName, string keyName, int status, FlagType flagType) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) PersistentItemData value = new PersistentItemData { SceneName = sceneName, ID = keyName, IsSemiPersistent = false, Value = status }; PersistentItemData val = default(PersistentItemData); if (SceneData.instance.PersistentInts.TryGetValue(sceneName, keyName, ref val)) { val.Value = status; } else { SceneData.instance.PersistentInts.SetValue(value); } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (flagType == FlagType.Currency && name != sceneName) { return; } PersistentItem val2 = ObjectHelper.FindPersistent(sceneName, keyName); if (!((Object)(object)val2 == (Object)null)) { PersistentIntItem val3 = (PersistentIntItem)(object)((val2 is PersistentIntItem) ? val2 : null); if (val3 != null) { ((PersistentItem)(object)val3).ItemData.Value = status; ((PersistentItem)(object)val3).Start(); } } } public static void SetPersistentBoolData(string sceneName, string keyName, bool status, bool forceDisable = false) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) PersistentItemData value = new PersistentItemData { SceneName = sceneName, ID = keyName, IsSemiPersistent = false, Value = status }; PersistentItemData val = default(PersistentItemData); if (SceneData.instance.PersistentBools.TryGetValue(sceneName, keyName, ref val)) { val.Value = status; } else { SceneData.instance.PersistentBools.SetValue(value); } PersistentItem val2 = ObjectHelper.FindPersistent(sceneName, keyName); if ((Object)(object)val2 == (Object)null) { return; } PersistentBoolItem val3 = (PersistentBoolItem)(object)((val2 is PersistentBoolItem) ? val2 : null); if (val3 == null) { return; } Lever val4 = default(Lever); Lever_tk2d val5 = default(Lever_tk2d); PersistentBoolItemResponder val6 = default(PersistentBoolItemResponder); if (((Component)val3).TryGetComponent(ref val4)) { TrackTriggerObjects canHitTrigger = val4.canHitTrigger; val4.canHitTrigger = null; val4.Hit(new HitInstance { IsFirstHit = true, IsNailTag = true, Direction = 0f }); val4.canHitTrigger = canHitTrigger; } else if (((Component)val3).TryGetComponent(ref val5)) { TrackTriggerObjects canHitTrigger2 = val5.canHitTrigger; val5.canHitTrigger = null; val5.Hit(new HitInstance { IsFirstHit = true, IsNailTag = true, Direction = 0f }); val5.canHitTrigger = canHitTrigger2; } else if (Object.op_Implicit((Object)(object)((PersistentItem)(object)val3).fsm)) { ((PersistentItem)(object)val3).SetValueOnFSM(((PersistentItem)(object)val3).fsm, status); ((PersistentItem)(object)val3).fsm.SendEvent("QUICK BREAK"); ((PersistentItem)(object)val3).fsm.SendEvent("ACTIVATE"); ((PersistentItem)(object)val3).fsm.SendEvent("DESTROY"); } else if (((Component)val3).TryGetComponent(ref val6)) { val6.InvokeEvents(status); } if (val3.disableIfActivated || forceDisable) { if (Object.op_Implicit((Object)(object)val3.disablePrefabIfActivated)) { val3.disablePrefabIfActivated.SetActive(false); } ((Component)val3).gameObject.SetActive(false); } } } internal class QuestHandler { public static void EndQuest(string name) { FullQuestBase quest = QuestManager.GetQuest(name); if (Object.op_Implicit((Object)(object)quest)) { quest.SilentlyComplete(); quest.ConsumeTarget(); UI.ShowPopup((ICollectableUIMsgItem)(object)new QuestPopupItem(quest)); } } public static void EndQuestSilent(string name) { FullQuestBase quest = QuestManager.GetQuest(name); if (Object.op_Implicit((Object)(object)quest)) { quest.SilentlyComplete(); } } } internal class UI { public static void ShowPopup(ICollectableUIMsgItem item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) CollectableUIMsg.Spawn(item, Color.white, (CollectableUIMsg)null, false); } } internal class QuestPopupItem : ICollectableUIMsgItem, IUIMsgPopupItem { public FullQuestBase Quest; public QuestPopupItem(FullQuestBase quest) { Quest = quest; } public Object GetRepresentingObject() { return (Object)(object)Quest; } public float GetUIMsgIconScale() { return 1f; } public string GetUIMsgName() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return Language.Get(((BasicQuestBase)Quest).DisplayName.Key, ((BasicQuestBase)Quest).DisplayName.Sheet); } public Sprite GetUIMsgSprite() { return ((BasicQuestBase)Quest).QuestType.Icon; } public bool HasUpgradeIcon() { return false; } } internal class Upgrader { private static FakeCollectable? MaskCollectable; private static FakeCollectable? SpoolCollectable; private static FakeCollectable? PouchCollectable; private static FakeCollectable? CraftingKitCollectable; private static FakeCollectable? SilkHeartCollectable; private static FakeCollectable? NeedleUpgrade; public static bool UpgradeMask(string sceneName) { string text = "Heart Piece"; if (sceneName == "Bone_East_LavaChallenge") { text += " (1)"; } PersistentHandler.SetPersistentBoolData(sceneName, text, status: true, forceDisable: true); PlayerData instance = PlayerData.instance; instance.heartPieces++; if (PlayerData.instance.heartPieces >= 4) { PlayerData.instance.heartPieces = 0; HeroController.instance.AddToMaxHealth(1); HeroController.instance.MaxHealth(); EventRegister.SendEvent("MAX HP UP", (GameObject)null); } LoadAndDisplay(ref MaskCollectable, "Mask", "UI", "SHOP_SHELLFRAG_NAME"); return Save(); } public static bool UpgradeSpool(string sceneName) { PersistentHandler.SetPersistentBoolData(sceneName, "Silk Spool", status: true, forceDisable: true); PlayerData instance = PlayerData.instance; instance.silkSpoolParts++; if (PlayerData.instance.silkSpoolParts >= 2) { PlayerData.instance.silkSpoolParts = 0; HeroController.instance.AddToMaxSilk(1); EventRegister.SendEvent("SPOOL MAX UP", (GameObject)null); } LoadAndDisplay(ref SpoolCollectable, "Spool", "UI", "SHOP_SPOOL_SEGMENT_NAME"); return Save(); } public static bool GiveCollectable(string persistentScene, string persistentKey, string itemKey) { if (!string.IsNullOrEmpty(persistentScene) && !string.IsNullOrEmpty(persistentKey)) { PersistentHandler.SetPersistentBoolData(persistentScene, persistentKey, status: true, forceDisable: true); } CollectableItem itemByName = CollectableItemManager.GetItemByName(itemKey); itemByName.AddAmount(1); UI.ShowPopup((ICollectableUIMsgItem)(object)itemByName); return Save(); } public static bool UpgradePouch() { PlayerData instance = PlayerData.instance; instance.ToolPouchUpgrades++; LoadAndDisplay(ref PouchCollectable, "Pouch", "UI", "INV_NAME_TOOLPOUCH"); return Save(); } public static bool UpgradeCraftingKit() { PlayerData instance = PlayerData.instance; instance.ToolKitUpgrades++; LoadAndDisplay(ref CraftingKitCollectable, "CraftKit", "UI", "INV_MSG_TOOLKIT"); return Save(); } public static bool UpgradeSilkHeart(string scene) { if (HeroControllerHook.LastCollectedScene == scene) { return false; } HeroControllerHook.LastCollectedScene = scene; PlayerData instance = PlayerData.instance; instance.silkRegenMax++; LoadAndDisplay(ref SilkHeartCollectable, "SilkHeart", "UI", "INV_MSG_THREAD_HEART"); return Save(); } public static bool UpgradeNeedle() { PlayerData instance = PlayerData.instance; instance.nailUpgrades++; LoadAndDisplay(ref NeedleUpgrade, "Needle", "UI", "INV_MSG_NEEDLE_UPGRADE"); return Save(); } private static void LoadAndDisplay(ref FakeCollectable? assetRef, string dictKey, string langSheet, string langKey) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)assetRef == (Object)null) { if (!SyncPlugin.Collectables.TryGetValue(dictKey, out ManagedAsset value)) { return; } if (!((ManagedAssetBase)(object)value).IsLoaded) { ((MonoBehaviour)SyncPlugin.Instance).StartCoroutine(Coroutine(value)); return; } assetRef = ((ManagedAssetBase)(object)value).Handle.Result; } UI.ShowPopup((ICollectableUIMsgItem)(object)assetRef); IEnumerator Coroutine(ManagedAsset asset) { if (asset != null) { ((ManagedAssetBase)(object)asset).Load(); yield return ((ManagedAssetBase)(object)asset).Handle; if (((ManagedAssetBase)(object)asset).Handle.OperationException != null) { Debug.LogError((object)$"Error loading asset: {((ManagedAssetBase)(object)asset).Handle.OperationException}"); } else { FakeCollectable result = ((ManagedAssetBase)(object)asset).Handle.Result; result.uiMsgName = new LocalisedString { Sheet = langSheet, Key = langKey }; UI.ShowPopup((ICollectableUIMsgItem)(object)result); } } } } public static bool Save() { if (!Object.op_Implicit((Object)(object)HeroController.SilentInstance)) { Log.LogError("[CLI: SAVE] Not in save file. Cannot save data."); return false; } GameManager.instance.SaveGame((Action)delegate { }); return true; } public static bool NoOp() { return false; } } } namespace BasicItemSync.Modules.Network { internal class ClientPacket : IPacketData { public virtual bool IsReliable => true; public virtual bool DropReliableDataIfNewerExists => false; public virtual void ReadData(IPacket packet) { } public virtual void WriteData(IPacket packet) { } } internal class SendFlagPacket : ClientPacket { public string Key = ""; public string Name = ""; public FlagType FlagType; public override void WriteData(IPacket packet) { packet.Write(Key); packet.Write(Name); packet.Write((int)FlagType); } public override void ReadData(IPacket packet) { Key = packet.ReadString(); Name = packet.ReadString(); FlagType = (FlagType)packet.ReadInt(); } } internal class SendBoolItemPacket : SendFlagPacket { public bool State = true; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadBool(); } } internal class SendUpgradeItemPacket : SendFlagPacket { private FlagType UpgradeType; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write((int)UpgradeType); } public override void ReadData(IPacket packet) { base.ReadData(packet); UpgradeType = (FlagType)packet.ReadInt(); } } internal class SendPersistentPacket : SendFlagPacket { public string PersistentScene = ""; public string PersistentObject = ""; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(PersistentScene); packet.Write(PersistentObject); } public override void ReadData(IPacket packet) { base.ReadData(packet); PersistentScene = packet.ReadString(); PersistentObject = packet.ReadString(); } } internal class SendPersistentBoolPacket : SendPersistentPacket { public bool State = true; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadBool(); } } internal class SendPersistentIntPacket : SendPersistentPacket { public int State; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadInt(); } } internal class SendIntItemPacket : SendFlagPacket { public int Number; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(Number); } public override void ReadData(IPacket packet) { base.ReadData(packet); Number = packet.ReadInt(); } } internal class SendFloatItemPacket : SendFlagPacket { public float Number; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(Number); } public override void ReadData(IPacket packet) { base.ReadData(packet); Number = packet.ReadFloat(); } } internal class SendCurrencyPacket : ClientPacket { public InternalCurrencyType CurrencyType; public int Amount; public override bool IsReliable => false; public override void WriteData(IPacket packet) { packet.Write((byte)CurrencyType); packet.Write(Amount); } public override void ReadData(IPacket packet) { CurrencyType = (InternalCurrencyType)packet.ReadByte(); Amount = packet.ReadInt(); } } internal class SettingsUpdatePacket : IPacketData { public SyncServerSettings Settings; public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public void WriteData(IPacket packet) { List list = Settings.ToValues(); packet.Write(list.Count); foreach (bool item in list) { packet.Write(item); } } public void ReadData(IPacket packet) { int num = packet.ReadInt(); List list = new List(); for (int i = 0; i < num; i++) { list.Add(packet.ReadBool()); } Settings = SyncServerSettings.PopulateFromValues(list); } } internal class SendQuestItemPacket : SendBoolItemPacket { } internal static class PacketInstantiate { internal static IPacketData Instantiate(Packets packetID) { Log.LogDebug($"[PACKETS] Received {packetID}"); return (IPacketData)(packetID switch { Packets.BoolPlayerData => new PacketDataCollection(), Packets.IntPlayerData => new PacketDataCollection(), Packets.FloatPlayerData => new PacketDataCollection(), Packets.Currency => new PacketDataCollection(), Packets.Quest => new PacketDataCollection(), Packets.Tool => new PacketDataCollection(), Packets.Upgrade => new PacketDataCollection(), Packets.Collectable => new PacketDataCollection(), Packets.PersistentBool => new PacketDataCollection(), Packets.PersistentInt => new PacketDataCollection(), Packets.Settings => new SettingsUpdatePacket(), _ => new ErrorThrowingPacket(packetID, server: true), }); } } internal class ErrorThrowingPacket : ClientPacket { public ErrorThrowingPacket(Packets id, bool server) { Log.LogError(id.ToString(), server); throw new NotImplementedException(id.ToString()); } } } namespace BasicItemSync.Modules.Network.Server { internal class NetworkForwarder { private static IServerAddonNetworkReceiver Receiver; private static IServerAddonNetworkSender Sender; public static void Initialize() { Sender = ServerAddon.api.NetServer.GetNetworkSender((ServerAddon)(object)ServerAddon.Instance); Receiver = ServerAddon.api.NetServer.GetNetworkReceiver((ServerAddon)(object)ServerAddon.Instance, (Func)PacketInstantiate.Instantiate); Receiver.RegisterPacketHandler(Packets.BoolPlayerData, (GenericServerPacketHandler)OnBool); Receiver.RegisterPacketHandler(Packets.IntPlayerData, (GenericServerPacketHandler)OnInt); Receiver.RegisterPacketHandler(Packets.FloatPlayerData, (GenericServerPacketHandler)OnFloat); Receiver.RegisterPacketHandler(Packets.Currency, (GenericServerPacketHandler)OnCurrency); Receiver.RegisterPacketHandler(Packets.Quest, (GenericServerPacketHandler)OnQuest); Receiver.RegisterPacketHandler(Packets.Tool, (GenericServerPacketHandler)OnTool); Receiver.RegisterPacketHandler(Packets.Upgrade, (GenericServerPacketHandler)OnUpgrade); Receiver.RegisterPacketHandler(Packets.Collectable, (GenericServerPacketHandler)OnCollectable); Receiver.RegisterPacketHandler(Packets.PersistentBool, (GenericServerPacketHandler)OnPersistentBool); Receiver.RegisterPacketHandler(Packets.PersistentInt, (GenericServerPacketHandler)OnPersistentInt); } private static void Broadcast(ushort senderId, Packets type, ClientPacket packet) { if (Sender == null) { return; } foreach (IServerPlayer player in ServerAddon.api.ServerManager.Players) { if (player.Id != senderId) { Sender.SendCollectionData(type, packet, player.Id); } } } public static void SendSettingsUpdate() { SettingsUpdatePacket settingsUpdatePacket = new SettingsUpdatePacket { Settings = ServerAddon.Settings }; ServerAddon.api.ServerManager.BroadcastMessage("Sync settings have been updated"); Sender.BroadcastSingleData(Packets.Settings, (IPacketData)(object)settingsUpdatePacket); } public static void SendSettingsUpdate(ushort id) { SettingsUpdatePacket settingsUpdatePacket = new SettingsUpdatePacket { Settings = ServerAddon.Settings }; Sender.SendSingleData(Packets.Settings, (IPacketData)(object)settingsUpdatePacket, id); } private static void OnCurrency(ushort id, SendCurrencyPacket packet) { if (ServerAddon.Settings.FlagAllowed(FlagType.Currency)) { Broadcast(id, Packets.Currency, packet); } } private static void OnBool(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.BoolPlayerData, isPD: true); } private static void OnInt(ushort id, SendIntItemPacket packet) { OnFlag(id, packet, Packets.IntPlayerData, isPD: true); } private static void OnFloat(ushort id, SendFloatItemPacket packet) { OnFlag(id, packet, Packets.FloatPlayerData, isPD: true); } private static void OnQuest(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.Quest, isPD: false); } private static void OnTool(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.Tool, isPD: false); } private static void OnUpgrade(ushort id, SendFlagPacket packet) { OnFlag(id, packet, Packets.Upgrade, isPD: false); } private static void OnCollectable(ushort id, SendPersistentBoolPacket packet) { OnFlag(id, packet, Packets.Collectable, isPD: false); } private static void OnPersistentBool(ushort id, SendPersistentBoolPacket packet) { OnFlag(id, packet, Packets.PersistentBool, isPD: true); } private static void OnPersistentInt(ushort id, SendPersistentIntPacket packet) { OnFlag(id, packet, Packets.PersistentInt, isPD: true); } private static void Announce(ushort id, SendFlagPacket packet, bool isPossiblySilent) { FlagType flagType = packet.FlagType; Log.LogDebug($"[SERVER] Received {flagType}"); if (!isPossiblySilent || !string.IsNullOrEmpty(packet.Name)) { string text = flagType switch { FlagType.Ability => "obtained $", FlagType.Map => "obtained $ Map", FlagType.Pin => "obtained $", FlagType.Bellshrine => "activated the $ Bellshrine", FlagType.Boss => "defeated $", FlagType.Arena => "defeated $", FlagType.Progression => "obtained the $", FlagType.Collectable => "collected a $", FlagType.Bellway => "unlocked the $ Bellway Station", FlagType.Ventrica => "unlocked the $ Ventrica", FlagType.Mask => "obtained the Mask Shard in $", FlagType.Spool => "obtained the Spool Fragment in $", FlagType.Pouch => "obtained a Tool Pouch from $", FlagType.CraftingKit => "obtained a Crafting Kit from $", FlagType.SilkHeart => "obtained a Silk Heart", FlagType.Needle => "upgraded their needle", FlagType.Quest => "finished the $ quest", FlagType.QuestItem => "", FlagType.Tool => "obtained $", FlagType.Crest => "obtained $ Crest", FlagType.Currency => "", FlagType.Bench => "unlocked a bench", FlagType.Flea => "saved a flea from $", _ => "", }; if (!string.IsNullOrEmpty(text)) { IServerPlayer player = ServerAddon.api.ServerManager.GetPlayer(id); string text2 = string.Concat(str2: text.Replace("$", string.IsNullOrEmpty(packet.Name) ? packet.Key : packet.Name), str0: ((player != null) ? player.Username : null) ?? "Unknown Player", str1: " "); ServerAddon.api.ServerManager.BroadcastMessage(text2); } } } private static void OnFlag(ushort id, SendFlagPacket packet, Packets type, bool isPD) { if (ServerAddon.Settings.FlagAllowed(packet.FlagType)) { Announce(id, packet, isPD); Broadcast(id, type, packet); } } } internal class ServerAddon : ServerAddon { public static IServerApi api; public static ServerAddon Instance; public static SyncServerSettings Settings; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "BasicItemSync"; protected override string Version => "0.1.0"; public override void Initialize(IServerApi serverApi) { Log.SetLogger(((ServerAddon)this).Logger); Log.LogInfo("Item Sync Server Addon enabled"); Settings = SyncServerSettings.ReadFromFile(); Instance = this; api = serverApi; NetworkForwarder.Initialize(); api.ServerManager.PlayerConnectEvent += OnPlayerConnect; } private void OnPlayerConnect(IServerPlayer player) { NetworkForwarder.SendSettingsUpdate(player.Id); } } internal class SettingCommand : IServerCommand, ICommand { private readonly SyncServerSettings Settings; public bool AuthorizedOnly => true; public string Trigger => "/sync"; public string[] Aliases => Array.Empty(); public SettingCommand(SyncServerSettings settings) { Settings = settings; } public void Execute(ICommandSender commandSender, string[] arguments) { if (arguments.Length < 2) { SendUsage(); return; } string settingName = "Sync" + arguments[1]; FieldInfo fieldInfo = Extensions.GetAllFields(typeof(SyncServerSettings), BindingFlags.Public).FirstOrDefault((FieldInfo s) => s.Name.Equals(settingName, StringComparison.CurrentCultureIgnoreCase)); if (fieldInfo == null) { commandSender.SendMessage("Unknown setting '" + settingName); return; } if (arguments.Length == 2) { bool variable = VariableExtensionsGeneric.GetVariable((object)Settings, fieldInfo.Name); commandSender.SendMessage($"Setting '{fieldInfo.Name}' is currently {variable}"); return; } bool flag; if (arguments[2] == "true") { flag = true; } else { if (!(arguments[2] == "false")) { SendUsage(); return; } flag = false; } VariableExtensionsGeneric.SetVariable((object)Settings, fieldInfo.Name, flag); NetworkForwarder.SendSettingsUpdate(); void SendUsage() { commandSender.SendMessage("Invalid usage: " + Trigger + " "); } } } internal class SyncServerSettings { public bool KillSwitch; public bool SyncCurrency = true; public bool SyncQuests = true; public bool SyncQuestItems; public bool SyncTools = true; public bool SyncAbilities = true; public bool SyncMaps = true; public bool SyncPins = true; public bool SyncBattles = true; public bool SyncProgression = true; public bool SyncCollectables = true; public bool SyncUpgrades = true; public bool SyncTransit = true; public bool SyncShortcuts = true; public bool FlagAllowed(FlagType flag) { if (ServerAddon.Settings.KillSwitch) { return false; } return flag switch { FlagType.Ability => SyncAbilities, FlagType.Map => SyncMaps, FlagType.Pin => SyncPins, FlagType.Bellshrine => SyncProgression, FlagType.Boss => SyncBattles, FlagType.Arena => SyncBattles, FlagType.Progression => SyncProgression, FlagType.Flea => SyncCollectables, FlagType.Collectable => SyncCollectables, FlagType.Bellway => SyncTransit, FlagType.Ventrica => SyncTransit, FlagType.Currency => SyncCurrency, FlagType.Mask => SyncUpgrades, FlagType.Spool => SyncUpgrades, FlagType.Pouch => SyncUpgrades, FlagType.CraftingKit => SyncUpgrades, FlagType.Quest => SyncQuests, FlagType.QuestItem => SyncQuestItems, FlagType.Tool => SyncTools, FlagType.Crest => SyncTools, FlagType.SilkHeart => SyncUpgrades, FlagType.Needle => SyncUpgrades, FlagType.Bench => SyncProgression, FlagType.Shortcut => SyncShortcuts, FlagType.DoNotSync => false, _ => false, }; } public List ToValues() { List properties = GetProperties(); List list = new List(); foreach (PropertyInfo item in properties) { list.Add(VariableExtensionsGeneric.GetVariable((object)this, item.Name)); } return list; } private static List GetProperties() { List list = typeof(SyncServerSettings).GetProperties().ToList(); list.Sort((PropertyInfo a, PropertyInfo b) => a.Name.CompareTo(b.Name)); return list; } public void CopyFrom(SyncServerSettings settings) { foreach (PropertyInfo property in GetProperties()) { VariableExtensionsGeneric.SetVariable((object)this, property.Name, VariableExtensionsGeneric.GetVariable((object)settings, property.Name)); } } public static SyncServerSettings PopulateFromValues(List values) { SyncServerSettings syncServerSettings = new SyncServerSettings(); List properties = GetProperties(); int num = Math.Min(properties.Count, values.Count); if (num != properties.Count) { Log.LogWarning("Mismatched server setting property count"); } for (int i = 0; i < num; i++) { PropertyInfo propertyInfo = properties[i]; bool flag = values[i]; propertyInfo.SetValue(syncServerSettings, flag); } return syncServerSettings; } public static SyncServerSettings ReadFromFile() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ServerSettings.json"); if (!File.Exists(text)) { Log.LogWarning("[SERVER: SETTINGS] " + text + " doesn't exist"); SyncServerSettings syncServerSettings = new SyncServerSettings(); syncServerSettings.SaveToFile(); return syncServerSettings; } try { return JsonConvert.DeserializeObject(File.ReadAllText(text)) ?? new SyncServerSettings(); } catch (Exception arg) { Log.LogError($"[SERVER: SETTINGS] Could not load server settings from file:\n{arg}"); return new SyncServerSettings(); } } public void SaveToFile() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ServerSettings.json"); if (!Directory.Exists(Path.GetDirectoryName(text))) { Log.LogWarning("[SERVER: SETTINGS] " + text + " directory doesn't exist"); return; } string text2 = JsonConvert.SerializeObject((object)this, (Formatting)1); if (text2 == null) { return; } try { File.WriteAllText(text, text2); } catch (Exception arg) { Log.LogError($"[SERVER: SETTINGS] Could not write server settings to file:\n{arg}"); } } } } namespace BasicItemSync.Modules.Network.Client { internal class ClientAddon : ClientAddon { public static IClientApi api; public static ClientAddon Instance; public static SyncServerSettings Settings = new SyncServerSettings(); private static readonly WaitForSeconds CurrencyTimer = new WaitForSeconds(4f); private static Coroutine? CurrencyRoutine; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "BasicItemSync"; protected override string Version => "0.1.0"; public override void Initialize(IClientApi clientApi) { Log.SetLogger(((ClientAddon)this).Logger); Log.LogInfo("Sync Client Addon enabled"); Settings = new SyncServerSettings(); Instance = this; api = clientApi; NetworkSender.Initialize(); NetworkReceiver.Initialize(); api.ClientManager.ConnectEvent += OnConnect; api.ClientManager.DisconnectEvent += OnDisconnect; } private void OnConnect() { EventHooks.Initialize(); Settings = new SyncServerSettings(); api.UiManager.ChatBox.AddMessage("BasicItemSync is in beta. Please report any bugs to the link on the mod page."); CurrencyRoutine = ((MonoBehaviour)SyncPlugin.Instance).StartCoroutine(CurrencySender()); } private void OnDisconnect() { EventHooks.Uninitialize(); if (CurrencyRoutine != null) { NetworkSender.SendPendingCurrency(); ((MonoBehaviour)SyncPlugin.Instance).StopCoroutine(CurrencyRoutine); } } public static void LocalChat(string message) { api.UiManager.ChatBox.AddMessage(message); } private static IEnumerator CurrencySender() { while (true) { NetworkSender.SendPendingCurrency(); yield return CurrencyTimer; } } } internal class NetworkReceiver { private static IClientAddonNetworkReceiver Receiver; public static void Initialize() { Receiver = ClientAddon.api.NetClient.GetNetworkReceiver((ClientAddon)(object)ClientAddon.Instance, (Func)PacketInstantiate.Instantiate); Receiver.RegisterPacketHandler(Packets.Settings, (GenericClientPacketHandler)OnSettingsUpdate); Receiver.RegisterPacketHandler(Packets.BoolPlayerData, (GenericClientPacketHandler)OnBoolFlag); Receiver.RegisterPacketHandler(Packets.IntPlayerData, (GenericClientPacketHandler)OnIntFlag); Receiver.RegisterPacketHandler(Packets.FloatPlayerData, (GenericClientPacketHandler)OnFloatFlag); Receiver.RegisterPacketHandler(Packets.Currency, (GenericClientPacketHandler)OnCurrency); Receiver.RegisterPacketHandler(Packets.Quest, (GenericClientPacketHandler)OnQuestItem); Receiver.RegisterPacketHandler(Packets.Tool, (GenericClientPacketHandler)OnTool); Receiver.RegisterPacketHandler(Packets.Upgrade, (GenericClientPacketHandler)OnUpgrade); Receiver.RegisterPacketHandler(Packets.Collectable, (GenericClientPacketHandler)OnCollectable); Receiver.RegisterPacketHandler(Packets.PersistentBool, (GenericClientPacketHandler)OnPersistentBool); Receiver.RegisterPacketHandler(Packets.PersistentInt, (GenericClientPacketHandler)OnPersistentInt); } private static void OnIntFlag(SendIntItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetInt(packet.Key, packet.Number); ClientState.LastItem = ""; }); } private static void OnFloatFlag(SendFloatItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetFloat(packet.Key, packet.Number); ClientState.LastItem = ""; }); } private static void OnBoolFlag(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetBool(packet.Key, packet.State); ClientState.LastItem = ""; if (packet.FlagType == FlagType.Boss || packet.FlagType == FlagType.Arena) { BattleManager.DefeatBattleScene(packet.Key); } }); } private static void OnCurrency(SendCurrencyPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastCurrency = packet.Amount; if (packet.CurrencyType == InternalCurrencyType.Rosary) { CurrencyManager.AddGeo(packet.Amount); } else { CurrencyManager.AddShards(packet.Amount); } ClientState.LastCurrency = 0; }); } private static void OnQuestItem(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; QuestHandler.EndQuest(packet.Key); ClientState.LastItem = ""; }); } private static void OnTool(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; if (packet.FlagType == FlagType.Crest) { ToolCrest crestByName = ToolItemManager.GetCrestByName(packet.Key); if (Object.op_Implicit((Object)(object)crestByName)) { crestByName.Unlock(); } } else { ToolItem toolByName = ToolItemManager.GetToolByName(packet.Key); if (Object.op_Implicit((Object)(object)toolByName)) { PlayerData.instance.SeenToolGetPrompt = true; PlayerData.instance.SeenToolWeaponGetPrompt = true; if (packet.State) { toolByName.Unlock((Action)null, (PopupFlags)3); } else { toolByName.Lock(); } } } ClientState.LastItem = ""; }); } private static void OnUpgrade(SendFlagPacket packet) { if (!string.IsNullOrEmpty(packet.Name)) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Name; QuestHandler.EndQuestSilent(packet.Name); ClientState.LastItem = ""; }); } SyncPlugin.AddNextFrameAction(delegate { ClientState.LastUpgrade = packet.FlagType; switch (packet.FlagType) { case FlagType.Mask: Upgrader.UpgradeMask(packet.Key); break; case FlagType.Spool: Upgrader.UpgradeSpool(packet.Key); break; case FlagType.Pouch: Upgrader.UpgradePouch(); break; case FlagType.CraftingKit: Upgrader.UpgradeCraftingKit(); break; case FlagType.SilkHeart: Upgrader.UpgradeSilkHeart(packet.Key); break; case FlagType.Needle: Upgrader.UpgradeNeedle(); break; default: Upgrader.NoOp(); break; } ClientState.LastUpgrade = FlagType.DoNotSync; }); } private static void OnCollectable(SendPersistentBoolPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; Upgrader.GiveCollectable(packet.PersistentScene, packet.PersistentObject, packet.Key); ClientState.LastItem = ""; }); } private static void OnPersistentBool(SendPersistentBoolPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PersistentHandler.SetPersistentBoolData(packet.PersistentScene, packet.PersistentObject, packet.State); ClientState.LastItem = ""; }); } private static void OnPersistentInt(SendPersistentIntPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PersistentHandler.SetPersistentIntData(packet.PersistentScene, packet.PersistentObject, packet.State, packet.FlagType); ClientState.LastItem = ""; }); } private static void OnSettingsUpdate(SettingsUpdatePacket packet) { ClientAddon.Settings.CopyFrom(packet.Settings); Log.LogInfo("[CLI] Received new settings"); } } internal class NetworkSender { private static IClientAddonNetworkSender? Sender; private static int RosariesToSend = 0; private static int ShardsToSend = 0; private static Dictionary<(string, string), SendPersistentPacket> PersistentsToSend = new Dictionary<(string, string), SendPersistentPacket>(); public static void Initialize() { Sender = ClientAddon.api.NetClient.GetNetworkSender((ClientAddon)(object)ClientAddon.Instance); } private static void SendData(Packets type, ClientPacket packet) { if (!ClientAddon.api.NetClient.IsConnected || Sender == null) { Log.LogDebug("Not connected"); } else if (!(packet is SendFlagPacket sendFlagPacket) || CanSync(sendFlagPacket.FlagType)) { Log.LogDebug($"[CLI] Sending {type}"); Sender.SendCollectionData(type, packet); } } private static bool CanSync(FlagType flagType) { if (!ClientAddon.Settings.FlagAllowed(flagType)) { Log.LogDebug($"[CLI] Syncing {flagType} is disabled."); return false; } return true; } public static void AddCurrency(InternalCurrencyType currencyType, int amount) { if (CanSync(FlagType.Currency)) { if (currencyType == InternalCurrencyType.Rosary) { RosariesToSend += amount; } else { ShardsToSend += amount; } } } public static void SendPendingCurrency() { if (RosariesToSend > 0) { SendData(Packets.Currency, new SendCurrencyPacket { CurrencyType = InternalCurrencyType.Rosary, Amount = RosariesToSend }); } if (ShardsToSend > 0) { SendData(Packets.Currency, new SendCurrencyPacket { CurrencyType = InternalCurrencyType.ShellShard, Amount = ShardsToSend }); } RosariesToSend = 0; ShardsToSend = 0; } public static void SendPendingPersistents() { if (PersistentsToSend.Count > 0) { foreach (SendPersistentPacket value in PersistentsToSend.Values) { SendData(Packets.PersistentBool, value); } } PersistentsToSend.Clear(); } public static void SendFlag(string playerDataKey, FlagType flagType, string name, bool state = true) { SendData(Packets.BoolPlayerData, new SendBoolItemPacket { Key = playerDataKey, Name = name, FlagType = flagType, State = state }); } public static void SendInt(string playerDataKey, FlagType flagType, string name, int state) { SendData(Packets.IntPlayerData, new SendIntItemPacket { Key = playerDataKey, Name = name, FlagType = flagType, Number = state }); } public static void SendQuestComplete(string internalName, string displayName) { SendData(Packets.Quest, new SendBoolItemPacket { Key = internalName, Name = displayName, FlagType = FlagType.Quest, State = true }); } public static void SendTool(string toolName, string displayName, bool state, bool isCrest) { SendData(Packets.Tool, new SendBoolItemPacket { Key = toolName, Name = displayName, FlagType = (isCrest ? FlagType.Crest : FlagType.Tool), State = state }); } public static void SendUpgrade(string sceneName, FlagType upgradeType) { SendData(Packets.Upgrade, new SendFlagPacket { Key = sceneName, Name = "", FlagType = upgradeType }); } public static void SendCollectable(string key, string itemName, string persistentKey, string persistentScene, FlagType flagType = FlagType.Collectable) { SendData(Packets.Collectable, new SendPersistentBoolPacket { Key = key, Name = itemName, FlagType = flagType, PersistentObject = persistentKey, PersistentScene = persistentScene }); } public static void AddPersistentIntData(string id, string scene, int value, FlagType flagType) { if (!PersistentsToSend.ContainsKey((scene, id))) { PersistentsToSend[(scene, id)] = new SendPersistentIntPacket { Key = "", Name = "", FlagType = flagType, PersistentObject = id, PersistentScene = scene, State = value }; if (PersistentsToSend.Count == 1) { SyncPlugin.AddNextFrameAction(SendPendingPersistents); } } } public static void AddPersistentBoolData(string id, string scene, bool value, FlagType flagType) { if (!PersistentsToSend.ContainsKey((scene, id))) { PersistentsToSend[(scene, id)] = new SendPersistentBoolPacket { Key = "", Name = "", FlagType = flagType, PersistentObject = id, PersistentScene = scene, State = value }; if (PersistentsToSend.Count == 1) { SyncPlugin.AddNextFrameAction(SendPendingPersistents); } } } } } namespace BasicItemSync.Modules.Hooks { [HarmonyPatch(typeof(CollectableItemPickup))] internal class CollectableItemPickupHook { private static Dictionary CollectableTypes = new Dictionary { { "Ant Trapper Item", FlagType.QuestItem }, { "Architect Key", FlagType.Progression }, { "Beastfly Remainss", FlagType.QuestItem }, { "Belltown House Key", FlagType.Progression }, { "Blue Goop Jar", FlagType.QuestItem }, { "Broken SilkShot", FlagType.Tool }, { "Broodmother Remains", FlagType.QuestItem }, { "Clover Heart", FlagType.Progression }, { "Cog Heart Pieces", FlagType.Progression }, { "Common Spine", FlagType.QuestItem }, { "Conchfly Remains", FlagType.QuestItem }, { "Coral Chunk", FlagType.QuestItem }, { "Coral Heart", FlagType.Progression }, { "Coral Ingredient", FlagType.QuestItem }, { "Courier Supplies", FlagType.DoNotSync }, { "Courier Supplies Mask Maker", FlagType.DoNotSync }, { "Courier Supplies Slave", FlagType.DoNotSync }, { "Craw Summons", FlagType.Progression }, { "Crawbell", FlagType.Collectable }, { "Crest Socket Unlocker", FlagType.Collectable }, { "Crow Feather", FlagType.QuestItem }, { "Crowman Memento", FlagType.Collectable }, { "Dock Demo Key", FlagType.DoNotSync }, { "Dock Key", FlagType.Progression }, { "Dresses", FlagType.DoNotSync }, { "Enemy Morsel Seared", FlagType.QuestItem }, { "Enemy Morsel Shredded", FlagType.QuestItem }, { "Enemy Morsel Speared", FlagType.QuestItem }, { "Extractor Machine Pins", FlagType.QuestItem }, { "Farsight", FlagType.Collectable }, { "Fine Pins", FlagType.QuestItem }, { "Fixer Idol", FlagType.Quest }, { "Flower Heart", FlagType.Progression }, { "Great Shard", FlagType.Collectable }, { "Grey Memento", FlagType.Collectable }, { "Growstone", FlagType.Quest }, { "Hunter Heart", FlagType.Collectable }, { "Hunter Memento", FlagType.Collectable }, { "Materium", FlagType.Collectable }, { "Memento Garmond", FlagType.Collectable }, { "Memento Seth", FlagType.Collectable }, { "Memento Surface", FlagType.Collectable }, { "Mossberry", FlagType.QuestItem }, { "Mossberry Stew", FlagType.QuestItem }, { "Pale Oil", FlagType.QuestItem }, { "Pickled Roach Egg", FlagType.QuestItem }, { "Pilgrim Rag", FlagType.QuestItem }, { "Plasmium", FlagType.QuestItem }, { "Plasmium Blood", FlagType.QuestItem }, { "Plasmium Gland", FlagType.Quest }, { "Pristine Core", FlagType.Progression }, { "Quill", FlagType.Collectable }, { "R Ancient Egg", FlagType.Collectable }, { "R Bone Record", FlagType.Collectable }, { "R Librarian Melody Cylinder", FlagType.Progression }, { "R Psalm Cylinder", FlagType.Collectable }, { "R Seal Chit", FlagType.Collectable }, { "R Weaver Record", FlagType.Collectable }, { "R Weaver Totem", FlagType.Collectable }, { "Roach Corpse Item", FlagType.QuestItem }, { "Rock Roller Item", FlagType.QuestItem }, { "Rosary_Set_Frayed", FlagType.Collectable }, { "Rosary_Set_Huge_White", FlagType.Collectable }, { "Rosary_Set_Large", FlagType.Collectable }, { "Rosary_Set_Medium", FlagType.Collectable }, { "Rosary_Set_Small", FlagType.Collectable }, { "Shard Pouch", FlagType.Collectable }, { "Shell Flower", FlagType.QuestItem }, { "Shining Cog", FlagType.QuestItem }, { "Silk Grub", FlagType.Collectable }, { "Silver Bellclapper", FlagType.QuestItem }, { "Simple Key", FlagType.Collectable }, { "Skull King Fragment", FlagType.QuestItem }, { "Slab Key", FlagType.Progression }, { "Snare Soul Bell Hermit", FlagType.Progression }, { "Snare Soul Churchkeeper", FlagType.Progression }, { "Snare Soul Swamp Bug", FlagType.Progression }, { "Song Pilgrim Cloak", FlagType.QuestItem }, { "Sprintmaster Memento", FlagType.Collectable }, { "Tool Metal", FlagType.Collectable }, { "Vintage Nectar", FlagType.QuestItem }, { "Ward Boss Key", FlagType.Progression }, { "Ward Key", FlagType.Progression }, { "White Flower", FlagType.Progression }, { "Wood Witch Item", FlagType.Progression } }; [HarmonyPatch("DoPickupAction")] [HarmonyPostfix] public static void DoPickupAction(CollectableItemPickup __instance, ref bool __result) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) string name = ((Object)__instance.Item).name; if (ClientState.WasItemReceived(name) || !__result || !Object.op_Implicit((Object)(object)__instance.Item)) { return; } if (!CollectableTypes.TryGetValue(((Object)__instance.Item).name, out var value)) { if (!(__instance.Item is CollectableRelic)) { return; } value = FlagType.Collectable; } if (value != FlagType.DoNotSync) { PlayerDataHook.BoolUpdated(__instance.playerDataBool, value: true, (Operation)0); string popupName = __instance.Item.GetPopupName(); string persistentKey; string persistentScene; if (Object.op_Implicit((Object)(object)__instance.persistent)) { persistentKey = ((PersistentItem)(object)__instance.persistent).ItemData.ID; persistentScene = ((PersistentItem)(object)__instance.persistent).ItemData.SceneName; } else { persistentKey = ((Object)((Component)__instance).gameObject).name; Scene activeScene = SceneManager.GetActiveScene(); persistentScene = ((Scene)(ref activeScene)).name; } NetworkSender.SendCollectable(name, popupName, persistentKey, persistentScene, value); } } } [HarmonyPatch(typeof(CurrencyManager))] internal class CurrencyManagerHook { [HarmonyPatch("ChangeCurrency")] [HarmonyPostfix] public static void ChangeCurrency(int amount, CurrencyType type) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (amount >= 1 && !ClientState.WasCurrencyReceived(amount)) { NetworkSender.AddCurrency(((int)type != 0) ? InternalCurrencyType.ShellShard : InternalCurrencyType.Rosary, amount); } } } internal class EventHooks { private static GameObject? HookObj; private static List Hooks = new List(); private static bool CollectedRecently = false; public static void Initialize() { AddHook("HEART PIECE COLLECTED", OnMask); AddHook("SILK SPOOL SAVE", OnSilkSpool); } public static void Uninitialize() { foreach (EventRegister hook in Hooks) { EventRegister.UnsubscribeEvent(hook); } if (Object.op_Implicit((Object)(object)HookObj)) { Object.Destroy((Object)(object)HookObj); } } private static void AddHook(string eventName, Action action) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)HookObj)) { HookObj = new GameObject("BasicItemSync Event Hooks"); Object.DontDestroyOnLoad((Object)(object)HookObj); } EventRegister val = HookObj.AddComponent(); val.SubscribedEvent = eventName; ((EventBase)val).ReceivedEvent += action; EventRegister.SubscribeEvent(val); Hooks.Add(val); } private static void SendUpgrade(FlagType upgradeType) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!CollectedRecently && !ClientState.WasUpgradeReceived(upgradeType)) { CollectedRecently = true; Scene activeScene = SceneManager.GetActiveScene(); NetworkSender.SendUpgrade(((Scene)(ref activeScene)).name, upgradeType); SyncPlugin.AddNextFrameAction(delegate { CollectedRecently = false; }); } } private static void OnMask() { SendUpgrade(FlagType.Mask); } private static void OnSilkSpool() { SendUpgrade(FlagType.Spool); } private static void OnPouch() { SendUpgrade(FlagType.Pouch); } private static void OnCraftingKit() { SendUpgrade(FlagType.CraftingKit); } } [HarmonyPatch(typeof(HeroController))] internal class HeroControllerHook { public static string LastCollectedScene = ""; [HarmonyPatch("CocoonBroken", new Type[] { typeof(bool), typeof(bool) })] [HarmonyPrefix] public static void CocoonBrokenPrefix(out int __state) { __state = ClientState.LastCurrency; int heroCorpseMoneyPool = PlayerData.instance.HeroCorpseMoneyPool; if (heroCorpseMoneyPool != 0) { ClientState.LastCurrency = heroCorpseMoneyPool; } } [HarmonyPatch("CocoonBroken", new Type[] { typeof(bool), typeof(bool) })] [HarmonyPostfix] public static void CocoonBrokenPostfix(int __state) { ClientState.LastCurrency = __state; } [HarmonyPatch("AddToMaxSilkRegen")] [HarmonyPrefix] public static bool AddToMaxSilkRegen() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (ClientState.WasUpgradeReceived(FlagType.SilkHeart)) { return true; } Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name == LastCollectedScene) { return false; } activeScene = SceneManager.GetActiveScene(); NetworkSender.SendUpgrade(LastCollectedScene = ((Scene)(ref activeScene)).name, FlagType.SilkHeart); return true; } } public abstract class PersistentItemMock : MonoBehaviour { public delegate void SetValueEvent(bool value); public delegate void GetValueEvent(out bool value); [SerializeField] private PlayerDataTest saveCondition; private bool dontSave; private bool hasSetup; private bool isValueOverridden; private GameManager gm; private PlayMakerFSM fsm; private PersistentItemData itemData; private bool started; private bool disableIfActivated; private GameObject disablePrefabIfActivated; public PersistentItemData ItemData { get { EnsureSetup(); return itemData; } } protected bool DefaultValue => false; protected PersistentItemData SerializedItemData => itemData; public bool HasLoadedValue { get; private set; } public bool LoadedValue { get; private set; } public event SetValueEvent? OnSetSaveState; public event GetValueEvent? OnGetSaveState; public event Action? SemiPersistentReset; protected virtual void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (itemData == null) { itemData = new PersistentItemData(); } if (saveCondition == null) { saveCondition = new PlayerDataTest(); } gm = GameManager.instance; gm.SavePersistentObjects += SaveState; if (Object.op_Implicit((Object)(object)disablePrefabIfActivated)) { OnSetSaveState += delegate(bool value) { if (value) { disablePrefabIfActivated.SetActive(false); } }; } if (!disableIfActivated) { return; } OnSetSaveState += delegate(bool value) { if (value) { ((Component)this).gameObject.SetActive(false); } }; } private void OnEnable() { if (SerializedItemData.IsSemiPersistent) { gm.ResetSemiPersistentObjects += ResetState; } if (this.OnGetSaveState == null) { fsm = LookForMyFSM(); } } private void OnDisable() { if ((Object)(object)gm != (Object)null) { gm.ResetSemiPersistentObjects -= ResetState; } } private void OnDestroy() { if ((Object)(object)gm != (Object)null) { gm.SavePersistentObjects -= SaveState; } } private void Start() { started = true; EnsureSetup(); CheckIsValid(); bool flag = false; PersistentItemData val = default(PersistentItemData); if (SceneData.instance.PersistentBools.TryGetValue(itemData.SceneName, itemData.ID, ref val)) { itemData.Value = val.Value; flag = true; } if (flag) { this.OnSetSaveState?.Invoke(itemData.Value); if ((Object)(object)fsm == (Object)null) { fsm = LookForMyFSM(); } if ((Object)(object)fsm != (Object)null) { fsm.FsmVariables.FindFsmBool("Activated").Value = itemData.Value; } HasLoadedValue = true; LoadedValue = itemData.Value; } else { UpdateValue(); } } private void UpdateValue() { if (!isValueOverridden) { if (this.OnGetSaveState != null) { this.OnGetSaveState(out ItemData.Value); } else { UpdateActivatedFromFSM(); } } } private void CheckIsValid() { Type type = ((object)this).GetType(); if (((Component)this).GetComponents(type).Length > 1) { Debug.LogError((object)$"There is more than one component of type: {type} on {((Object)((Component)this).gameObject).name}, please remove one!", (Object)(object)this); } } public void SaveState() { if (!saveCondition.IsDefined || saveCondition.IsFulfilled) { SaveStateNoCondition(); } } public void SaveStateNoCondition() { EnsureSetup(); if (!isValueOverridden) { if (this.OnGetSaveState != null) { this.OnGetSaveState(out itemData.Value); } else { UpdateActivatedFromFSM(); } } HasLoadedValue = true; LoadedValue = itemData.Value; if (!dontSave) { SceneData.instance.PersistentBools.SetValue(itemData); } } private void ResetState() { if (!itemData.IsSemiPersistent) { return; } SaveState(); if (!itemData.Value.Equals(DefaultValue)) { itemData.Value = DefaultValue; this.SemiPersistentReset?.Invoke(); if ((Object)(object)fsm != (Object)null) { fsm.SendEvent("RESET"); } } } private void EnsureSetup() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!hasSetup) { hasSetup = true; itemData = SerializedItemData; if (string.IsNullOrEmpty(itemData.ID)) { itemData.ID = ((Object)this).name; } if (string.IsNullOrEmpty(itemData.SceneName)) { PersistentItemData obj = itemData; Scene scene = ((Component)this).gameObject.scene; obj.SceneName = GameManager.GetBaseSceneName(((Scene)(ref scene)).name); } } } private void UpdateActivatedFromFSM() { if ((Object)(object)fsm != (Object)null) { itemData.Value = fsm.FsmVariables.FindFsmBool("Activated").Value; } else { fsm = LookForMyFSM(); } } protected virtual PlayMakerFSM LookForMyFSM() { PlayMakerFSM[] components = ((Component)this).GetComponents(); if (components == null) { return null; } return FSMUtility.FindFSMWithPersistentBool(components); } } [HarmonyPatch(typeof(PersistentItem))] internal class PersistentBoolItemHook { private static readonly Dictionary PersistentTypes = new Dictionary { { "moss vine cluster", FlagType.Shortcut }, { "abyss_collapse_wall_bot", FlagType.Shortcut }, { "shaman_collapse_wall", FlagType.Shortcut }, { "explode_wall", FlagType.Shortcut }, { "explode_wall 4", FlagType.Shortcut }, { "explode_wall fake", FlagType.Shortcut }, { "abyss_collapse_wall", FlagType.Shortcut }, { "weaver_lift_power_chamber", FlagType.Shortcut }, { "ant_lever_persistent", FlagType.Shortcut }, { "one way wall", FlagType.Shortcut }, { "collapser small", FlagType.Shortcut }, { "collapser small_bell_type", FlagType.Shortcut }, { "breakable wall aqueduct start", FlagType.Shortcut }, { "breakable wall", FlagType.Shortcut }, { "breakable wall bot mid", FlagType.Shortcut }, { "breakable wall_silhouette", FlagType.Shortcut }, { "drop_planks", FlagType.Shortcut }, { "inverse remasker", FlagType.Shortcut }, { "grouped remasker", FlagType.Shortcut }, { "grouped remasker_large", FlagType.Shortcut }, { "secret mask", FlagType.Shortcut }, { "secret mask new", FlagType.Shortcut }, { "secret mask new sharp ultra", FlagType.Shortcut }, { "secret mask new gate", FlagType.Shortcut }, { "slab_vent_mask", FlagType.Shortcut }, { "breakable window", FlagType.Shortcut }, { "breakable window arborium", FlagType.Shortcut }, { "coral crust wall sphere", FlagType.Shortcut }, { "crust wall", FlagType.Shortcut }, { "coral crust wall tall", FlagType.Shortcut }, { "coral crust wall mid", FlagType.Shortcut }, { "coral crust wall small", FlagType.Shortcut }, { "song_lever_side old", FlagType.Shortcut }, { "song_lever_side", FlagType.Shortcut }, { "song_lever_side right", FlagType.Shortcut }, { "bell wall tall", FlagType.Shortcut }, { "mines lift", FlagType.Shortcut }, { "act3_bellway_01_breakable_wall", FlagType.Shortcut }, { "wind rock", FlagType.Shortcut }, { "plank_wall_cluster", FlagType.Shortcut }, { "understore lever", FlagType.Shortcut }, { "gg_breakable_junk_pile_red", FlagType.Shortcut }, { "charm_break_wall", FlagType.Shortcut }, { "bone lever", FlagType.Shortcut }, { "chain drop platform", FlagType.Shortcut }, { "bone gate", FlagType.Shortcut }, { "fall_platform_barrels", FlagType.Shortcut }, { "lever", FlagType.Shortcut }, { "song_gate_small", FlagType.Shortcut }, { "trapdoor lever", FlagType.Shortcut }, { "plate", FlagType.Shortcut }, { "bone carriage", FlagType.Shortcut }, { "barrel plat lift", FlagType.Shortcut }, { "dockdashexploderock", FlagType.Shortcut }, { "explode_wall (7) - boneEast07_openedMidRoof", FlagType.Shortcut }, { "bone_east_10_church", FlagType.Shortcut }, { "toll door", FlagType.Shortcut }, { "toll door breakable cog", FlagType.Shortcut }, { "dropbomb rock", FlagType.Shortcut }, { "bone east 11 cross over group", FlagType.Shortcut }, { "coal bucket plat", FlagType.Shortcut }, { "explode_wall_norck", FlagType.Shortcut }, { "hornet_pressure_plate_small_persistent", FlagType.Shortcut }, { "clover gate", FlagType.Shortcut }, { "cog_lever", FlagType.Shortcut }, { "active", FlagType.Shortcut }, { "cog_junk_pile_break", FlagType.Shortcut }, { "pipe_vent_hatch broken", FlagType.Shortcut }, { "plank_wall_cluster_metal", FlagType.Shortcut }, { "slide_gate_ring", FlagType.Shortcut }, { "coral10rightsidegate", FlagType.Shortcut }, { "coral crust mask", FlagType.Shortcut }, { "coral_gate_large", FlagType.Shortcut }, { "stalactite group bottom", FlagType.Shortcut }, { "stalactite group top", FlagType.Shortcut }, { "breakable blocker struts", FlagType.Shortcut }, { "cradle_plat", FlagType.Shortcut }, { "cradle_spike_plat", FlagType.Shortcut }, { "aspid_sealed_gate_stone", FlagType.Shortcut }, { "harpoon ring pull switch", FlagType.Shortcut }, { "dock_pressure_plate_lock", FlagType.Shortcut }, { "greymoor_lever_simple_edited", FlagType.Shortcut }, { "Dust_02Gate", FlagType.Shortcut }, { "greymoor_lever_simple", FlagType.Shortcut }, { "break wall dustpen cage", FlagType.Shortcut }, { "plat collider", FlagType.Shortcut }, { "bell chain plat", FlagType.Shortcut }, { "greymoor stand lever - bridge", FlagType.Shortcut }, { "greymoor stand lever - crane", FlagType.Shortcut }, { "hornet_cage_rect_lift_greymoor", FlagType.Shortcut }, { "propeller", FlagType.Shortcut }, { "greymoor_breakable_plat", FlagType.Shortcut }, { "belltown_lever_drop_plat", FlagType.Shortcut }, { "greymoor_drop_propeller", FlagType.Shortcut }, { "grey_wood_chain_plat_mid_breakable", FlagType.Shortcut }, { "greymoor stand lever", FlagType.Shortcut }, { "greystore_floor_shortcut", FlagType.Shortcut }, { "bank_door", FlagType.Shortcut }, { "bot blocker", FlagType.Shortcut }, { "innerworks_lever", FlagType.Shortcut }, { "top shortcut", FlagType.Shortcut }, { "attic_ladder", FlagType.Shortcut }, { "understore propeller", FlagType.Shortcut }, { "gg_breakable_junk_pile_gold", FlagType.Shortcut }, { "understore big plate gate", FlagType.Shortcut }, { "barrel plat lift thin", FlagType.Shortcut }, { "plug_gate", FlagType.Shortcut }, { "stalactite group", FlagType.Shortcut }, { "vine platform", FlagType.Shortcut }, { "bone lever 2", FlagType.Shortcut }, { "thick silk vines", FlagType.Shortcut }, { "shellwood twig wall", FlagType.Shortcut }, { "hornet_cage_rect_lift_organ", FlagType.Shortcut }, { "organ_lift_broken_drop", FlagType.Shortcut }, { "one way wall crystal", FlagType.Shortcut }, { "junk_pile_break_peak", FlagType.Shortcut }, { "crystal break chunk", FlagType.Shortcut }, { "drop pillar_chunk", FlagType.Shortcut }, { "sack_multi_breakable", FlagType.Shortcut }, { "diving_bell_door_breakable", FlagType.Shortcut }, { "dock_sealed_gate", FlagType.Shortcut }, { "cage_mid_small_long_sway drop", FlagType.Shortcut }, { "breakablewallswampbenchshortcut", FlagType.Shortcut }, { "plank_wall_cluster_swamp", FlagType.Shortcut }, { "witch cluster vine", FlagType.Shortcut }, { "drop_planks_horizontal", FlagType.Shortcut }, { "gloom_lift_destroy", FlagType.Shortcut }, { "hornet_pressure_plate", FlagType.Shortcut }, { "shellwood hive", FlagType.Shortcut }, { "hornet_cage_rect_lift", FlagType.Shortcut }, { "plank solid 2", FlagType.Shortcut }, { "shellwood twig wall start", FlagType.Shortcut }, { "shellwood twig wall top", FlagType.Shortcut }, { "slab_jail_lever", FlagType.Shortcut }, { "door_slabcaged", FlagType.Shortcut }, { "slab lock", FlagType.Shortcut }, { "slab_cage_plat_thin_fall", FlagType.Shortcut }, { "slab lock l", FlagType.Shortcut }, { "slab lock r", FlagType.Shortcut }, { "breakable wall waterways", FlagType.Shortcut }, { "jail gate door - persistent", FlagType.Shortcut }, { "breakable floor_basic song_01", FlagType.Shortcut }, { "breakable wall entrance secret", FlagType.Shortcut }, { "breakable wall ruin lift", FlagType.Shortcut }, { "chain platform breaker", FlagType.Shortcut }, { "breakable wall second", FlagType.Shortcut }, { "propeller understore propeller", FlagType.Shortcut }, { "hatch", FlagType.Shortcut }, { "song_lever_side - merchant quest accepted", FlagType.Shortcut }, { "breakable floor_basic", FlagType.Shortcut }, { "cradle_pipe_trapdoor", FlagType.Shortcut }, { "one way vinewall", FlagType.Shortcut }, { "snail_collapse_wall", FlagType.Shortcut }, { "pipe_cog_lever", FlagType.Shortcut }, { "barrel plat lift dropper bottom", FlagType.Shortcut }, { "barrel plat lift dropper", FlagType.Shortcut }, { "collapser small understore grate", FlagType.Shortcut }, { "fall_platform_barrel wide", FlagType.Shortcut }, { "cog_05_shortcut", FlagType.Shortcut }, { "innerworks trap bridge large", FlagType.Shortcut }, { "architect shrine door", FlagType.Shortcut }, { "fall platform support pole", FlagType.Shortcut }, { "fall_platform_barrel", FlagType.Shortcut }, { "understore lever l main", FlagType.Shortcut }, { "under_lift_large", FlagType.Shortcut }, { "lock_machine", FlagType.Shortcut }, { "pipe_vent_hatch", FlagType.Shortcut }, { "ward_junk_pile_break", FlagType.Shortcut }, { "ward_junk_pile_break 1", FlagType.Shortcut }, { "ward_junk_pile_break 2", FlagType.Shortcut }, { "ward_junk_pile_break 3", FlagType.Shortcut }, { "ward_junk_pile_break 4", FlagType.Shortcut }, { "junk hatch", FlagType.Shortcut }, { "crest_shrine_break_tube", FlagType.Shortcut }, { "loom_room_jar2", FlagType.Shortcut }, { "ant_item_string", FlagType.Collectable }, { "tool_metal_deposit", FlagType.Collectable }, { "ladybug craft pickup", FlagType.CraftingKit }, { "silk grub large cocoon", FlagType.Collectable }, { "silk grub small cocoon 1", FlagType.Collectable }, { "silk grub small cocoon 2", FlagType.Collectable }, { "breakable hang sack memory locket", FlagType.Collectable }, { "collectable item pickup", FlagType.Collectable }, { "slab_item_chain", FlagType.Collectable }, { "breakable hang sack 3", FlagType.Collectable }, { "heart piece", FlagType.Mask }, { "library_glass_heart_piece", FlagType.Mask }, { "silk spool", FlagType.Spool }, { "lamp_hang_top", FlagType.Spool }, { "moss_berry_fruit", FlagType.QuestItem }, { "nectar pickup", FlagType.QuestItem }, { "bone chest", FlagType.Currency }, { "geo small persistent", FlagType.Currency }, { "geo mid persistent", FlagType.Currency }, { "ant chest", FlagType.Currency }, { "shell shard 01 persistent", FlagType.Currency }, { "shell shard 02 persistent", FlagType.Currency }, { "shell shard 03 persistent", FlagType.Currency }, { "city shard chest", FlagType.Currency }, { "pilgrim chest", FlagType.Currency }, { "craw_home_front", FlagType.Currency }, { "craw_home_right", FlagType.Currency }, { "craw_home_hang", FlagType.Currency }, { "chest", FlagType.Currency }, { "shell shard fossil shellwood", FlagType.Currency }, { "churchkeeper_rosary", FlagType.Currency }, { "cog_choir_cylinder", FlagType.Progression }, { "thread_memory_orb_source", FlagType.Progression }, { "shrine pressure plate l", FlagType.Progression }, { "shrine pressure plate TR", FlagType.Progression }, { "shrine pressure plate BR", FlagType.Progression }, { "plaque pressure plate", FlagType.Progression }, { "memory orb group", FlagType.Progression }, { "song knight tube", FlagType.Progression }, { "diving bell door", FlagType.Progression }, { "prince cell door", FlagType.Progression }, { "slab_break_weaver_gate_ring", FlagType.Progression }, { "organ webbed bench", FlagType.Bench }, { "fake bench collapser", FlagType.Bench } }; [HarmonyPatch("Start")] [HarmonyPostfix] private static void Start(PersistentItem __instance) { if (!Object.op_Implicit((Object)(object)__instance.fsm) || __instance.ItemData.Value) { return; } FsmState[] fsmStates = __instance.fsm.FsmStates; foreach (FsmState val in fsmStates) { int num = -1; int num2 = 0; while (num2 < val.Actions.Length) { FsmStateAction obj = val.Actions[num2]; SetFsmBool val2 = (SetFsmBool)(object)((obj is SetFsmBool) ? obj : null); if (val2 == null || !(val2.variableName.Value == "Activated")) { FsmStateAction obj2 = val.Actions[num2]; SetBoolValue val3 = (SetBoolValue)(object)((obj2 is SetBoolValue) ? obj2 : null); if (val3 == null || !(((NamedVariable)val3.boolVariable).Name == "Activated")) { num2++; continue; } } num = num2; break; } if (num != -1) { PersistentHook persistentHook = new PersistentHook(); ((FsmStateAction)persistentHook).Init(val); List list = val.Actions.ToList(); list.Insert(num + 1, (FsmStateAction)(object)persistentHook); val.Actions = list.ToArray(); val.SaveActions(); break; } } } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPrefix] private static void SaveStateNoConditionPrefix(PersistentItem __instance, out bool __state) { __state = __instance.ItemData.Value; } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPostfix] public static void SaveStateNoConditionPostfix(PersistentItem __instance, bool __state) { if (__state == __instance.ItemData.Value) { Log.LogDebug($"persistent '{__instance.ItemData.ID}' value was the same ({__instance.ItemData.Value}), skipping"); return; } if (__instance.itemData.IsSemiPersistent || __instance.dontSave) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was semipersistent"); return; } string iD = __instance.itemData.ID; string sceneName = __instance.itemData.SceneName; bool value = __instance.itemData.Value; string text = Regex.Replace(iD.ToLower(), " ?\\((\\d+|Clone)\\)$", ""); Gate val = default(Gate); FlagType flagType; FlagType value2; if (((Component)__instance).TryGetComponent(ref val)) { flagType = FlagType.Shortcut; } else if (PersistentTypes.TryGetValue(text, out value2)) { flagType = value2; } else if (text.StartsWith("remask")) { flagType = FlagType.Shortcut; } else if (text.StartsWith("black_thread_core")) { flagType = FlagType.Boss; } else if (text.StartsWith("bell_toll")) { flagType = FlagType.Bench; } else { if (!text.StartsWith("battle scene") && !text.StartsWith("black thread battle scene") && !text.StartsWith("boss scene")) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was not sent"); return; } flagType = FlagType.Arena; } Log.LogDebug($"[CLI: PERSISTENT] {text}, {flagType}"); NetworkSender.AddPersistentBoolData(iD, sceneName, value, flagType); } } [HarmonyPatch(typeof(Lever))] internal static class LeverHook { [HarmonyPatch("Hit")] [HarmonyPostfix] public static void Hit(Lever __instance, ref HitResponse __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__result.response != 0 && !__instance.doesNotActivate) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); if (Object.op_Implicit((Object)(object)__instance.persistent) && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponentInParent())) { ((PersistentItem)(object)__instance.persistent).ItemData.Value = true; PersistentBoolItemHook.SaveStateNoConditionPostfix((PersistentItem)(object)__instance.persistent, __state: false); } } } } [HarmonyPatch(typeof(Lever_tk2d))] internal static class Lever_tk2dHook { [HarmonyPatch("Hit")] [HarmonyPrefix] public static void Hit(Lever_tk2d __instance, HitInstance damageInstance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) HitInstance val = damageInstance; PersistentBoolItem val2 = default(PersistentBoolItem); if (((HitInstance)(ref val)).IsNailDamage && (!Object.op_Implicit((Object)(object)__instance.canHitTrigger) || __instance.canHitTrigger.IsInside) && !__instance.activated && ((Component)__instance).TryGetComponent(ref val2)) { ((PersistentItem)(object)val2).ItemData.Value = true; PersistentBoolItemHook.SaveStateNoConditionPostfix((PersistentItem)(object)val2, __state: false); } } } internal class PersistentHook : FsmStateAction { public override void OnEnter() { GameObject gameObject = ((Component)((FsmStateAction)this).Fsm.Owner).gameObject; if (Object.op_Implicit((Object)(object)gameObject)) { PersistentBoolItem component = gameObject.GetComponent(); if (Object.op_Implicit((Object)(object)component)) { ((PersistentItem)(object)component).SaveState(); ((FsmStateAction)this).Finish(); } } } } [HarmonyPatch(typeof(PersistentItem))] internal class PersistentIntItemHook { private static readonly string[] CurrencyItems = new string[4] { "rosary", "shell", "song", "ant_" }; [HarmonyPatch("SaveStateNoCondition")] [HarmonyPrefix] private static void SaveStateNoCondition_Prefix(PersistentItem __instance, out int __state) { __state = __instance.ItemData.Value; } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPostfix] public static void SaveStateNoConditionPostfix(PersistentItem __instance, int __state) { if (__state == __instance.ItemData.Value) { Log.LogDebug($"persistent '{__instance.ItemData.ID}' value was the same ({__instance.ItemData.Value}), skipping"); return; } if (__instance.itemData.IsSemiPersistent || __instance.dontSave) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was semipersistent"); return; } string iD = __instance.itemData.ID; string sceneName = __instance.itemData.SceneName; int value = __instance.itemData.Value; FlagType flagType = FlagType.DoNotSync; string text = iD.ToLower(); string[] currencyItems = CurrencyItems; foreach (string value2 in currencyItems) { if (text.StartsWith(value2)) { flagType = FlagType.Currency; break; } } if (flagType == FlagType.DoNotSync) { if (iD.StartsWith("bellshrine")) { flagType = FlagType.Bellshrine; } else if (iD.StartsWith("library sliding")) { flagType = FlagType.Shortcut; } } if (flagType == FlagType.DoNotSync) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was not sent"); } else { Log.LogDebug($"[CLI: PERSISTENT INT] {text}, {flagType}"); NetworkSender.AddPersistentIntData(iD, sceneName, value, flagType); } } } [HarmonyPatch(typeof(PlayerData))] internal static class PlayerDataHook { [HarmonyPatch("SetBool")] [HarmonyPrefix] private static void SetBool(string boolName, bool value) { BoolUpdated(boolName, value, (Operation)0); } [HarmonyPatch("SetInt")] [HarmonyPrefix] private static void SetInt(string intName, int value) { IntUpdated(intName, value, (Operation)3); } public static void BoolUpdated(string boolName, bool value, Operation operation = (Operation)0) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 if (string.IsNullOrEmpty(boolName)) { return; } Log.LogDebug("[CLI: PD BOOL] " + boolName); if (!ItemNames.BoolKeys.TryGetValue(boolName, out var value2) || ClientState.WasItemReceived(boolName)) { return; } bool state = value; bool flag = PlayerData.instance.GetBool(boolName); if ((int)operation != 0 || value != flag) { if ((int)operation == 1) { state = !flag; } NetworkSender.SendFlag(boolName, value2.Type, value2.Name, state); } } public static void IntUpdated(string intName, int value, Operation operation = (Operation)3) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if (string.IsNullOrEmpty(intName)) { return; } Log.LogDebug("[CLI: PD INT] " + intName); if (!ItemNames.IntKeys.TryGetValue(intName, out var value2)) { return; } int num = PlayerData.instance.GetInt(intName); int num2 = num; if ((int)operation == 3 && value == num) { return; } if ((int)operation == 0) { num2 += value; } else if ((int)operation == 1) { num2 -= value; } else { if ((int)operation != 2) { return; } num2 *= value; } NetworkSender.SendInt(intName, value2.Type, value2.Name, num2); } } [HarmonyPatch(typeof(SetPlayerDataBool))] internal static class SetPlayerDataBoolHook { [HarmonyPatch("SetBool")] [HarmonyPrefix] public static void SetBool(SetPlayerDataBool __instance) { string boolName = __instance.boolName; bool value = __instance.value; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(SetPlayerDataVariable))] internal static class SetPlayerDataVariableHook { [HarmonyPatch("OnEnter")] [HarmonyPrefix] public static void OnEnter(SetPlayerDataVariable __instance) { string value = __instance.VariableName.Value; object value2 = __instance.SetValue.GetValue(); if (value2 is bool value3) { PlayerDataHook.BoolUpdated(value, value3, (Operation)0); } else if (value2 is int value4) { PlayerDataHook.IntUpdated(value, value4, (Operation)3); } } } [HarmonyPatch(typeof(StateChangeSequence))] internal static class StateChangeSequenceHook { [HarmonyPatch("SetIsCompleteBool")] [HarmonyPrefix] public static void OnEnter(StateChangeSequence __instance) { string isCompleteBool = __instance.isCompleteBool; bool value = true; PlayerDataHook.BoolUpdated(isCompleteBool, value, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataCollectable))] internal static class PlayerDataCollectableHook { [HarmonyPatch("Get")] [HarmonyPrefix] public static void Get(PlayerDataCollectable __instance) { PlayerDataHook.BoolUpdated(__instance.linkedPDBool, value: true, (Operation)0); PlayerDataHook.IntUpdated(__instance.linkedPDInt, 1, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataBoolCollectable))] internal static class PlayerDataBoolCollectableHook { [HarmonyPatch("Get")] [HarmonyPostfix] public static void UnlockSequence(PlayerDataBoolCollectable __instance) { string boolName = __instance.boolName; bool value = true; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(CollectableItemBasic))] internal static class CollectableItemBasicHook { [HarmonyPatch("SetUniqueBool")] [HarmonyPrefix] private static void SetUniqueBool(CollectableItemBasic __instance) { PlayerDataHook.BoolUpdated(__instance.uniqueCollectBool, value: true, (Operation)0); } } [HarmonyPatch(typeof(CaravanTroupeHunter))] internal static class CaravanTroupeHunterHook { [HarmonyPatch("OnPurchasedItem")] [HarmonyPrefix] public static void OnPurchasedItem(CaravanTroupeHunter __instance, int itemIndex) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) string boolName = CaravanTroupeHunter.PdBools[__instance.currentListGroups[itemIndex]]; bool value = true; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(ConfigBase))] internal static class ControlReminderHook { [HarmonyPatch("DoAppear")] [HarmonyPrefix] public static void DoAppear(ConfigBase __instance) { string playerDataBool = __instance.PlayerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(CurrencyObjectBase))] internal static class CurrencyObjectBaseHook { [HarmonyPatch("CollectPopup")] [HarmonyPrefix] private static void CollectPopup(CurrencyObjectBase __instance) { PlayerDataHook.BoolUpdated(__instance.firstGetPDBool, value: true, (Operation)0); PlayerDataHook.BoolUpdated(__instance.popupPDBool, value: true, (Operation)0); } } [HarmonyPatch] internal static class GameManagerHook { private static MethodInfo TargetMethod() { return typeof(GameManager).GetMethod("SetPlayerDataVariable", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(typeof(bool)); } private static void Prefix(string fieldName, bool value) { PlayerDataHook.BoolUpdated(fieldName, value, (Operation)0); } } [HarmonyPatch(typeof(ItemReceptacle))] internal static class ItemReceptacleHook { [HarmonyPatch("UnlockSequence")] [HarmonyPrefix] public static void UnlockSequence(ItemReceptacle __instance) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(PersistentPressurePlate))] internal static class PersistentPressurePlateHook { [HarmonyPatch("Activate")] [HarmonyPrefix] public static void Activate(PersistentPressurePlate __instance) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataBoolOperation))] internal static class PlayerDataBoolOperationHook { [HarmonyPatch("Execute")] [HarmonyPrefix] public static void Execute(PlayerDataBoolOperation __instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) PlayerDataHook.BoolUpdated(__instance.variableName, __instance.value, __instance.operation); } } [HarmonyPatch(typeof(PlayerDataIntOperation))] internal static class PlayerDataIntOperationHook { [HarmonyPatch("Execute")] [HarmonyPrefix] public static void Execute(PlayerDataIntOperation __instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) PlayerDataHook.IntUpdated(__instance.variableName, __instance.number, __instance.operation); } } [HarmonyPatch(typeof(SceneAdditiveLoadConditional))] internal static class SceneAdditiveLoadConditionalHook { [HarmonyPatch("OnWasLoaded")] [HarmonyPrefix] public static void OnWasLoaded(SceneAdditiveLoadConditional __instance) { PlayerDataHook.BoolUpdated(__instance.setPdBoolOnLoad, value: true, (Operation)0); } } [HarmonyPatch(typeof(TempGate))] internal static class TempGateHook { [HarmonyPatch("SetBroken")] [HarmonyPrefix] public static void SetBroken(TempGate __instance) { PlayerDataHook.BoolUpdated(__instance.brokenPDBool, value: true, (Operation)0); } } [HarmonyPatch(typeof(ToolItemToggleState))] internal static class ToolItemToggleStateHook { [HarmonyPatch("DoToggle")] [HarmonyPrefix] public static void DoToggle(ToolItemToggleState __instance) { PlayerDataHook.BoolUpdated(__instance.statePdBool, value: true, (Operation)1); } } [HarmonyPatch(typeof(QuestTargetPlayerDataBools))] internal static class QuestTargetPlayerDataBoolsHook { [HarmonyPatch("Get")] [HarmonyPrefix] public static void Get(QuestTargetPlayerDataBools __instance) { if (__instance.UsesSceneBools()) { string boolName = __instance.pdFieldTemplate + GameManager.instance.GetSceneNameString(); bool value = true; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } } [HarmonyPatch(typeof(SubQuest))] internal static class SubQuestHook { [HarmonyPatch("Get")] [HarmonyPrefix] public static void Get(SubQuest __instance) { string linkedBool = __instance.linkedBool; bool value = true; PlayerDataHook.BoolUpdated(linkedBool, value, (Operation)0); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void HasBeenSeen(SubQuest __instance, bool value) { PlayerDataHook.BoolUpdated(__instance.seenBool, value, (Operation)0); } } [HarmonyPatch(typeof(QuestRewardHolder))] internal class QuestRewardHolderHook { [HarmonyPatch("OnItemPickup")] [HarmonyPostfix] private static void OnItemPickup(QuestRewardHolder __instance, ref bool __result) { if (__result) { PlayerDataHook.BoolUpdated(__instance.pickupPdBool, value: true, (Operation)0); } } } [HarmonyPatch] internal static class GameManagerIntHook { private static MethodInfo TargetMethod() { return typeof(GameManager).GetMethod("SetPlayerDataVariable", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(typeof(int)); } private static void Prefix(string fieldName, int value) { PlayerDataHook.IntUpdated(fieldName, value, (Operation)3); } } [HarmonyPatch(typeof(QuestTargetPlayerDataInt))] internal static class QuestTargetPlayerDataIntHook { [HarmonyPatch("Get")] [HarmonyPrefix] public static void Get(QuestTargetPlayerDataInt __instance) { string playerDataInt = __instance.playerDataInt; int value = 1; PlayerDataHook.IntUpdated(playerDataInt, value, (Operation)0); } } [HarmonyPatch(typeof(QuestManager))] internal static class QuestHook { [HarmonyPatch("ShowQuestCompleted")] [HarmonyPrefix] private static void ShowQuestCompleted(FullQuestBase quest) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (ClientState.WasItemReceived(((Object)quest).name)) { return; } if (Object.op_Implicit((Object)(object)quest.RewardItem)) { if (((Object)quest.RewardItem).name == "Heart Piece") { NetworkSender.SendUpgrade("", FlagType.Mask); } else if (((Object)quest.RewardItem).name == "Silk Spool") { NetworkSender.SendUpgrade("", FlagType.Spool); } else if (((Object)quest.RewardItem).name == "Tool Pouch Pickup") { NetworkSender.SendUpgrade("", FlagType.Pouch); } else if (((Object)quest.RewardItem).name == "Took Kit Pickup") { NetworkSender.SendUpgrade("", FlagType.CraftingKit); } else if (((Object)quest.RewardIcon).name == "Needle Upgrade") { NetworkSender.SendUpgrade("", FlagType.Needle); } } string displayName = Language.Get(LocalisedString.op_Implicit(((BasicQuestBase)quest).displayName)); NetworkSender.SendQuestComplete(((Object)quest).name, displayName); } } [HarmonyPatch(typeof(ShopItem))] internal static class ShopItemHook { [HarmonyPatch("SetPurchased")] [HarmonyPrefix] private static void SetPurchased(ShopItem __instance, int subItemIndex) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected I4, but got Unknown PlayerDataHook.BoolUpdated(__instance.playerDataBoolName, value: true, (Operation)0); string[] setExtraPlayerDataBools = __instance.setExtraPlayerDataBools; for (int i = 0; i < setExtraPlayerDataBools.Length; i++) { PlayerDataHook.BoolUpdated(setExtraPlayerDataBools[i], value: true, (Operation)0); } if (subItemIndex >= 0 && subItemIndex < __instance.SubItemsCount) { string playerDataIntName = __instance.playerDataIntName; int value = (int)__instance.GetSubItem(subItemIndex).Value; PlayerDataHook.IntUpdated(playerDataIntName, value, (Operation)3); } SavedItem item = __instance.Item; if ((Object)(object)item != (Object)null) { if (((Object)item).name == "Heart Piece") { NetworkSender.SendUpgrade("", FlagType.Mask); } else if (((Object)item).name == "Silk Spool") { NetworkSender.SendUpgrade("", FlagType.Spool); } else if (((Object)item).name == "Tool Pouch Pickup") { NetworkSender.SendUpgrade("", FlagType.Pouch); } else if (((Object)item).name == "Took Kit Pickup") { NetworkSender.SendUpgrade("", FlagType.CraftingKit); } else if (!(item is ToolBase)) { NetworkSender.SendCollectable(((Object)item).name, item.GetPopupName(), "", ""); } } else { Log.LogError("[CLI: ShopItemHook] Unknown item for shop " + ((Object)__instance).name); } } } [HarmonyPatch(typeof(ToolItem))] internal static class ToolItemHook { [HarmonyPatch("Unlock")] [HarmonyPostfix] public static void Unlock(ToolItem __instance) { if (__instance.IsUnlocked && !ClientState.WasItemReceived(__instance.name)) { NetworkSender.SendTool(__instance.name, ((SavedItem)__instance).GetPopupName(), state: true, isCrest: false); } } [HarmonyPatch("Lock")] [HarmonyPostfix] public static void Lock(ToolItem __instance) { if (!__instance.IsUnlocked && !ClientState.WasItemReceived(__instance.name)) { NetworkSender.SendTool(__instance.name, ((SavedItem)__instance).GetPopupName(), state: false, isCrest: false); } } [HarmonyPatch("SetUnlockedTestsComplete")] [HarmonyPrefix] public static void SetUnlockedTestsComplete(ToolItem __instance) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) TestGroup[] testGroups = __instance.alternateUnlockedTest.TestGroups; for (int i = 0; i < testGroups.Length; i++) { Test[] tests = testGroups[i].Tests; foreach (Test val in tests) { if ((int)val.Type == 0) { PlayerDataHook.BoolUpdated(val.FieldName, val.BoolValue, (Operation)0); } } } } } [HarmonyPatch(typeof(ToolCrest))] internal static class ToolCrestHook { [HarmonyPatch("Unlock")] [HarmonyPostfix] public static void Unlock(ToolCrest __instance) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (__instance.IsUnlocked && !ClientState.WasItemReceived(__instance.name)) { string displayName = Language.Get(__instance.DisplayName.Key, __instance.DisplayName.Sheet); NetworkSender.SendTool(__instance.name, displayName, state: true, isCrest: true); } } } } namespace BasicItemSync.Data { internal struct Key { public string Name; public FlagType Type; public Key(string name, FlagType type) { Name = name; Type = type; } } internal class ItemNames { public static Dictionary BoolKeys = new Dictionary { { "hasNeedolin", new Key("Needolin", FlagType.Ability) }, { "hasDash", new Key("Dash", FlagType.Ability) }, { "hasBrolly", new Key("Drifter's Cloak", FlagType.Ability) }, { "hasWalljump", new Key("Cling Grip", FlagType.Ability) }, { "hasDoubleJump", new Key("Faydown Cloak", FlagType.Ability) }, { "UnlockedFastTravel", new Key("Bell Beast", FlagType.Ability) }, { "UnlockedFastTravelTeleport", new Key("Beastling Call", FlagType.Ability) }, { "hasChargeSlash", new Key("Needle Strike", FlagType.Ability) }, { "hasSuperJump", new Key("Silk Soar", FlagType.Ability) }, { "HasBoundCrestUpgrader", new Key("", FlagType.Ability) }, { "HasMossGrottoMap", new Key("Moss Grotto", FlagType.Map) }, { "HasWildsMap", new Key("Far Fields", FlagType.Map) }, { "HasBoneforestMap", new Key("Marrow", FlagType.Map) }, { "HasDocksMap", new Key("Deep Docks", FlagType.Map) }, { "HasGreymoorMap", new Key("Greymoor", FlagType.Map) }, { "HasBellhartMap", new Key("Bellhart", FlagType.Map) }, { "HasShellwoodMap", new Key("Shellwood", FlagType.Map) }, { "HasCrawlMap", new Key("Wormways", FlagType.Map) }, { "HasHuntersNestMap", new Key("Hunters March", FlagType.Map) }, { "HasJudgeStepsMap", new Key("Blasted Steps", FlagType.Map) }, { "HasDustpensMap", new Key("Sinners Road", FlagType.Map) }, { "HasSlabMap", new Key("Slab", FlagType.Map) }, { "HasPeakMap", new Key("Mount Fay", FlagType.Map) }, { "HasCitadelUnderstoreMap", new Key("Underworks", FlagType.Map) }, { "HasCoralMap", new Key("Sands of Karak", FlagType.Map) }, { "HasSwampMap", new Key("Bilewater", FlagType.Map) }, { "HasCloverMap", new Key("Verdania", FlagType.Map) }, { "HasAbyssMap", new Key("Abyss", FlagType.Map) }, { "HasHangMap", new Key("High Halls", FlagType.Map) }, { "HasSongGateMap", new Key("Grand Gate", FlagType.Map) }, { "HasHallsMap", new Key("Choral Chambers", FlagType.Map) }, { "HasWardMap", new Key("Whiteward", FlagType.Map) }, { "HasCogMap", new Key("Cogwork Core", FlagType.Map) }, { "HasLibraryMap", new Key("Whispering Vaults", FlagType.Map) }, { "HasCradleMap", new Key("Cradle", FlagType.Map) }, { "HasArboriumMap", new Key("Memorium", FlagType.Map) }, { "HasAqueductMap", new Key("Putrified Ducts", FlagType.Map) }, { "HasWeavehomeMap", new Key("Weavenest Atla", FlagType.Map) }, { "visitedMossCave", new Key("", FlagType.Map) }, { "visitedBoneBottom", new Key("", FlagType.Map) }, { "visitedBoneForest", new Key("", FlagType.Map) }, { "visitedMosstown", new Key("", FlagType.Map) }, { "visitedHuntersTrail", new Key("", FlagType.Map) }, { "visitedDeepDocks", new Key("", FlagType.Map) }, { "visitedWilds", new Key("", FlagType.Map) }, { "visitedGrove", new Key("", FlagType.Map) }, { "visitedGreymoor", new Key("", FlagType.Map) }, { "visitedWisp", new Key("", FlagType.Map) }, { "visitedBellhartHaunted", new Key("", FlagType.Map) }, { "visitedBellhart", new Key("", FlagType.Map) }, { "visitedBellhartSaved", new Key("", FlagType.Map) }, { "visitedShellwood", new Key("", FlagType.Map) }, { "visitedCrawl", new Key("", FlagType.Map) }, { "visitedDustpens", new Key("", FlagType.Map) }, { "visitedShadow", new Key("", FlagType.Map) }, { "visitedAqueducts", new Key("", FlagType.Map) }, { "visitedMistmaze", new Key("", FlagType.Map) }, { "visitedCoral", new Key("", FlagType.Map) }, { "visitedCoralRiver", new Key("", FlagType.Map) }, { "visitedCoralRiverInner", new Key("", FlagType.Map) }, { "visitedCoralTower", new Key("", FlagType.Map) }, { "visitedSlab", new Key("", FlagType.Map) }, { "visitedGrandGate", new Key("", FlagType.Map) }, { "visitedCitadel", new Key("", FlagType.Map) }, { "visitedUnderstore", new Key("", FlagType.Map) }, { "visitedWard", new Key("", FlagType.Map) }, { "visitedHalls", new Key("", FlagType.Map) }, { "visitedLibrary", new Key("", FlagType.Map) }, { "visitedStage", new Key("", FlagType.Map) }, { "visitedGloom", new Key("", FlagType.Map) }, { "visitedWeave", new Key("", FlagType.Map) }, { "visitedMountain", new Key("", FlagType.Map) }, { "visitedIceCore", new Key("", FlagType.Map) }, { "visitedHang", new Key("", FlagType.Map) }, { "visitedHangAtrium", new Key("", FlagType.Map) }, { "visitedEnclave", new Key("", FlagType.Map) }, { "visitedArborium", new Key("", FlagType.Map) }, { "visitedCogwork", new Key("", FlagType.Map) }, { "visitedCradle", new Key("", FlagType.Map) }, { "visitedRuinedCradle", new Key("", FlagType.Map) }, { "visitedFleatopia", new Key("", FlagType.Map) }, { "visitedFleaFestival", new Key("", FlagType.Map) }, { "visitedAbyss", new Key("", FlagType.Map) }, { "visitedBoneEast14b", new Key("", FlagType.Map) }, { "enteredGreymoor05", new Key("", FlagType.Map) }, { "visitedShellwood_16", new Key("", FlagType.Map) }, { "previouslyVisitedGreymoor_05", new Key("", FlagType.Map) }, { "visitedShadow03", new Key("", FlagType.Map) }, { "visitedCoralBellshrine", new Key("", FlagType.Map) }, { "visitedUpperSlab", new Key("", FlagType.Map) }, { "SeenBelltownCutscene", new Key("", FlagType.Map) }, { "ShakraFinalQuestAppear", new Key("", FlagType.Map) }, { "hasPinBench", new Key("Bench Pins", FlagType.Pin) }, { "hasPinCocoon", new Key("Cocoon Pin", FlagType.Pin) }, { "hasPinShop", new Key("Shop Pins", FlagType.Pin) }, { "hasPinSpa", new Key("Spa Pins", FlagType.Pin) }, { "hasPinStag", new Key("Bellway Pins", FlagType.Pin) }, { "hasPinTube", new Key("Ventrica Pins", FlagType.Pin) }, { "hasPinFleaMarrowlands", new Key("Marrowlands Flea Pins", FlagType.Pin) }, { "hasPinFleaMidlands", new Key("Midlands Flea Pins", FlagType.Pin) }, { "hasPinFleaBlastedlands", new Key("Blastedlands Flea Pins", FlagType.Pin) }, { "hasPinFleaCitadel", new Key("Citadel Flea Pins", FlagType.Pin) }, { "hasPinFleaPeaklands", new Key("Peaklands Flea Pins", FlagType.Pin) }, { "hasPinFleaMucklands", new Key("Mucklands Flea Pins", FlagType.Pin) }, { "hasMarker_a", new Key("Shell Map Marker", FlagType.Pin) }, { "hasMarker_b", new Key("Ring Map Marker", FlagType.Pin) }, { "hasMarker_c", new Key("Hunt Map Marker", FlagType.Pin) }, { "hasMarker_d", new Key("Dark Map Marker", FlagType.Pin) }, { "hasMarker_e", new Key("Bronze Map Marker", FlagType.Pin) }, { "bellShrineBoneForest", new Key("Marrow", FlagType.Bellshrine) }, { "bellShrineWilds", new Key("Far Fields", FlagType.Bellshrine) }, { "bellShrineGreymoor", new Key("Greymoor", FlagType.Bellshrine) }, { "bellShrineShellwood", new Key("Shellwood", FlagType.Bellshrine) }, { "bellShrineBellhart", new Key("Bellhart", FlagType.Bellshrine) }, { "bellShrineEnclave", new Key("Enclave", FlagType.Bellshrine) }, { "defeatedMossMother", new Key("Moss Mother", FlagType.Boss) }, { "defeatedMossEvolver", new Key("Moss Mothers", FlagType.Boss) }, { "DefeatedBonetownBoss", new Key("Bone Bottom Boss", FlagType.Boss) }, { "skullKingDefeated", new Key("Skull Tyrant", FlagType.Boss) }, { "skullKingKilled", new Key("Skull Tyrant (Bone Bottom)", FlagType.Boss) }, { "defeatedBellBeast", new Key("Bell Beast", FlagType.Boss) }, { "bonebottomQuestBoardFixed", new Key("", FlagType.Boss) }, { "defeatedAntQueen", new Key("Skarrsinger Karmelita", FlagType.Boss) }, { "defeatedLace1", new Key("Lace 1", FlagType.Boss) }, { "laceLeftDocks", new Key("", FlagType.Boss) }, { "defeatedSongGolem", new Key("Fourth Chorus", FlagType.Boss) }, { "defeatedDockForemen", new Key("Forebrothers Signis & Gron", FlagType.Boss) }, { "defeatedAntTrapper", new Key("Gurr the Outcast", FlagType.Boss) }, { "defeatedVampireGnatBoss", new Key("Moorwing", FlagType.Boss) }, { "defeatedCrowCourt", new Key("Crawfather", FlagType.Boss) }, { "defeatedWispPyreEffigy", new Key("Father of the Flame", FlagType.Boss) }, { "roofCrabDefeated", new Key("Craggler", FlagType.Boss) }, { "spinnerDefeated", new Key("Widow", FlagType.Boss) }, { "defeatedSplinterQueen", new Key("Sister Splinter", FlagType.Boss) }, { "defeatedSeth", new Key("Seth", FlagType.Boss) }, { "defeatedFlowerQueen", new Key("Nyleth", FlagType.Boss) }, { "defeatedRoachkeeperChef", new Key("Disgraced Chef Lugoli", FlagType.Boss) }, { "defeatedPhantom", new Key("Phantom", FlagType.Boss) }, { "defeatedBoneFlyerGiant", new Key("Savage Beastfly", FlagType.Boss) }, { "defeatedBoneFlyerGiantGolemScene", new Key("Savage Beastfly 2", FlagType.Boss) }, { "DefeatedSwampShaman", new Key("Groal the Great", FlagType.Boss) }, { "defeatedCoralDrillers", new Key("Great Conchflies", FlagType.Boss) }, { "defeatedCoralDrillerSolo", new Key("Raging Conchfly", FlagType.Boss) }, { "coralDrillerSoloReady", new Key("", FlagType.Boss) }, { "defeatedCoralKing", new Key("Crust King Khann", FlagType.Boss) }, { "defeatedLastJudge", new Key("Last Judge", FlagType.Boss) }, { "defeatedZapCoreEnemy", new Key("Voltvyrm", FlagType.Boss) }, { "defeatedGreyWarrior", new Key("Watcher at the Edge", FlagType.Boss) }, { "defeatedFirstWeaver", new Key("First Sinner", FlagType.Boss) }, { "defeatedBroodMother", new Key("Broodmother", FlagType.Boss) }, { "defeatedTrobbio", new Key("Trobbio", FlagType.Boss) }, { "defeatedTormentedTrobbio", new Key("Tormented Trobbio", FlagType.Boss) }, { "completedGrandStageBattle", new Key("", FlagType.Boss) }, { "defeatedCogworkDancers", new Key("Cogwork Dancers", FlagType.Boss) }, { "defeatedLaceTower", new Key("Lace 2", FlagType.Boss) }, { "laceTowerDoorOpened", new Key("", FlagType.Boss) }, { "MelodyLiftCanReturn", new Key("", FlagType.Boss) }, { "wardBossDefeated", new Key("The Unravelled", FlagType.Boss) }, { "defeatedSongChevalierBoss", new Key("Second Sentinel", FlagType.Boss) }, { "defeatedWhiteCloverstag", new Key("Palestag", FlagType.Boss) }, { "defeatedCloverDancers", new Key("Clover Dancers", FlagType.Boss) }, { "garmondBlackThreadDefeated", new Key("Lost Garmond", FlagType.Boss) }, { "ant02GuardDefeated", new Key("Skarrgard 1", FlagType.Boss) }, { "ant04_battleCompleted", new Key("Hunter's March", FlagType.Arena) }, { "greymoor_04_battleCompleted", new Key("Greymoor Left", FlagType.Arena) }, { "completedGreymoor17Battle", new Key("Greymoor Right", FlagType.Arena) }, { "aspid06_battleComplete", new Key("Aspid 06 [Unknown, please report where this text is found]", FlagType.Arena) }, { "aspid_04b_battleCompleted", new Key("Verdania", FlagType.Arena) }, { "dust01_battleCompleted", new Key("Sinner's Road (Dust 01) [Unknown, please report where this text is found]", FlagType.Arena) }, { "dust03_battleCompleted", new Key("Sinner's Road (Dust 03) [Unknown, please report where this text is found]", FlagType.Arena) }, { "slab_cloak_battle_completed", new Key("Slab", FlagType.Arena) }, { "under07_battleCompleted", new Key("Underworks (Exit)", FlagType.Arena) }, { "completedLibraryEntryBattle", new Key("Whispering Vaults (Entrance)", FlagType.Arena) }, { "completedLibraryAcolyteBattle", new Key("Whispering Vaults (Entrance, Act 3)", FlagType.Arena) }, { "hang04Battle", new Key("High Halls", FlagType.Arena) }, { "song_04_battleCompleted", new Key("Choral Chambers (Spa)", FlagType.Arena) }, { "completedCog10_abyssBattle", new Key("Cogwork Core", FlagType.Arena) }, { "HasMelodyArchitect", new Key("Architect's Melody", FlagType.Progression) }, { "HasMelodyLibrarian", new Key("Vaultkeeper's Melody", FlagType.Progression) }, { "HasMelodyConductor", new Key("Conductor's Melody", FlagType.Progression) }, { "UnlockedMelodyLift", new Key("Cradle Elevator", FlagType.Progression) }, { "collectedWardKey", new Key("White Key", FlagType.Progression) }, { "HasSlabKeyA", new Key("Key of Indolent", FlagType.Progression) }, { "HasSlabKeyB", new Key("Key of Heretic", FlagType.Progression) }, { "HasSlabKeyC", new Key("Key of Apostate", FlagType.Progression) }, { "CollectedHeartFlower", new Key("Pollen Heart", FlagType.Progression) }, { "CollectedHeartCoral", new Key("Encrusted Heart", FlagType.Progression) }, { "CollectedHeartHunter", new Key("Hunter's Heart", FlagType.Progression) }, { "CollectedHeartClover", new Key("Conjoined Heart", FlagType.Progression) }, { "CompletedRedMemory", new Key("Red Memory", FlagType.Progression) }, { "UnlockedDustCage", new Key("Green Prince's Freedom", FlagType.Progression) }, { "grubFarmerEmerged", new Key("Styx", FlagType.Progression) }, { "BelltownHouseUnlocked", new Key("Bellhome", FlagType.Progression) }, { "collectedWardBossKey", new Key("Whiteward Boss Key", FlagType.Progression) }, { "wokeSongChevalier", new Key("Second Sentinel", FlagType.Progression) }, { "PurchasedBonebottomFaithToken", new Key("", FlagType.Collectable) }, { "PurchasedBonebottomToolMetal", new Key("", FlagType.Collectable) }, { "PurchasedPilgrimsRestMemoryLocket", new Key("", FlagType.Collectable) }, { "PurchasedBelltownMemoryLocket", new Key("", FlagType.Collectable) }, { "MerchantEnclaveSocket", new Key("", FlagType.Collectable) }, { "MerchantEnclaveWardKey", new Key("", FlagType.Collectable) }, { "MerchantEnclaveSimpleKey", new Key("", FlagType.Collectable) }, { "MerchantEnclaveToolMetal", new Key("", FlagType.Collectable) }, { "purchasedGrindleSimpleKey", new Key("", FlagType.Collectable) }, { "purchasedGrindleMemoryLocket", new Key("", FlagType.Collectable) }, { "PurchasedArchitectKey", new Key("", FlagType.Collectable) }, { "hasQuill", new Key("Quill", FlagType.Collectable) }, { "hasJournal", new Key("Journal", FlagType.Collectable) }, { "CollectedMementoGrey", new Key("Grey Memento", FlagType.Collectable) }, { "CollectedMementoSprintmaster", new Key("Sprintmaster Memento", FlagType.Collectable) }, { "FleaGamesMementoGiven", new Key("Flea Memento", FlagType.Collectable) }, { "PickedUpCrowMemento", new Key("Craw Memento", FlagType.Collectable) }, { "nuuMementoAwarded", new Key("Hunter's Memento", FlagType.Collectable) }, { "gotPickledRoachEgg", new Key("Pickled Muckmaggot", FlagType.Collectable) }, { "BelltownFurnishingDesk", new Key("Bellhome Desk", FlagType.Collectable) }, { "BelltownFurnishingSpa", new Key("Bellhome Spa", FlagType.Collectable) }, { "BelltownFurnishingFairyLights", new Key("Bellhome Fairy Lights", FlagType.Collectable) }, { "BelltownFurnishingGramaphone", new Key("Bellhome Gramaphone", FlagType.Collectable) }, { "DeskPlacedRelicList", new Key("", FlagType.Collectable) }, { "DeskPlacedLibrarianList", new Key("", FlagType.Collectable) }, { "ConstructedMaterium", new Key("", FlagType.Collectable) }, { "ConstructedFarsight", new Key("", FlagType.Collectable) }, { "UnlockedDocksStation", new Key("Deep Docks", FlagType.Bellway) }, { "UnlockedBoneforestEastStation", new Key("Far Fields", FlagType.Bellway) }, { "UnlockedGreymoorStation", new Key("Greymoor", FlagType.Bellway) }, { "UnlockedBelltownStation", new Key("Bellhart", FlagType.Bellway) }, { "UnlockedCoralTowerStation", new Key("Blasted Steps", FlagType.Bellway) }, { "UnlockedCityStation", new Key("Grand", FlagType.Bellway) }, { "UnlockedPeakStation", new Key("The Slab", FlagType.Bellway) }, { "UnlockedShellwoodStation", new Key("Shellwood", FlagType.Bellway) }, { "UnlockedShadowStation", new Key("Bilewater", FlagType.Bellway) }, { "UnlockedAqueductStation", new Key("Putrified Ducts", FlagType.Bellway) }, { "UnlockedSongTube", new Key("Choral Chambers", FlagType.Ventrica) }, { "UnlockedUnderTube", new Key("Underworks", FlagType.Ventrica) }, { "UnlockedCityBellwayTube", new Key("Grand Bellway", FlagType.Ventrica) }, { "UnlockedHangTube", new Key("High Halls", FlagType.Ventrica) }, { "UnlockedEnclaveTube", new Key("Songclave", FlagType.Ventrica) }, { "UnlockedArboriumTube", new Key("Memorium", FlagType.Ventrica) }, { "PurchasedBonebottomHeartPiece", new Key("", FlagType.Mask) }, { "PurchasedBelltownShellFragment", new Key("", FlagType.Mask) }, { "MerchantEnclaveShellFragment", new Key("", FlagType.Mask) }, { "purchasedGrindleSpoolPiece", new Key("", FlagType.Spool) }, { "PurchasedBelltownSpoolSegment", new Key("", FlagType.Spool) }, { "MerchantEnclaveSpoolPiece", new Key("", FlagType.Spool) }, { "tookGreymoor17Spool", new Key("", FlagType.Spool) }, { "PurchasedPilgrimsRestToolPouch", new Key("", FlagType.Pouch) }, { "PurchasedBelltownToolPouch", new Key("", FlagType.Pouch) }, { "PurchasedForgeToolKit", new Key("", FlagType.CraftingKit) }, { "PurchasedArchitectToolKit", new Key("", FlagType.CraftingKit) }, { "purchasedGrindleToolKit", new Key("", FlagType.CraftingKit) }, { "SavedFlea_Bone_06", new Key("The Marrow", FlagType.Flea) }, { "SavedFlea_Dock_16", new Key("Deep Docks", FlagType.Flea) }, { "SavedFlea_Bone_East_05", new Key("Far Fields", FlagType.Flea) }, { "SavedFlea_Bone_East_17b", new Key("Far Fields", FlagType.Flea) }, { "SavedFlea_Ant_03", new Key("Hunter's March", FlagType.Flea) }, { "SavedFlea_Greymoor_15b", new Key("Greymoor", FlagType.Flea) }, { "SavedFlea_Greymoor_06", new Key("Greymoor", FlagType.Flea) }, { "SavedFlea_Shellwood_03", new Key("Shellwood", FlagType.Flea) }, { "SavedFlea_Bone_East_10_Church", new Key("Hunter's March", FlagType.Flea) }, { "SavedFlea_Coral_35", new Key("Blasted Steps", FlagType.Flea) }, { "SavedFlea_Dust_12", new Key("Sinners Road", FlagType.Flea) }, { "SavedFlea_Dust_09", new Key("Sinners Road", FlagType.Flea) }, { "SavedFlea_Belltown_04", new Key("Bellhart", FlagType.Flea) }, { "SavedFlea_Crawl_06", new Key("The Underworks", FlagType.Flea) }, { "SavedFlea_Slab_Cell", new Key("The Slab", FlagType.Flea) }, { "SavedFlea_Shadow_28", new Key("Bilewater", FlagType.Flea) }, { "SavedFlea_Dock_03d", new Key("Deep Docks", FlagType.Flea) }, { "SavedFlea_Under_23", new Key("The Underworks", FlagType.Flea) }, { "SavedFlea_Shadow_10", new Key("Bilewater", FlagType.Flea) }, { "SavedFlea_Song_14", new Key("The Choral Chambers", FlagType.Flea) }, { "SavedFlea_Coral_24", new Key("The Sands of Karak", FlagType.Flea) }, { "SavedFlea_Peak_05c", new Key("Mount Fay", FlagType.Flea) }, { "SavedFlea_Library_09", new Key("The Whispering Vaults", FlagType.Flea) }, { "SavedFlea_Song_11", new Key("The Choral Chambers", FlagType.Flea) }, { "SavedFlea_Library_01", new Key("The Whispering Valuts", FlagType.Flea) }, { "SavedFlea_Under_21", new Key("The Underworks", FlagType.Flea) }, { "SavedFlea_Slab_06", new Key("The Slab", FlagType.Flea) }, { "CaravanLechSaved", new Key("Greymoor (Kratt)", FlagType.Flea) }, { "tamedGiantFlea", new Key("The Memorium (Giant Flea)", FlagType.Flea) }, { "MetTroupeHunterWild", new Key("Putrified Ducts (Vog)", FlagType.Flea) }, { "hasSilkSpecial", new Key("", FlagType.Tool) }, { "hasNeedleThrow", new Key("Silk Spear", FlagType.Tool) }, { "hasThreadSphere", new Key("Thread Storm", FlagType.Tool) }, { "hasParry", new Key("Cross Stitch", FlagType.Tool) }, { "hasHarpoonDash", new Key("Clawline", FlagType.Tool) }, { "hasSilkCharge", new Key("Sharpdart", FlagType.Tool) }, { "hasSilkBomb", new Key("Rune Rage", FlagType.Tool) }, { "hasSilkBossNeedle", new Key("Pale Nails", FlagType.Tool) }, { "UnlockedExtraBlueSlot", new Key("Upgraded Vesticrest", FlagType.Tool) }, { "UnlockedExtraYellowSlot", new Key("Vesticrest", FlagType.Tool) }, { "dicePilgrimDefeated", new Key("", FlagType.Tool) }, { "completedMemory_reaper", new Key("", FlagType.Tool) }, { "completedMemory_wanderer", new Key("", FlagType.Tool) }, { "completedMemory_beast", new Key("", FlagType.Tool) }, { "completedMemory_witch", new Key("", FlagType.Tool) }, { "completedMemory_toolmaster", new Key("", FlagType.Tool) }, { "completedMemory_shaman", new Key("", FlagType.Tool) }, { "chapelClosed_reaper", new Key("", FlagType.Tool) }, { "chapelClosed_wanderer", new Key("", FlagType.Tool) }, { "chapelClosed_beast", new Key("", FlagType.Tool) }, { "chapelClosed_witch", new Key("", FlagType.Tool) }, { "chapelClosed_toolmaster", new Key("", FlagType.Tool) }, { "chapelClosed_shaman", new Key("", FlagType.Tool) }, { "MottledChildGivenTool", new Key("", FlagType.Tool) }, { "MottledChildNewTool", new Key("", FlagType.Tool) }, { "destroyedRosaryCannonMachine", new Key("", FlagType.Tool) }, { "BonePlazaOpened", new Key("", FlagType.Shortcut) }, { "belltownBasementBreakWall", new Key("", FlagType.Shortcut) }, { "basementAntWall", new Key("", FlagType.Shortcut) }, { "skullKingShortcut", new Key("", FlagType.Shortcut) }, { "greatBoneGateOpened", new Key("", FlagType.Shortcut) }, { "bone01shortcutPlat", new Key("", FlagType.Shortcut) }, { "mosstown01_shortcut", new Key("", FlagType.Shortcut) }, { "Bone_East_04b_ExplodeWall", new Key("", FlagType.Shortcut) }, { "bone03_openedTrapdoor", new Key("", FlagType.Shortcut) }, { "bone03_openedTrapdoorForRockRoller", new Key("", FlagType.Shortcut) }, { "antBenchTrapDefused", new Key("", FlagType.Shortcut) }, { "boneEast07_openedMidRoof", new Key("", FlagType.Shortcut) }, { "openedTallGeyser", new Key("", FlagType.Shortcut) }, { "openedGeyserShaft", new Key("", FlagType.Shortcut) }, { "openedSongGateDocks", new Key("", FlagType.Shortcut) }, { "openedDocksBackEntrance", new Key("", FlagType.Shortcut) }, { "docks_02_shortcut_right", new Key("", FlagType.Shortcut) }, { "docks_02_shortcut_left", new Key("", FlagType.Shortcut) }, { "PilgrimsRestDoorBroken", new Key("", FlagType.Shortcut) }, { "rhinoChurchUnlocked", new Key("", FlagType.Shortcut) }, { "antMerchantShortcut", new Key("", FlagType.Shortcut) }, { "openedBeastmasterDen", new Key("", FlagType.Shortcut) }, { "openedCauldronShortcut", new Key("", FlagType.Shortcut) }, { "cauldronShortcutUpdraft", new Key("", FlagType.Shortcut) }, { "explodeWallBoneEast18c", new Key("", FlagType.Shortcut) }, { "hitCrowCourtSwitch", new Key("", FlagType.Shortcut) }, { "brokeUnderstoreFloor", new Key("", FlagType.Shortcut) }, { "greymoor05_farmerPlatBroken", new Key("", FlagType.Shortcut) }, { "greymoor08_plat_destroyed", new Key("", FlagType.Shortcut) }, { "crawl03_oneWayWall", new Key("", FlagType.Shortcut) }, { "aspid_16_oneway", new Key("", FlagType.Shortcut) }, { "shellwoodTwigShortcut", new Key("", FlagType.Shortcut) }, { "shellwood13_BellWall", new Key("", FlagType.Shortcut) }, { "sethShortcut", new Key("", FlagType.Shortcut) }, { "openedDust05Gate", new Key("", FlagType.Shortcut) }, { "FixedDustBellBench", new Key("", FlagType.Shortcut) }, { "openedGateCoral_14", new Key("", FlagType.Shortcut) }, { "weave01_oneWay", new Key("", FlagType.Shortcut) }, { "weave05_oneWay", new Key("", FlagType.Shortcut) }, { "wokeLiftWeaver", new Key("", FlagType.Shortcut) }, { "slab_03_rubbishCleared", new Key("", FlagType.Shortcut) }, { "slab_cloak_gate_reopened", new Key("", FlagType.Shortcut) }, { "slab_05_gateOpen", new Key("", FlagType.Shortcut) }, { "slab_07_gateOpen", new Key("", FlagType.Shortcut) }, { "slab_17_openedGateRight", new Key("", FlagType.Shortcut) }, { "slab_cell_quiet_oneWayWall", new Key("", FlagType.Shortcut) }, { "slab_17_openedGateLeft", new Key("", FlagType.Shortcut) }, { "peak13_oneWay", new Key("", FlagType.Shortcut) }, { "peak05b_oneWay", new Key("", FlagType.Shortcut) }, { "peak05c_oneWay", new Key("", FlagType.Shortcut) }, { "peak06_oneWay", new Key("", FlagType.Shortcut) }, { "droppedFloorBreakerPlat", new Key("", FlagType.Shortcut) }, { "openedShellwoodShortcut", new Key("", FlagType.Shortcut) }, { "openedUnder_05", new Key("", FlagType.Shortcut) }, { "openedUnder_19", new Key("", FlagType.Shortcut) }, { "openedUnder_01b", new Key("", FlagType.Shortcut) }, { "song18Shortcut", new Key("", FlagType.Shortcut) }, { "libraryRoofShortcut", new Key("", FlagType.Shortcut) }, { "song_27_opened", new Key("", FlagType.Shortcut) }, { "song_11_oneway", new Key("", FlagType.Shortcut) }, { "wardBossHatchOpened", new Key("", FlagType.Shortcut) }, { "hang_10_oneWay", new Key("", FlagType.Shortcut) }, { "bankOpened", new Key("", FlagType.Shortcut) }, { "opened_cog_06_door", new Key("", FlagType.Shortcut) }, { "cog7_gateOpened", new Key("", FlagType.Shortcut) }, { "arborium_09_oneWay", new Key("", FlagType.Shortcut) }, { "arborium_08_oneWay", new Key("", FlagType.Shortcut) }, { "aspid_04_gate", new Key("", FlagType.Shortcut) }, { "openedCitadelSpaLeft", new Key("", FlagType.Shortcut) }, { "openedCitadelSpaRight", new Key("", FlagType.Shortcut) }, { "fixerBridgeConstructed", new Key("", FlagType.Quest) }, { "fixerStatueConstructed", new Key("", FlagType.Quest) }, { "boneBottomAddition_RagLine", new Key("", FlagType.Quest) }, { "soulSnareReady", new Key("", FlagType.Quest) }, { "PinstressPeakBattleAccepted", new Key("", FlagType.Quest) }, { "broodMotherEyeCollected", new Key("", FlagType.Quest) }, { "GotGourmandReward", new Key("", FlagType.Quest) }, { "enclaveAddition_PinRack", new Key("", FlagType.Quest) }, { "enclaveAddition_CloakLine", new Key("", FlagType.Quest) }, { "MushroomQuestFound1", new Key("", FlagType.Quest) }, { "MushroomQuestFound2", new Key("", FlagType.Quest) }, { "MushroomQuestFound3", new Key("", FlagType.Quest) }, { "MushroomQuestFound4", new Key("", FlagType.Quest) }, { "MushroomQuestFound5", new Key("", FlagType.Quest) }, { "MushroomQuestFound6", new Key("", FlagType.Quest) }, { "MushroomQuestFound7", new Key("", FlagType.Quest) }, { "GourmandGivenStew", new Key("", FlagType.QuestItem) }, { "GourmandGivenNectar", new Key("", FlagType.QuestItem) }, { "GourmandGivenEgg", new Key("", FlagType.QuestItem) }, { "GourmandGivenMeat", new Key("", FlagType.QuestItem) }, { "GourmandGivenCoral", new Key("", FlagType.QuestItem) }, { "fixerBridgeBroken", new Key("", FlagType.Shortcut) }, { "fixerBridgeBreaking", new Key("", FlagType.Shortcut) }, { "hasActivatedBellBench", new Key("", FlagType.Bench) }, { "activatedStepsUpperBellbench", new Key("Blasted Steps (Upper)", FlagType.Bench) } }; public static Dictionary IntKeys = new Dictionary { { "QuillState", new Key("", FlagType.Collectable) } }; public static HashSet FloatKeys = new HashSet(); public static Dictionary Collectables = new Dictionary { { "Crest Socket Unlocker", "Memory Locket" }, { "Simple Key", "Simple Key" } }; } }