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;
}
}
}
}