using System.Collections.Generic; using System.IO; using YamlDotNet.Serialization; namespace ManifoldGardenArchipelago { public readonly struct SceneItemReference(string scene_arg, int index_arg) { public readonly string scene = scene_arg; public readonly int index = index_arg; public override string ToString() { return $"({scene}, {index})"; } } public readonly struct EntranceIdentifier(string region, string name) { public readonly string region = region; public readonly string name = name; public override string ToString() { return $"Entrance({region},{name})"; } } public readonly struct LocationDescription(string name_arg, Requirement requirement_arg) { public readonly string name = name_arg; public readonly Requirement requirement = requirement_arg; } public readonly struct PortalDescription(string scene, string name) { public readonly string scene = scene; public readonly string name = name; } public class SceneDescription { public readonly List locations = []; public readonly Dictionary doors = []; public readonly Dictionary smokeWalls = []; public readonly Dictionary worldGrows = []; public readonly Dictionary lasers = []; public readonly Dictionary portals = []; } public class GameStateListeners { public readonly Dictionary locations = []; public readonly Dictionary doors = []; public readonly Dictionary smokeWalls = []; public readonly Dictionary worldGrows = []; public readonly Dictionary lasers = []; } public static class GameData { public static readonly Dictionary scenes = []; public static readonly Dictionary listenersByScene = []; public static readonly Dictionary listenersByButton = []; public static readonly Dictionary listenersBySocket = []; public static readonly Dictionary listenersByPad = []; public static readonly Dictionary listenersByWaterwheel = []; public static readonly Dictionary listenersBySphere = []; public static readonly Dictionary listenersByItem = []; public static readonly Dictionary portal_by_entrance = []; public static Requirement ParseRequirement(string scene, Dictionary yamlReq) { List reqs = []; if (yamlReq.ContainsKey("entry")) { if (yamlReq["entry"] is string sceneName) { reqs.Add(new EntryRequirement(sceneName)); } else if (yamlReq["entry"] is List scenes) { foreach (var sceneName2 in scenes) { reqs.Add(new EntryRequirement(sceneName2)); } } } if (yamlReq.ContainsKey("item")) { if (yamlReq["item"] is string itemName) { reqs.Add(new ItemRequirement(itemName)); } else if (yamlReq["item"] is List items) { foreach (var item in items) { reqs.Add(new ItemRequirement(item)); } } } if (yamlReq.ContainsKey("button")) { if (yamlReq["button"] is string index) { reqs.Add(new ButtonRequirement(new(scene, int.Parse(index)))); } else if (yamlReq["button"] is Dictionary details) { reqs.Add(new ButtonRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["button"] is List buttons) { foreach (var button in buttons) { if (button is string index2) { reqs.Add(new ButtonRequirement(new(scene, int.Parse(index2)))); } else if (button is Dictionary details2) { reqs.Add(new ButtonRequirement(new((string)details2["scene"], int.Parse((string)details2["index"])))); } } } } if (yamlReq.ContainsKey("socket")) { if (yamlReq["socket"] is string index) { reqs.Add(new SocketRequirement(new(scene, int.Parse(index)))); } else if (yamlReq["socket"] is Dictionary details) { reqs.Add(new SocketRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["socket"] is List buttons) { foreach (var button in buttons) { if (button is string index2) { reqs.Add(new SocketRequirement(new(scene, int.Parse(index2)))); } else if (button is Dictionary details2) { reqs.Add(new SocketRequirement(new((string)details2["scene"], int.Parse((string)details2["index"])))); } } } } if (yamlReq.ContainsKey("pad")) { if (yamlReq["pad"] is string index) { reqs.Add(new PadRequirement(new(scene, int.Parse(index)))); } else if (yamlReq["pad"] is Dictionary details) { reqs.Add(new PadRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["pad"] is List buttons) { foreach (var button in buttons) { if (button is string index2) { reqs.Add(new PadRequirement(new(scene, int.Parse(index2)))); } else if (button is Dictionary details2) { reqs.Add(new PadRequirement(new((string)details2["scene"], int.Parse((string)details2["index"])))); } } } } if (yamlReq.ContainsKey("waterwheel")) { if (yamlReq["waterwheel"] is string index) { reqs.Add(new WaterwheelRequirement(new(scene, int.Parse(index)))); } else if (yamlReq["waterwheel"] is Dictionary details) { reqs.Add(new WaterwheelRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["waterwheel"] is List buttons) { foreach (var button in buttons) { if (button is string index2) { reqs.Add(new WaterwheelRequirement(new(scene, int.Parse(index2)))); } else if (button is Dictionary details2) { reqs.Add(new WaterwheelRequirement(new((string)details2["scene"], int.Parse((string)details2["index"])))); } } } } if (yamlReq.ContainsKey("sphere")) { if (yamlReq["sphere"] is string index) { reqs.Add(new SphereRequirement(new(scene, int.Parse(index)))); } else if (yamlReq["sphere"] is Dictionary details) { reqs.Add(new SphereRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["sphere"] is List buttons) { foreach (var button in buttons) { if (button is string index2) { reqs.Add(new SphereRequirement(new(scene, int.Parse(index2)))); } else if (button is Dictionary details2) { reqs.Add(new SphereRequirement(new((string)details2["scene"], int.Parse((string)details2["index"])))); } } } } if (yamlReq.ContainsKey("full_garden") && (string)yamlReq["full_garden"] == "true") { reqs.Add(new FullGardenRequirement()); } if (yamlReq.ContainsKey("or")) { List unionReq = []; foreach (var reqObj in (List)yamlReq["or"]) { unionReq.Add(ParseRequirement(scene, (Dictionary)reqObj)); } reqs.Add(new OrRequirement(unionReq)); } if (yamlReq.ContainsKey("inverted")) { reqs.Add(new InvertedRequirement(ParseRequirement(scene, (Dictionary)yamlReq["inverted"]))); } if (reqs.Count == 1) { return reqs[0]; } else { return new AndRequirement(reqs); } } private static GameStateListeners GetOrAddListeners(Dictionary kvp, T key) { if (!kvp.TryGetValue(key, out GameStateListeners listeners)) { listeners = new(); kvp[key] = listeners; } return listeners; } public static void Load() { Plugin.Logger.LogInfo(Path.Combine(BepInEx.Paths.BepInExRootPath, "plugins", "game_data.yaml")); var yamlText = File.OpenText(Path.Combine(BepInEx.Paths.BepInExRootPath, "plugins", "game_data.yaml")); var deserializer = new DeserializerBuilder().Build(); var yamlObject = deserializer.Deserialize>(yamlText); foreach (var scenePair in yamlObject) { Plugin.Logger.LogInfo($"Scene: {scenePair.Key}"); SceneDescription sceneDescription = new(); var sceneDetails = (Dictionary)scenePair.Value; if (sceneDetails.ContainsKey("locations")) { var sceneLocations = (Dictionary)sceneDetails["locations"]; foreach (var locationPair in sceneLocations) { string locationName = (string)locationPair.Key; Requirement req = ParseRequirement(scenePair.Key, (Dictionary)locationPair.Value); sceneDescription.locations.Add(new(locationName, req)); Plugin.Logger.LogInfo($"{locationName} requirements: {req}"); foreach (var reqScene in req.references.scenes) { GetOrAddListeners(listenersByScene, reqScene).locations[locationName] = req; } foreach (var button in req.references.buttons) { GetOrAddListeners(listenersByButton, button).locations[locationName] = req; } foreach (var socket in req.references.sockets) { GetOrAddListeners(listenersBySocket, socket).locations[locationName] = req; } foreach (var pad in req.references.pads) { GetOrAddListeners(listenersByPad, pad).locations[locationName] = req; } foreach (var waterwheel in req.references.waterwheels) { GetOrAddListeners(listenersByWaterwheel, waterwheel).locations[locationName] = req; } foreach (var sphere in req.references.spheres) { GetOrAddListeners(listenersBySphere, sphere).locations[locationName] = req; } foreach (var item in req.references.items) { GetOrAddListeners(listenersByItem, item).locations[locationName] = req; } } } if (sceneDetails.ContainsKey("doors")) { var sceneDoors = (Dictionary)sceneDetails["doors"]; foreach (var doorPair in sceneDoors) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)doorPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary)doorPair.Value); sceneDescription.doors[sir.index] = req; Plugin.Logger.LogInfo($"Door {sir} requirements: {req}"); foreach (var reqScene in req.references.scenes) { GetOrAddListeners(listenersByScene, reqScene).doors[sir] = req; } foreach (var button in req.references.buttons) { GetOrAddListeners(listenersByButton, button).doors[sir] = req; } foreach (var socket in req.references.sockets) { GetOrAddListeners(listenersBySocket, socket).doors[sir] = req; } foreach (var pad in req.references.pads) { GetOrAddListeners(listenersByPad, pad).doors[sir] = req; } foreach (var waterwheel in req.references.waterwheels) { GetOrAddListeners(listenersByWaterwheel, waterwheel).doors[sir] = req; } foreach (var sphere in req.references.spheres) { GetOrAddListeners(listenersBySphere, sphere).doors[sir] = req; } foreach (var item in req.references.items) { GetOrAddListeners(listenersByItem, item).doors[sir] = req; } } } if (sceneDetails.ContainsKey("smoke")) { foreach (var smokePair in (Dictionary)sceneDetails["smoke"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)smokePair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary)smokePair.Value); sceneDescription.smokeWalls[sir.index] = req; Plugin.Logger.LogInfo($"Smoke {sir} requirements: {req}"); foreach (var reqScene in req.references.scenes) { GetOrAddListeners(listenersByScene, reqScene).smokeWalls[sir] = req; } foreach (var button in req.references.buttons) { GetOrAddListeners(listenersByButton, button).smokeWalls[sir] = req; } foreach (var socket in req.references.sockets) { GetOrAddListeners(listenersBySocket, socket).smokeWalls[sir] = req; } foreach (var pad in req.references.pads) { GetOrAddListeners(listenersByPad, pad).smokeWalls[sir] = req; } foreach (var waterwheel in req.references.waterwheels) { GetOrAddListeners(listenersByWaterwheel, waterwheel).smokeWalls[sir] = req; } foreach (var sphere in req.references.spheres) { GetOrAddListeners(listenersBySphere, sphere).smokeWalls[sir] = req; } foreach (var item in req.references.items) { GetOrAddListeners(listenersByItem, item).smokeWalls[sir] = req; } } } if (sceneDetails.ContainsKey("world_grow")) { foreach (var worldPair in (Dictionary)sceneDetails["world_grow"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)worldPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary)worldPair.Value); sceneDescription.worldGrows[sir.index] = req; Plugin.Logger.LogInfo($"World {sir} requirements: {req}"); foreach (var reqScene in req.references.scenes) { GetOrAddListeners(listenersByScene, reqScene).worldGrows[sir] = req; } foreach (var button in req.references.buttons) { GetOrAddListeners(listenersByButton, button).worldGrows[sir] = req; } foreach (var socket in req.references.sockets) { GetOrAddListeners(listenersBySocket, socket).worldGrows[sir] = req; } foreach (var pad in req.references.pads) { GetOrAddListeners(listenersByPad, pad).worldGrows[sir] = req; } foreach (var waterwheel in req.references.waterwheels) { GetOrAddListeners(listenersByWaterwheel, waterwheel).worldGrows[sir] = req; } foreach (var sphere in req.references.spheres) { GetOrAddListeners(listenersBySphere, sphere).worldGrows[sir] = req; } foreach (var item in req.references.items) { GetOrAddListeners(listenersByItem, item).worldGrows[sir] = req; } } } if (sceneDetails.ContainsKey("lasers")) { foreach (var laserPair in (Dictionary)sceneDetails["lasers"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)laserPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary)laserPair.Value); sceneDescription.lasers[sir.index] = req; Plugin.Logger.LogInfo($"Laser {sir} requirements: {req}"); foreach (var reqScene in req.references.scenes) { GetOrAddListeners(listenersByScene, reqScene).lasers[sir] = req; } foreach (var button in req.references.buttons) { GetOrAddListeners(listenersByButton, button).lasers[sir] = req; } foreach (var socket in req.references.sockets) { GetOrAddListeners(listenersBySocket, socket).lasers[sir] = req; } foreach (var pad in req.references.pads) { GetOrAddListeners(listenersByPad, pad).lasers[sir] = req; } foreach (var waterwheel in req.references.waterwheels) { GetOrAddListeners(listenersByWaterwheel, waterwheel).lasers[sir] = req; } foreach (var sphere in req.references.spheres) { GetOrAddListeners(listenersBySphere, sphere).lasers[sir] = req; } foreach (var item in req.references.items) { GetOrAddListeners(listenersByItem, item).lasers[sir] = req; } } } if (sceneDetails.ContainsKey("portals")) { foreach (var portalPair in (Dictionary)sceneDetails["portals"]) { string portalName = (string)portalPair.Key; var portalData = (Dictionary)portalPair.Value; EntranceIdentifier entranceIdentifier = new((string)portalData["region"], (string)portalData["connection"]); sceneDescription.portals[portalName] = entranceIdentifier; portal_by_entrance[entranceIdentifier] = new(scenePair.Key, portalName); } } scenes[scenePair.Key] = sceneDescription; } } } }