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 LocationDescription(string name_arg, Requirement requirement_arg) { public readonly string name = name_arg; public readonly Requirement requirement = requirement_arg; } public class SceneDescription { public readonly List<LocationDescription> locations = []; public readonly Dictionary<int, Requirement> doors = []; public readonly Dictionary<int, Requirement> smokeWalls = []; public readonly Dictionary<int, Requirement> worldGrows = []; public readonly Dictionary<int, Requirement> lasers = []; } public class GameStateListeners { public readonly Dictionary<string, Requirement> locations = []; public readonly Dictionary<SceneItemReference, Requirement> doors = []; public readonly Dictionary<SceneItemReference, Requirement> smokeWalls = []; public readonly Dictionary<SceneItemReference, Requirement> worldGrows = []; public readonly Dictionary<SceneItemReference, Requirement> lasers = []; } public static class GameData { public static readonly Dictionary<string, SceneDescription> scenes = []; public static readonly Dictionary<string, GameStateListeners> listenersByScene = []; public static readonly Dictionary<SceneItemReference, GameStateListeners> listenersByButton = []; public static readonly Dictionary<SceneItemReference, GameStateListeners> listenersBySocket = []; public static readonly Dictionary<SceneItemReference, GameStateListeners> listenersByPad = []; public static readonly Dictionary<SceneItemReference, GameStateListeners> listenersByWaterwheel = []; public static readonly Dictionary<SceneItemReference, GameStateListeners> listenersBySphere = []; public static readonly Dictionary<string, GameStateListeners> listenersByItem = []; public static Requirement ParseRequirement(string scene, Dictionary<object, object> yamlReq) { List<Requirement> reqs = []; if (yamlReq.ContainsKey("entry")) { if (yamlReq["entry"] is string sceneName) { reqs.Add(new EntryRequirement(sceneName)); } else if (yamlReq["entry"] is List<string> 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<string> 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<object, object> details) { reqs.Add(new ButtonRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["button"] is List<object> 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<object, object> 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<object, object> details) { reqs.Add(new SocketRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["socket"] is List<object> 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<object, object> 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<object, object> details) { reqs.Add(new PadRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["pad"] is List<object> 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<object, object> 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<object, object> details) { reqs.Add(new WaterwheelRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["waterwheel"] is List<object> 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<object, object> 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<object, object> details) { reqs.Add(new SphereRequirement(new((string)details["scene"], int.Parse((string)details["index"])))); } else if (yamlReq["sphere"] is List<object> 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<object, object> 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<Requirement> unionReq = []; foreach (var reqObj in (List<object>)yamlReq["or"]) { unionReq.Add(ParseRequirement(scene, (Dictionary<object, object>)reqObj)); } reqs.Add(new OrRequirement(unionReq)); } if (yamlReq.ContainsKey("inverted")) { reqs.Add(new InvertedRequirement(ParseRequirement(scene, (Dictionary<object, object>)yamlReq["inverted"]))); } if (reqs.Count == 1) { return reqs[0]; } else { return new AndRequirement(reqs); } } private static GameStateListeners GetOrAddListeners<T>(Dictionary<T, GameStateListeners> 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<Dictionary<string, object>>(yamlText); foreach (var scenePair in yamlObject) { Plugin.Logger.LogInfo($"Scene: {scenePair.Key}"); SceneDescription sceneDescription = new(); var sceneDetails = (Dictionary<object, object>)scenePair.Value; if (sceneDetails.ContainsKey("locations")) { var sceneLocations = (Dictionary<object, object>)sceneDetails["locations"]; foreach (var locationPair in sceneLocations) { string locationName = (string)locationPair.Key; Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)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<object, object>)sceneDetails["doors"]; foreach (var doorPair in sceneDoors) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)doorPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)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<object, object>)sceneDetails["smoke"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)smokePair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)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<object, object>)sceneDetails["world_grow"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)worldPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)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<object, object>)sceneDetails["lasers"]) { SceneItemReference sir = new(scenePair.Key, int.Parse((string)laserPair.Key)); Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)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; } } } scenes[scenePair.Key] = sceneDescription; } } } }