From f717a556a909b831cb6965bcd2f8e057053e5161 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 22 May 2024 12:45:43 -0400 Subject: Start sending and receiving items --- AnodyneArchipelago/AnodyneArchipelago.csproj | 3 + AnodyneArchipelago/ArchipelagoManager.cs | 110 ++++++++++++++++++++++++++- AnodyneArchipelago/ArchipelagoTreasure.cs | 21 +++++ AnodyneArchipelago/Locations.cs | 20 +++++ AnodyneArchipelago/Plugin.cs | 51 ++++++++++++- 5 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 AnodyneArchipelago/ArchipelagoTreasure.cs create mode 100644 AnodyneArchipelago/Locations.cs (limited to 'AnodyneArchipelago') diff --git a/AnodyneArchipelago/AnodyneArchipelago.csproj b/AnodyneArchipelago/AnodyneArchipelago.csproj index 5ad5e82..abe6c23 100644 --- a/AnodyneArchipelago/AnodyneArchipelago.csproj +++ b/AnodyneArchipelago/AnodyneArchipelago.csproj @@ -32,5 +32,8 @@ ..\..\BepInEx\bin\NET.Framework\net462\BepInEx.NET.Common.dll + + ..\..\..\SteamLibrary\steamapps\common\Anodyne\Remake\FNA.dll + diff --git a/AnodyneArchipelago/ArchipelagoManager.cs b/AnodyneArchipelago/ArchipelagoManager.cs index 8c107a3..4ffa3af 100644 --- a/AnodyneArchipelago/ArchipelagoManager.cs +++ b/AnodyneArchipelago/ArchipelagoManager.cs @@ -1,12 +1,21 @@ -using Archipelago.MultiClient.Net; +using AnodyneSharp.Entities; +using AnodyneSharp.Registry; +using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.Enums; +using Archipelago.MultiClient.Net.Models; +using Archipelago.MultiClient.Net.Packets; using System; +using System.Collections.Generic; +using System.Linq; namespace AnodyneArchipelago { internal class ArchipelagoManager { private static ArchipelagoSession _session; + private static int _itemIndex = 0; + + private static readonly Queue _itemsToCollect = new(); public static void Connect(string url, string slotName, string password) { @@ -38,6 +47,105 @@ namespace AnodyneArchipelago return; } + + _itemIndex = 0; + _itemsToCollect.Clear(); + } + + public static void Disconnect() + { + if (_session == null) + { + return; + } + + _session.Socket.DisconnectAsync(); + _session = null; + } + + public static void SendLocation(string location) + { + if (_session == null) + { + Plugin.Instance.Log.LogError("Attempted to send location while disconnected"); + return; + } + + _session.Locations.CompleteLocationChecks(_session.Locations.GetLocationIdFromName("Anodyne", location)); + } + + public static void Update() + { + if (_session == null) + { + // We're not connected. + return; + } + + if (_session.Items.AllItemsReceived.Count > _itemIndex) + { + for (int i = _itemIndex; i < _session.Items.AllItemsReceived.Count; i++) + { + string itemKey = $"ArchipelagoItem-{i}"; + if (GlobalState.events.GetEvent(itemKey) != 0) + { + continue; + } + + GlobalState.events.SetEvent(itemKey, 1); + + NetworkItem item = _session.Items.AllItemsReceived[i]; + _itemsToCollect.Enqueue(item); + } + } + + if (_itemsToCollect.Count > 0) + { + NetworkItem item = _itemsToCollect.Dequeue(); + HandleItem(item); + } + } + + private static string GetMapNameForDungeon(string dungeon) + { + switch (dungeon) + { + case "Temple of the Seeing One": return "BEDROOM"; + case "Apartment": return "APARTMENT"; + case "Mountain Cavern": return "CROWD"; + case "Hotel": return "HOTEL"; + case "Red Grotto": return "REDCAVE"; + case "Circus": return "CIRCUS"; + default: return "STREET"; + } + } + + private static void HandleItem(NetworkItem item) + { + string itemName = _session.Items.GetItemName(item.Item); + + if (itemName.StartsWith("Small Key")) + { + string dungeonName = itemName.Substring(11); + dungeonName = dungeonName.Substring(0, dungeonName.Length - 1); + + string mapName = GetMapNameForDungeon(dungeonName); + GlobalState.inventory.AddMapKey(mapName, 1); + } else if (itemName == "Green Key") + { + GlobalState.inventory.BigKeyStatus[0] = true; + } + else if (itemName == "Blue Key") + { + GlobalState.inventory.BigKeyStatus[2] = true; + } + else if (itemName == "Red Key") + { + GlobalState.inventory.BigKeyStatus[1] = true; + } else if (itemName == "Jump Shoes") + { + GlobalState.inventory.CanJump = true; + } } } } diff --git a/AnodyneArchipelago/ArchipelagoTreasure.cs b/AnodyneArchipelago/ArchipelagoTreasure.cs new file mode 100644 index 0000000..6d000e8 --- /dev/null +++ b/AnodyneArchipelago/ArchipelagoTreasure.cs @@ -0,0 +1,21 @@ +using AnodyneSharp.Entities.Gadget.Treasures; +using Microsoft.Xna.Framework; + +namespace AnodyneArchipelago +{ + internal class ArchipelagoTreasure : Treasure + { + private string _location; + + public ArchipelagoTreasure(string location, Vector2 pos) : base("item_jump_shoes", pos, 0, -1) + { + _location = location; + } + + public override void GetTreasure() + { + base.GetTreasure(); + ArchipelagoManager.SendLocation(_location); + } + } +} diff --git a/AnodyneArchipelago/Locations.cs b/AnodyneArchipelago/Locations.cs new file mode 100644 index 0000000..46e2471 --- /dev/null +++ b/AnodyneArchipelago/Locations.cs @@ -0,0 +1,20 @@ + +using System; +using System.Collections.Generic; + +namespace AnodyneArchipelago +{ + internal class Locations + { + public static Dictionary LocationsByGuid = new() + { + {new Guid("40de36cf-9238-f8b0-7a57-c6c8ca465cc2"), "Temple of the Seeing One - Entrance Chest" }, + {new Guid("621c284f-cbd0-74c3-f51b-2a9fdde8d4d7"), "Temple of the Seeing One - Rock-Surrounded Chest" }, + {new Guid("401939a4-41ba-e07e-3ba2-dc22513dcc5c"), "Temple of the Seeing One - Dark Room Chest" }, + {new Guid("88d0a7b8-eeab-c45f-324e-f1c7885c41ce"), "Temple of the Seeing One - Shieldy Room Chest" }, + {new Guid("0bce5346-48a2-47f9-89cb-3f59d9d0b7d2"), "Temple of the Seeing One - Boss Chest" }, + {new Guid("d41f2750-e3c7-bbb4-d650-fafc190ebd32"), "Temple of the Seeing One - After Statue Left Chest" }, + {new Guid("3167924e-5d52-1cd6-d6f4-b8e0e328215b"), "Temple of the Seeing One - After Statue Right Chest" }, + }; + } +} diff --git a/AnodyneArchipelago/Plugin.cs b/AnodyneArchipelago/Plugin.cs index 7375503..f49ba35 100644 --- a/AnodyneArchipelago/Plugin.cs +++ b/AnodyneArchipelago/Plugin.cs @@ -1,7 +1,12 @@ -using BepInEx; +using AnodyneSharp; +using AnodyneSharp.Entities; +using AnodyneSharp.Entities.Gadget; +using AnodyneSharp.Entities.Gadget.Treasures; +using BepInEx; using BepInEx.NET.Common; using HarmonyLib; using HarmonyLib.Tools; +using System; using System.Reflection; namespace AnodyneArchipelago @@ -26,6 +31,15 @@ namespace AnodyneArchipelago } } + [HarmonyPatch(typeof(AnodyneGame), "Update")] + class GameUpdatePatch + { + static void Postfix() + { + ArchipelagoManager.Update(); + } + } + [HarmonyPatch(typeof(AnodyneSharp.States.PlayState), nameof(AnodyneSharp.States.PlayState.Create))] class PlayStateCreatePatch { @@ -36,4 +50,39 @@ namespace AnodyneArchipelago ArchipelagoManager.Connect("localhost:38281", "Anodyne", ""); } } + + [HarmonyPatch(typeof(TreasureChest), nameof(TreasureChest.PlayerInteraction))] + class ChestInteractPatch + { + static void Prefix(TreasureChest __instance) + { + Type chestType = typeof(TreasureChest); + FieldInfo presetField = chestType.GetField("_preset", BindingFlags.NonPublic | BindingFlags.Instance); + EntityPreset preset = presetField.GetValue(__instance) as EntityPreset; + Plugin.Instance.Log.LogInfo($"Touched chest: {preset.EntityID.ToString()}"); + } + } + + [HarmonyPatch(typeof(TreasureChest), "SetTreasure")] + class ChestSetTreasurePatch + { + static bool Prefix(TreasureChest __instance) + { + Type chestType = typeof(TreasureChest); + FieldInfo presetField = chestType.GetField("_preset", BindingFlags.NonPublic | BindingFlags.Instance); + EntityPreset preset = presetField.GetValue(__instance) as EntityPreset; + + if (Locations.LocationsByGuid.ContainsKey(preset.EntityID)) + { + BaseTreasure treasure = new ArchipelagoTreasure(Locations.LocationsByGuid[preset.EntityID], __instance.Position); + + FieldInfo treasureField = chestType.GetField("_treasure", BindingFlags.NonPublic | BindingFlags.Instance); + treasureField.SetValue(__instance, treasure); + + return false; + } + + return true; + } + } } -- cgit 1.4.1