using System.Collections.Generic; using System.Linq; using Godot; namespace AceFieldNewHorizon.Scripts.System; public enum RotationDirection { Up, // 0 degrees Right, // 90 degrees Down, // 180 degrees Left // 270 degrees } public record BuildingData( PackedScene Scene, Dictionary Cost, int Durability, GridLayer Layer, float BuildTime, Vector2I Size = default, RotationDirection[] AllowedRotations = null ) { public Vector2I Size { get; } = Size == default ? Vector2I.One : Size; public RotationDirection[] AllowedRotations { get; } = AllowedRotations ?? [RotationDirection.Up, RotationDirection.Right, RotationDirection.Down, RotationDirection.Left ]; public bool IsRotationAllowed(float degrees) { var direction = (RotationDirection)((Mathf.RoundToInt(degrees / 90f) % 4 + 4) % 4); return AllowedRotations.Contains(direction); } public Vector2I GetRotatedSize(float degrees) { return (Mathf.RoundToInt(degrees / 90f) % 2) == 0 ? Size : new Vector2I(Size.Y, Size.X); } } public partial class BuildingRegistry : Node { private Dictionary _registry = new(); [Export] public string JsonPath { get; set; } = "res://Data/Buildings.json"; public override void _Ready() { LoadFromJson(JsonPath); } public void LoadFromJson(string path) { var file = FileAccess.Open(path, FileAccess.ModeFlags.Read); if (file == null) { GD.PrintErr($"[BuildingRegistry] Failed to open {path}"); return; } var text = file.GetAsText(); file.Close(); var json = new Json(); var error = json.Parse(text); if (error != Error.Ok) { GD.PrintErr($"[BuildingRegistry] Failed to parse JSON: {json.GetErrorMessage()}"); return; } var dict = (Godot.Collections.Dictionary)json.Data; foreach (string key in dict.Keys) { // Each entry is a Dictionary with keys: "scene", "cost", "durability", "buildTime", "size", "allowedRotations" var buildingDict = dict[key].AsGodotDictionary(); // Parse scene var scenePath = buildingDict.ContainsKey("scene") ? buildingDict["scene"].AsString() : null; if (string.IsNullOrEmpty(scenePath)) { GD.PrintErr($"[BuildingRegistry] No scene path for '{key}'"); continue; } var scene = GD.Load(scenePath); if (scene == null) { GD.PrintErr($"[BuildingRegistry] Failed to load scene for '{key}' at {scenePath}"); continue; } // Parse cost Dictionary cost = new(); if (buildingDict.TryGetValue("cost", out var value)) { var costDict = value.AsGodotDictionary(); foreach (string mat in costDict.Keys) { int val; var obj = costDict[mat]; if (obj.VariantType == Variant.Type.PackedInt64Array) val = (int)obj.AsInt64(); else if (obj.VariantType == Variant.Type.PackedInt32Array) val = obj.AsInt32(); else int.TryParse(obj.ToString(), out val); cost[mat] = val; } } // Parse durability var durability = 0; if (buildingDict.TryGetValue("durability", out var dObj)) { if (dObj.VariantType == Variant.Type.PackedInt64Array) durability = (int)dObj.AsInt64(); else if (dObj.VariantType == Variant.Type.PackedInt32Array) durability = dObj.AsInt32(); else int.TryParse(dObj.ToString(), out durability); } // Parse buildTime var buildTime = 0f; if (buildingDict.TryGetValue("buildTime", out var bObj)) { switch (bObj.VariantType) { case Variant.Type.PackedFloat32Array or Variant.Type.PackedFloat64Array: buildTime = (float)bObj.AsDouble(); break; case Variant.Type.PackedInt64Array: buildTime = bObj.AsInt64(); break; case Variant.Type.PackedInt32Array: buildTime = bObj.AsInt32(); break; default: float.TryParse(bObj.ToString(), out buildTime); break; } } // Parse size var size = Vector2I.One; if (buildingDict.TryGetValue("size", out var sObj)) { var sizeArray = sObj.AsGodotArray(); if (sizeArray.Count == 2) { size = new Vector2I((int)sizeArray[0].AsInt64(), (int)sizeArray[1].AsInt64()); } } // Parse allowedRotations RotationDirection[] allowedRotations = null; if (buildingDict.TryGetValue("allowedRotations", out var arObj)) { var arArray = arObj.AsGodotArray(); allowedRotations = new RotationDirection[arArray.Count]; for (var i = 0; i < arArray.Count; i++) { allowedRotations[i] = (RotationDirection)arArray[i].AsInt32(); } } var layer = GridLayer.Building; if (buildingDict.TryGetValue("layer", out var lObj)) { layer = (GridLayer)lObj.AsInt32(); } var buildingData = new BuildingData(scene, cost, durability, layer, buildTime, size, allowedRotations); _registry[key] = buildingData; GD.Print($"[BuildingRegistry] Loaded building '{key}' from {scenePath}"); } GD.Print($"[BuildingRegistry] Loaded {_registry.Count} buildings"); } public BuildingData GetBuilding(string id) { _registry.TryGetValue(id, out var data); return data; } }