public override void Deserialize(IntVector3 position, SerializedObject serializedObject)
        {
            this.Type = (WallType) serializedObject.Properties[0];
            this.rotation = (Rotation) serializedObject.Properties[1];

            SpawnWall(this.Type, position, this.rotation);
        }
        private void DrawChunkBounds(IntVector3 relPos)
        {
            var cubeSize = new Vector3(Chunk.CHUNK_SIZE, Chunk.CHUNK_SIZE, Chunk.CHUNK_SIZE);
            var chunkCenter = new Vector3((topLeftCorner.x + relPos.x) * Chunk.CHUNK_SIZE - Chunk.HALF_CHUNK_SIZE,
                                          (topLeftCorner.y + relPos.y) * Chunk.CHUNK_SIZE - Chunk.HALF_CHUNK_SIZE,
                                          (topLeftCorner.z + relPos.z) * Chunk.CHUNK_SIZE - Chunk.HALF_CHUNK_SIZE);

            Gizmos.DrawWireCube(chunkCenter, cubeSize);
        }
        public Chunk(IntVector3 position)
        {
            var indexedSize = (int) Mathf.Pow(CHUNK_SIZE, 3);
            var chunkPos = ClampAbsPosition(position);

            this.cells = new CellDefinition[indexedSize];
            this.dirtyCells = new List<CellDefinition>(128);
            this.Bounds = new CubeBounds(chunkPos, CHUNK_SIZE);
        }
        public static IntVector3 ClampAbsPosition(IntVector3 absPos)
        {
            var clampedPos = new IntVector3();

            clampedPos.x = (int)Mathf.Floor(absPos.x / Chunk.CHUNK_SIZE) * CHUNK_SIZE;
            clampedPos.y = (int)Mathf.Floor(absPos.y / Chunk.CHUNK_SIZE) * CHUNK_SIZE;
            clampedPos.z = (int)Mathf.Floor(absPos.z / Chunk.CHUNK_SIZE) * CHUNK_SIZE;

            return clampedPos;
        }
        public static void IntXYZ(IntVector3 from, IntVector3 to, int step, Action<int, int, int> callback)
        {
            for (int x = from.x; x <= to.x; x += step) {
                for (int y = from.y; y <= to.y; y += step) {
                    for (int z = from.z; z <= to.z; z += step) {

                        callback(x, y, z);
                    }
                }
            }
        }
        public CellDefinition CreateCellAt(IntVector3 position)
        {
            var targetChunk = activeRegion.GetChunkAt(position);

            if (targetChunk == null) {
                Logger.Warn("CreateCellAt", "Could not create cell at {0}, position out of bounds.", position);
                return null;
            }

            return targetChunk.CreateCell(position);
        }
        private void SpawnDoor(IntVector3 position, Rotation rotation)
        {
            if (this.goReference == null) {
                PreloadPrefabs();

                this.goReference = doorPrefab.Spawn();
                this.goReference.transform.position = position.ToVector3();

                SetRotation(rotation);
            }
        }
        public IntVector3 ConvertRelToAbsPosition(IntVector3 relPos)
        {
            var absolutePos = new IntVector3();

            absolutePos = this.Bounds.Position.ToIntVector3();

            absolutePos.x += relPos.x;
            absolutePos.y += relPos.y;
            absolutePos.z += relPos.z;

            return absolutePos;
        }
        public CellDefinition CreateCell(IntVector3 position)
        {
            var index = ConvertAbsPositionToIndex(position);

            if (cells[index] != null) {
                Logger.Warn("CreateCell", "Cell at {0} already exists.", position);
                return cells[index];
            }

            cells[index] = new CellDefinition(position, this);

            return cells[index];
        }
        private void SpawnFloor(IntVector3 position)
        {
            /* Spawn floor object and initialize if it does not exist yet */
            if (this.goReference == null) {
                PreloadPrefabs();

                this.goReference = floorPrefab.Spawn();

                this.goReference.transform.position = position.ToVector3();
                this.goReference.transform.eulerAngles = floorPrefab.transform.eulerAngles;
            }

            /* TODO: Add texture update logic */
        }
        public void ReloadChunks(IntVector3 centerChunk)
        {
            var regionManager = RegionManager.Instance;

            topLeftCorner = centerChunk - 1;

            LoopHelper.IntXYZ(topLeftCorner, centerChunk + 1, 1, (x, y, z) => {
                var position = new IntVector3(x, y, z);
                var relativePosition = position - topLeftCorner;
                var index = GetChunkIndexFromPosition(relativePosition);

                Logger.Info("ReloadChunks", "Loading chunk at {0}.", position);
                this.activeChunks[index] = regionManager.GetChunkAt(position);
                this.activeChunks[index].SetActive();
            });
        }
        public Chunk GetChunkAt(IntVector3 position)
        {
            if (!this.Bounds.Contains(position)) {
                Logger.Warn("GetChunkAt", "Cannot get chunk at {0}, does not belong inside this region.", position);
                return null;
            }

            var indexedPos = ConvertPositionToIndex(position);

            /* Initialize chunk if it does not exist yet */
            if (this.chunks[indexedPos] == null) {
                this.chunks[indexedPos] = new Chunk(Chunk.ClampAbsPosition(position));
            }

            return this.chunks[indexedPos];
        }
        private void OnDrawGizmos()
        {
            if (!DebugMode) {
                return;
            }

            Gizmos.color = Color.yellow;

            LoopHelper.IntXYZ(IntVector3.zero, new IntVector3(2), 1, (x, y, z) => {
                var relativePosition = new IntVector3(x, y, z);

                this.DrawChunkBounds(relativePosition);
            });

            /* Render center chunk in green */
            Gizmos.color = Color.green;
            this.DrawChunkBounds(IntVector3.one);
        }
        public override void Update(IntVector3 position)
        {
            var neighborCells = new bool[4];
            var cellCounter = 0;

            foreach (IntVector2 offset in CellRange.Values2DCardinal()) {
                var absPos2D = offset + position.ToIntVector2();
                var absPos = new IntVector3(absPos2D.x, position.y, absPos2D.z);
                var cell = RegionManager.Instance.GetCellAt(absPos);

                neighborCells[cellCounter] = cell.wall != null;

                cellCounter++;
            }

            var rotation = (neighborCells[0] && neighborCells[2] ? Rotation.EAST : Rotation.NORTH);

            SpawnDoor(position, rotation);
        }
        public void ApplyToGrid()
        {
            var upperBounds = new IntVector2(cells.GetLength(0) - 1, cells.GetLength(1) - 1);
            var topLeftCorner = new IntVector3(64, 64, 64);

            LoopHelper.IntXZ(IntVector2.zero, upperBounds, 1, (x, z) => {
                var absPosition = topLeftCorner + new IntVector3(x, 0, upperBounds.z - z);
                var cell = RegionManager.Instance.GetCellAt(absPosition);

                /* If cell was empty and we don't want anything there, return */
                if (this.cells[x, z] == CellType.EMPTY && cell == null) {
                    return;
                }

                /* Otherwise, create a new cell if we need to add something here */
                if (cell == null) {
                    cell = RegionManager.Instance.CreateCellAt(absPosition);
                }

                switch (this.cells[x, z]) {
                    case CellType.WALL:
                        cell.CreateWall();
                        break;

                    case CellType.FLOOR:
                        cell.CreateFloor();
                        break;

                    case CellType.DOOR:
                        cell.CreateObject(new DoorObject());
                        break;

                    /* This is a special case! If something was here previously,
                     * we need to destroy the whole cell.
                     */
                    case CellType.EMPTY:
                        cell.Destroy();
                        break;
                }
            });
        }
        private CellDefinition GetClosestWalkableCell(IntVector3 position)
        {
            var regionManager = RegionManager.Instance;
            var centerCell = regionManager.GetCellAt(position);

            /* If our target is a floor tile, return it */
            if (centerCell.floor != null) {
                return centerCell;
            }

            /* If not, search in a 9x9 grid around the target to find a walkable cell */
            foreach (var offset in CellRange.Values2D(false)) {
                var absPosGrid = offset + position.ToIntVector2();
                var cell = regionManager.GetCellAt(absPosGrid.ToIntVector3(position.y));

                if (cell != null && cell.floor != null) {
                    return cell;
                }
            }

            return null;
        }
 private int Distance(IntVector3 start, IntVector3 end)
 {
     return Mathf.Abs(end.x - start.x) + Mathf.Abs(end.y - start.y) + Mathf.Abs(end.z - start.z);
 }
 public CubeBounds(IntVector3 position, float width, float height, float length)
     : this(position.x, position.y, position.z, width, height, length)
 {
 }
 public CubeBounds(IntVector3 position, float size)
     : this(position, size, size, size)
 {
 }
 public override void Deserialize(IntVector3 position, SerializedObject serializedObject)
 {
     SpawnFloor(position);
 }
 public override void Update(IntVector3 position)
 {
     SpawnFloor(position);
 }
 /**
  * Prepares all UI to show the room editing dialog.
  * This should be the central entry point for all other scripts referencing the room builder.
  *
  * @param center - Center point of the editable region
  */
 public void ShowBuildDialog(IntVector3 center)
 {
     this.cells = new CellType[AreaSize, AreaSize];
     this.temporaryCells = null;
 }
 public bool Contains(IntVector3 position)
 {
     return Contains(position.x, position.y, position.z);
 }
        private void SpawnWall(WallType type, IntVector3 position, Rotation rotation)
        {
            var prefabs = registry.WallObjectHelper.Prefabs;

            if (!prefabs.ContainsKey(type)) {
                Logger.Warn("Could not load prefab for type {0}.", type);
                return;
            }

            var prefab = prefabs[type].Spawn();
            prefab.transform.position = position.ToVector3();

            this.goReference = prefab;
            this.Type = type;

            SetRotation(rotation);
            Logger.Info("Spawned wall {0} at {1} {2}.", type, position, this.goReference.transform.eulerAngles);
        }
 private int GetChunkIndexFromPosition(IntVector3 relativePosition)
 {
     return relativePosition.x + (relativePosition.y * 3) + (relativePosition.z * 9);
 }
        public override void Update(IntVector3 position)
        {
            var neighborCells = new short[9];
            var validMasks = new Dictionary<WallType, CellMask>(5);

            /* Properties used to refine valid tile masks */
            var containsEmptySpaces = false;
            var containsOtherWalls = false;
            var wallCount = 0;

            var cellCounter = -1;

            /* Grab surrounding block information */
            foreach (IntVector2 offset in CellRange.Values2D()) {
                var absPos2D = offset + position.ToIntVector2();
                var absPos = new IntVector3(absPos2D.x, position.y, absPos2D.z);
                CellDefinition cell;

                cellCounter++;

                cell = RegionManager.Instance.GetCellAt(absPos);
                containsEmptySpaces = containsEmptySpaces || CellDefinition.IsEmpty(cell);

                if (cell == null) {
                    neighborCells[cellCounter] = GameRegistry.EmptyObjectId;
                    continue;
                }

                if (cell.wall != null) {
                    if (!absPos.Equals(position)) {
                        containsOtherWalls = containsOtherWalls || cell.wall != null;
                    }

                    wallCount++;
                    neighborCells[cellCounter] = registry.GetObjectId<WallObject>();
                } else if (cell.floor != null) {
                    neighborCells[cellCounter] = registry.GetObjectId<FloorObject>();
                } else if (cell.containedObject.Is<DoorObject>()) {
                    neighborCells[cellCounter] = registry.GetObjectId<DoorObject>();
                } else {
                    neighborCells[cellCounter] = GameRegistry.EmptyObjectId;
                }
            }

            if (containsOtherWalls) {

                /* Default walls */
                AddValidMask(WallType.INNER_DEFAULT, validMasks);
                AddValidMask(WallType.INNER_END, validMasks);

                if (wallCount >= 3) {
                    AddValidMask(WallType.INNER_EDGE_INNER, validMasks);
                    AddValidMask(WallType.INNER_EDGE_T, validMasks);
                }

                if (wallCount >= 4) {
                    AddValidMask(WallType.INNER_EDGE_CROSS, validMasks);
                }

                if (containsEmptySpaces) {

                    AddValidMask(WallType.OUTER_DEFAULT, validMasks);

                    if (wallCount >= 3) {
                        AddValidMask(WallType.OUTER_EDGE_OUTER, validMasks);
                        AddValidMask(WallType.OUTER_EDGE_INNER, validMasks);
                        AddValidMask(WallType.OUTER_EDGE_T_INNER, validMasks);
                        AddValidMask(WallType.OUTER_EDGE_T_OUTER, validMasks);
                    }
                }
            }

            var calculatedType = WallType.OUTER_DEFAULT;
            var calculatedRotation = Rotation.NORTH;
            var foundMatch = false;

            foreach (var wallType in validMasks.Keys) {

                if (validMasks[wallType].Match(neighborCells, out calculatedRotation)) {
                    calculatedType = wallType;
                    foundMatch = true;
                    break;
                }
            }

            if (!foundMatch) {
                Logger.Warn("Could not find matching type for wall at {0}", position);
            }

            /* Spawn the new wall object and set transform */
            if (this.Type != calculatedType) {
                SpawnWall(calculatedType, position, calculatedRotation);
            } else if (this.rotation != calculatedRotation) {
                SetRotation(calculatedRotation);
            }
        }
        public static CellDefinition Deserialize(IntVector3 position, Chunk parentChunk, SerializedCellDefinition serializedCell)
        {
            var cell = new CellDefinition(position, parentChunk);

            if (serializedCell.wall != null) {
                cell.wall = new WallObject();
                cell.wall.Deserialize(position, serializedCell.wall);
            }

            if (serializedCell.floor != null) {
                cell.floor = new FloorObject();
                cell.floor.Deserialize(position, serializedCell.floor);
            }

            if (serializedCell.containedObject != null) {
                var type = GameRegistry.Instance.GetObjectType(serializedCell.containedObject.Id);

                cell.containedObject = (BaseObject) Activator.CreateInstance(type);
                cell.containedObject.Deserialize(position, serializedCell.containedObject);
            }

            parentChunk.SetCellAt(position, cell);

            return cell;
        }
        public IEnumerator FindPath(IntVector3 start, IntVector3 end, Action<bool> callback)
        {
            currentNode = null;
            this.endNode = null;

            this.start = start;
            this.end = end;

            this.openNodes.Clear();
            this.closedNodes.Clear();
            this.finalNodes.Clear();

            /* Create start node and add to open list */
            var startNode = new PathNode() {
                Position = start,
                G = 0,
                F = Manhattan(start, end)
            };

            this.openNodes.Add(startNode);

            while (this.openNodes.Count > 0) {

                /* Sort open nodes by lowest f(G, H) = G + H */
                this.openNodes = this.openNodes.OrderBy( node => node.F ).ToList();

                currentNode = this.openNodes.PopAt(0);
                this.closedNodes.Add(currentNode);

                if (currentNode.Position.Equals(end)) {
                    this.endNode = currentNode;
                    break;
                }

                while (!ContinueSteps && PerformSteps) {
                    yield return null;
                }

                ContinueSteps = false;

                /* Visit all neighbors of our current node */
                foreach(var offset in CellRange.Values2D(false)) {
                    var absPos = currentNode.Position + offset.ToIntVector3(0);

                    /* Continue if we already visited that neighbor */
                    var isClosedNode = this.closedNodes.Find( node => node.Position.Equals(absPos) ) != null;
                    var isWalkable = CellDefinition.IsWalkable(regionManager.GetCellAt(absPos));

                    if (isClosedNode || !isWalkable) {
                        continue;
                    }

                    var tentativeG = currentNode.G + Distance(currentNode.Position, absPos);
                    var openNeighbor = this.openNodes.Find( node => node.Position.Equals(absPos) );

                    if (openNeighbor == null || tentativeG < openNeighbor.G) {
                        if (openNeighbor == null) {
                            openNeighbor = new PathNode();

                            this.openNodes.Add(openNeighbor);
                        }

                        openNeighbor.ParentNode = currentNode;
                        openNeighbor.Position = absPos;
                        openNeighbor.G = tentativeG;
                        openNeighbor.F = openNeighbor.G + Manhattan(absPos, end);
                    }
                }
            }

            callback(this.endNode != null);
        }
 public CellDefinition(IntVector3 position, Chunk parentChunk)
 {
     this.Position = position;
     this.ParentChunk = parentChunk;
 }
 private int Manhattan(IntVector3 start, IntVector3 end)
 {
     return Mathf.Abs(start.x - end.x) + Mathf.Abs(start.y - end.y) + Mathf.Abs(start.z - end.z);
 }