using System; using System.Collections.Generic; using UnityEngine; namespace InfiniteWorld { [CreateAssetMenu(menuName = "Infinite World/Chunk Template", fileName = "ChunkTemplate")] public class ChunkTemplate : ScriptableObject { [Min(4)] public int width = 16; [Min(4)] public int height = 16; [Header("Exits")] public bool exitTop = true; public bool exitRight = true; public bool exitBottom = true; public bool exitLeft = true; [SerializeField] private List cells = new List(); public int CellCount => width * height; public void EnsureCellData() { int target = Mathf.Max(1, width * height); while (cells.Count < target) { cells.Add(new ChunkCellData()); } while (cells.Count > target) { cells.RemoveAt(cells.Count - 1); } } public void Resize(int newWidth, int newHeight) { newWidth = Mathf.Max(4, newWidth); newHeight = Mathf.Max(4, newHeight); List oldCells = new List(cells); int oldWidth = width; int oldHeight = height; width = newWidth; height = newHeight; cells = new List(newWidth * newHeight); for (int i = 0; i < newWidth * newHeight; i++) { cells.Add(new ChunkCellData()); } for (int y = 0; y < Mathf.Min(oldHeight, newHeight); y++) { for (int x = 0; x < Mathf.Min(oldWidth, newWidth); x++) { int oldIndex = y * oldWidth + x; if (oldIndex < oldCells.Count) { cells[Index(x, y)] = oldCells[oldIndex]; } } } } public void Clear() { EnsureCellData(); for (int i = 0; i < cells.Count; i++) { cells[i] = new ChunkCellData(); } } public bool GetWall(int x, int y) { return IsInBounds(x, y) && cells[Index(x, y)].wall; } public bool GetEnvironment(int x, int y) { return IsInBounds(x, y) && cells[Index(x, y)].environment; } public void SetWall(int x, int y, bool value) { if (!IsInBounds(x, y)) { return; } EnsureCellData(); ChunkCellData data = cells[Index(x, y)]; data.wall = value; if (value) { data.environment = false; } cells[Index(x, y)] = data; } public void SetEnvironment(int x, int y, bool value) { if (!IsInBounds(x, y)) { return; } EnsureCellData(); ChunkCellData data = cells[Index(x, y)]; data.environment = value; if (value) { data.wall = false; } cells[Index(x, y)] = data; } public bool GetExit(ChunkExit exit) { return exit switch { ChunkExit.Top => exitTop, ChunkExit.Right => exitRight, ChunkExit.Bottom => exitBottom, ChunkExit.Left => exitLeft, _ => false }; } public int ExitCount() { int count = 0; count += exitTop ? 1 : 0; count += exitRight ? 1 : 0; count += exitBottom ? 1 : 0; count += exitLeft ? 1 : 0; return count; } public void ApplyBorderWallsFromExits(int openingWidth = 3) { EnsureCellData(); Clear(); for (int x = 0; x < width; x++) { SetWall(x, 0, true); SetWall(x, height - 1, true); } for (int y = 0; y < height; y++) { SetWall(0, y, true); SetWall(width - 1, y, true); } CarveExit(ChunkExit.Top, openingWidth); CarveExit(ChunkExit.Right, openingWidth); CarveExit(ChunkExit.Bottom, openingWidth); CarveExit(ChunkExit.Left, openingWidth); } public void CarveExit(ChunkExit exit, int openingWidth = 3) { if (!GetExit(exit)) { return; } int half = Mathf.Max(1, openingWidth) / 2; switch (exit) { case ChunkExit.Top: for (int x = width / 2 - half; x <= width / 2 + half; x++) { SetWall(x, height - 1, false); SetWall(x, height - 2, false); } break; case ChunkExit.Right: for (int y = height / 2 - half; y <= height / 2 + half; y++) { SetWall(width - 1, y, false); SetWall(width - 2, y, false); } break; case ChunkExit.Bottom: for (int x = width / 2 - half; x <= width / 2 + half; x++) { SetWall(x, 0, false); SetWall(x, 1, false); } break; case ChunkExit.Left: for (int y = height / 2 - half; y <= height / 2 + half; y++) { SetWall(0, y, false); SetWall(1, y, false); } break; } } private int Index(int x, int y) { return y * width + x; } private bool IsInBounds(int x, int y) { return x >= 0 && y >= 0 && x < width && y < height; } } public enum ChunkExit { Top, Right, Bottom, Left } [Serializable] public struct ChunkCellData { public bool wall; public bool environment; } }