using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using Sirenix.Utilities; using UnityEngine; using VOID.Generic.Dict; namespace VOID.Generic.World { public class ComplexArea : SerializedMonoBehaviour { [SerializeField] [ReadOnly] public List areas; [ReadOnly] [BoxGroup("variant", false)] public bool isVariant; [ReadOnly] [HideIf(nameof(isVariant))] [BoxGroup("variant", false)] public List variants = new(); [ReadOnly] [ShowIf(nameof(isVariant))] [BoxGroup("variant", false)] public ComplexArea original; #if UNITY_EDITOR private GameObject GetGameObjectForAreas() { const string areasGameObjectName = "_areas"; var areasGameObject = gameObject.transform.Find(areasGameObjectName)?.gameObject; if (areasGameObject) return areasGameObject; areasGameObject = new GameObject(areasGameObjectName); areasGameObject.transform.position = Vector3.zero; areasGameObject.transform.parent = gameObject.transform; areasGameObject.transform.SetAsFirstSibling(); return areasGameObject; } [Title("Generate")] [Button] private void GenerateAreas() { areas.ForEach(area => DestroyImmediate(area.gameObject)); areas.Clear(); GenerateMissingAreas(); } [Button] private void GenerateMissingAreas() { // Bounds thats contains everything in this ComplexArea Prefab var complexAreaBounds = new Bounds(); GetComponentsInChildren() .ForEach(collider => complexAreaBounds.Encapsulate(collider.bounds)); GetComponentsInChildren() .ForEach(meshRenderer => complexAreaBounds.Encapsulate(meshRenderer.bounds)); // To calculate position index we need only width (x) and length (z) var startIndex = new Vector2Int( Mathf.RoundToInt(complexAreaBounds.min.x / Area.Size), Mathf.RoundToInt(complexAreaBounds.min.z / Area.Size) ); var endIndex = new Vector2Int( Mathf.RoundToInt(complexAreaBounds.max.x / Area.Size), Mathf.RoundToInt(complexAreaBounds.max.z / Area.Size) ); var areasGameObject = GetGameObjectForAreas(); // Create areas for everything inside bounds for (var x = startIndex.x; x <= endIndex.x; x++) { for (var y = startIndex.y; y <= endIndex.y; y++) { if (areas.Any(area => area.position.x == x && area.position.y == y)) continue; var areaGameObject = new GameObject(); areaGameObject.AddComponent(); areaGameObject.transform.parent = areasGameObject.transform; areaGameObject.transform.position = new Vector3(x * Area.Size, 0, y * Area.Size); } } UpdateAll(); } [Button] private void GenerateVariants() { Editor.EditorWindow.World.ComplexAreaRotateVariantGenerator.GenerateRotatedVariants(this); } [Title("Update")] [Button(ButtonSizes.Large)] public void UpdateAll() { UpdateAreaList(); UpdatePositionIndexes(); UpdateNames(); UpdateConnections(); } [ResponsiveButtonGroup("Update")] [Button] private void UpdateAreaList() { areas = gameObject.GetComponentsInChildren().ToList(); areas.ForEach(area => area.complexArea = this); } [ResponsiveButtonGroup("Update")] [Button] private void UpdatePositionIndexes() { areas.ForEach(area => { var position = area.transform.position; area.position.x = Mathf.RoundToInt(position.x / Area.Size); area.position.y = Mathf.RoundToInt(position.z / Area.Size); }); } [ResponsiveButtonGroup("Update")] [Button] private void UpdateNames() { UpdatePositionIndexes(); areas.ForEach(area => area.name = $"{nameof(Area)} ({area.position.x},{area.position.y})"); } [ResponsiveButtonGroup("Update")] [Button] private void UpdateConnections() { UpdatePositionIndexes(); areas.ForEach(area => { var forwardArea = areas.Find(subArea => (area.position + Vector2Int.up).Equals(subArea.position)); var rightArea = areas.Find(subArea => (area.position + Vector2Int.right).Equals(subArea.position)); var backArea = areas.Find(subArea => (area.position + Vector2Int.down).Equals(subArea.position)); var leftArea = areas.Find(subArea => (area.position + Vector2Int.left).Equals(subArea.position)); area.forwardConnection.direction = Direction2D.Forward; area.rightConnection.direction = Direction2D.Right; area.backConnection.direction = Direction2D.Back; area.leftConnection.direction = Direction2D.Left; if (forwardArea) area.forwardConnection.type = AreaConnectionType.Inner; else if (area.forwardConnection.type == AreaConnectionType.Inner) area.forwardConnection.type = AreaConnectionType.None; if (rightArea) area.rightConnection.type = AreaConnectionType.Inner; else if (area.rightConnection.type == AreaConnectionType.Inner) area.rightConnection.type = AreaConnectionType.None; if (backArea) area.backConnection.type = AreaConnectionType.Inner; else if (area.backConnection.type == AreaConnectionType.Inner) area.backConnection.type = AreaConnectionType.None; if (leftArea) area.leftConnection.type = AreaConnectionType.Inner; else if (area.leftConnection.type == AreaConnectionType.Inner) area.leftConnection.type = AreaConnectionType.None; }); } [Title("Auto-Fix")] [Button(ButtonSizes.Large)] public void AutoFixAll() { AutoFixAreaTransforms(); AutoFixAreaHierarchy(); } [ResponsiveButtonGroup("AutoFix")] [Button] private void AutoFixAreaTransforms() { areas.ForEach(area => { var position = area.transform.localPosition; position.x = Mathf.RoundToInt(position.x / Area.Size) * (int)Area.Size; position.y = Mathf.RoundToInt(position.y / Area.Size) * (int)Area.Size; position.z = Mathf.RoundToInt(position.z / Area.Size) * (int)Area.Size; area.transform.localPosition = position; }); } [ResponsiveButtonGroup("AutoFix")] [Button] private void AutoFixAreaHierarchy() { var areasGameObject = GetGameObjectForAreas(); GetComponentsInChildren() .ForEach(area => area.gameObject.transform.parent = areasGameObject.transform); } #endif } }