using System; using System.Collections; using System.Collections.Generic; using TGS.PathFinding; using UnityEngine; namespace TGS { public partial class Cell { public int crossLevel = 0; public bool isVisited = false; public bool isInArea = false; public Vector3Int mathPointV3; public bool CanCross(int crossLevel) { return crossLevel > this.crossLevel && canCross; } public void ForceCanCross(bool value) { canCross = value; crossLevel = value ? 0 : Int32.MaxValue; } } public partial class TerrainGridSystem { public int CellGetCrossLevel(int cellIndex) { if (!ValidCellIndex(cellIndex)) return 0; return cells[cellIndex].crossLevel; } public void CellSetCrossLevel(int cellIndex, int crossLevel) { if (!ValidCellIndex(cellIndex)) return; cells[cellIndex].crossLevel = crossLevel; needRefreshRouteMatrix = true; } public void CellSetCrossLevel(List cellIndices, int crossLevel) { int count = cellIndices.Count; for (int k = 0; k < count; k++) { Cell cell = cells[cellIndices[k]]; cell.crossLevel = crossLevel; } needRefreshRouteMatrix = true; } public List FindPath(int cellIndexStart, int cellIndexEnd, int crossLevel, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) { float dummy; return FindPath(cellIndexStart, cellIndexEnd, crossLevel, out dummy, maxSearchCost, maxSteps, cellGroupMask, canCrossCheckType, ignoreCellCosts, includeInvisibleCells, minClearance, maxCellCrossCost); } public List FindPath(int cellIndexStart, int cellIndexEnd, int crossLevel, out float totalCost, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) { List results = new List(); FindPath(cellIndexStart, cellIndexEnd, results, crossLevel,out totalCost, maxSearchCost, maxSteps, cellGroupMask, canCrossCheckType, ignoreCellCosts, includeInvisibleCells, minClearance, maxCellCrossCost); return results; } public void FindPath(List result, int cellIndexStart, int cellIndexEnd, int crossLevel) { FindPath(cellIndexStart, cellIndexEnd, result, crossLevel, out _); } public int FindPath(int cellIndexStart, int cellIndexEnd, List cellIndices, int crossLevel, out float totalCost, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) { totalCost = 0; if (cellIndices == null) return 0; cellIndices.Clear(); if (cellIndexStart == cellIndexEnd || cellIndexStart < 0 || cellIndexEnd < 0 || cellIndexStart >= cells.Count || cellIndexEnd >= cells.Count) return 0; Cell startCell = cells[cellIndexStart]; Cell endCell = cells[cellIndexEnd]; if (startCell == null || endCell == null) return 0; bool startCellCanCross = startCell.canCross; bool endCellCanCross = endCell.canCross; int startCellCrossLevel = startCell.crossLevel; int endCellCrossLevel = endCell.crossLevel; if (canCrossCheckType != CanCrossCheckType.IgnoreCanCrossCheckOnAllCells) { switch (canCrossCheckType) { case CanCrossCheckType.ignoreCanCrossCheckOnStartAndEndCells: startCell.ForceCanCross(true); endCell.ForceCanCross(true); break; case CanCrossCheckType.ignoreCanCrossCheckOnStartCells: if (!endCell.CanCross(crossLevel)) return 0; startCell.ForceCanCross(true); break; case CanCrossCheckType.ignoreCanCrossCheckOnEndCells: if (!startCell.CanCross(crossLevel)) return 0; endCell.ForceCanCross(true); break; default: if (!startCell.CanCross(crossLevel) || !endCell.CanCross(crossLevel)) return 0; break; } } if (needRefreshRouteMatrix && minClearance > 1) { ComputeClearance(cellGroupMask, crossLevel); } ComputeRouteMatrix(); finder.Formula = _pathFindingHeuristicFormula; finder.MaxSteps = maxSteps > 0 ? maxSteps : _pathFindingMaxSteps; finder.Diagonals = _pathFindingUseDiagonals; finder.HeavyDiagonalsCost = _pathFindingHeavyDiagonalsCost; switch(_gridTopology) { case GridTopology.Irregular: finder.CellShape = CellType.Irregular; break; case GridTopology.Hexagonal: finder.CellShape = _pointyTopHexagons ? CellType.PointyTopHexagon : CellType.FlatTopHexagon; break; default: finder.CellShape = CellType.Box; break; } finder.MaxSearchCost = maxSearchCost > 0 ? maxSearchCost : _pathFindingMaxCost; finder.CellGroupMask = cellGroupMask; finder.IgnoreCanCrossCheck = canCrossCheckType == CanCrossCheckType.IgnoreCanCrossCheckOnAllCells || canCrossCheckType == CanCrossCheckType.ignoreCanCrossCheckOnAllCellsExceptStartAndEndCells; finder.IgnoreCellCost = ignoreCellCosts; finder.IncludeInvisibleCells = includeInvisibleCells; finder.MinClearance = minClearance; finder.MaxCellCrossCost = maxCellCrossCost; if (OnPathFindingCrossCell != null) { finder.OnCellCross = FindRoutePositionValidator; } else { finder.OnCellCross = null; } List route = finder.FindPath(this, startCell, endCell,crossLevel, out totalCost, _evenLayout); startCell.canCross = startCellCanCross; endCell.canCross = endCellCanCross; startCell.crossLevel = startCellCrossLevel; endCell.crossLevel = endCellCrossLevel; if (route != null) { int routeCount = route.Count; if (_gridTopology == GridTopology.Irregular) { for (int r = routeCount - 2; r >= 0; r--) { cellIndices.Add(route[r].PX); } } else { for (int r = routeCount - 2; r >= 0; r--) { int cellIndex = route[r].PY * _cellColumnCount + route[r].PX; cellIndices.Add(cellIndex); } } cellIndices.Add(cellIndexEnd); } else { return 0; // no route available } return cellIndices.Count; } /// /// Updates clearance data for each cell. Clearance is used with FindPath method (minClearance parameter) and it's used to specify the minimum width of a path /// public void ComputeClearance(int cellGroupMask, int objectCrossLevel) { if (clearanceComputed && clearanceCellGroupMask == cellGroupMask) return; clearanceComputed = true; clearanceCellGroupMask = cellGroupMask; int cellsCount = cells.Count; // clear clearance for (int k = 0; k < cellsCount; k++) { cells[k].clearance = 0; } int maxDim = Mathf.Max(rowCount, columnCount); // uses true clearance for (int j = rowCount - 1; j >= 0; j--) { for (int k = 0; k < columnCount; k++) { Cell cell = CellGetAtPosition(k, j); if (cell == null) continue; for (int maxClearance = 2; maxClearance < maxDim; maxClearance++) { bool blocked = false; int maxIter = maxClearance * maxClearance; for (int i = 1; i < maxIter; i++) { int nj = j - (i / maxClearance); int nk = k + (i % maxClearance); if (nj < 0 || nk >= columnCount) { blocked = true; break; } Cell neighbour = CellGetAtPosition(nk, nj); if (neighbour == null || (neighbour.group & cellGroupMask) == 0 || !neighbour.CanCross(objectCrossLevel)) { blocked = true; break; } } if (blocked) { cell.clearance = (byte)(maxClearance - 1); break; } } } } } public bool CellGetLineOfSight(int startCellIndex, int endCellIndex, int crossLevel, ref List cellIndices, ref List worldPositions, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false) { if (cellIndices == null) { cellIndices = new List(); } else { cellIndices.Clear(); } if (worldPositions == null) { worldPositions = new List(); } else { worldPositions.Clear(); } if (startCellIndex < 0 || startCellIndex >= cells.Count || endCellIndex < 0 || endCellIndex >= cells.Count) return false; Vector3 startPosition = CellGetPosition(startCellIndex); Vector3 endPosition; int vertexCount = exhaustiveCheck ? cells[endCellIndex].region.points.Count : 0; bool success = true; for (int p = 0; p <= vertexCount; p++) { if (p == 0) { endPosition = CellGetPosition(endCellIndex); } else { cellIndices.Clear(); worldPositions.Clear(); endPosition = CellGetVertexPosition(endCellIndex, p - 1); } int numSteps; switch (_gridTopology) { case GridTopology.Hexagonal: // Hexagon distance numSteps = CellGetHexagonDistance(startCellIndex, endCellIndex); lineResolution = Mathf.Max(2, lineResolution); numSteps *= lineResolution; break; case GridTopology.Box: numSteps = CellGetBoxDistance(startCellIndex, endCellIndex); lineResolution = Mathf.Max(2, lineResolution); numSteps *= lineResolution; if (numSteps % 2 == 0) numSteps++; break; default: float dist = Vector3.Distance(startPosition, endPosition); numSteps = Mathf.CeilToInt(dist * lineResolution); break; } Cell lastCell = cells[startCellIndex]; success = true; for (int k = 1; k <= numSteps; k++) { Vector3 position = Vector3.Lerp(startPosition, endPosition, (float)k / numSteps); Cell cell = k == numSteps ? cells[endCellIndex] : CellGetAtPosition(position, true); if (cell != null && cell != lastCell) { if (checkLastCell || cell != cells[endCellIndex]) { if (!cell.CanCross(crossLevel) && !ignoreCanCrossCheck) { success = false; break; } if ((cell.group & cellGroupMask) == 0) { success = false; break; } } // Check LOD blocks if (LOSIsBlocked(lastCell, cell) || LOSIsBlocked(cell, lastCell)) { success = false; break; } cellIndices.Add(cell.index); lastCell = cell; } worldPositions.Add(position); } if (success) { if (p == 0 && _gridTopology != GridTopology.Irregular) { return true; } break; } } if (success) { CellGetLine(startCellIndex, endCellIndex, ref cellIndices, ref worldPositions, lineResolution); } return success; } public bool CellGetLineOfSight(Vector3 startPosition, Vector3 endPosition, int crossLevel, ref List cellIndices, ref List worldPositions, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false) { cellIndices = null; Cell startCell = CellGetAtPosition(startPosition, true); Cell endCell = CellGetAtPosition(endPosition, true); if (startCell == null || endCell == null) { return false; } int cell1 = CellGetIndex(startCell); int cell2 = CellGetIndex(endCell); if (cell1 < 0 || cell2 < 0) return false; return CellGetLineOfSight(cell1, cell2, crossLevel, ref cellIndices, ref worldPositions, cellGroupMask, lineResolution, exhaustiveCheck, ignoreCanCrossCheck, checkLastCell); } public void CellTestLineOfSight(int startCellIndex, List targetCellIndices, int crossLevel, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false) { int count = targetCellIndices.Count; List dummyPositions = null; cellIteration++; for (int k = 0; k < count; k++) { int targetCellIndex = targetCellIndices[k]; if (cells[targetCellIndex].iteration != cellIteration) { if (CellGetLineOfSight(startCellIndex, targetCellIndex, crossLevel, ref tempListCells, ref dummyPositions, cellGroupMask, lineResolution, exhaustiveCheck, ignoreCanCrossCheck, checkLastCell)) { int lineCount = tempListCells.Count; for (int j = 0; j < lineCount; j++) { int index = tempListCells[j]; cells[index].iteration = cellIteration; } } else { targetCellIndices.RemoveAt(k); k--; count--; } } } } } }