2023-08-22 19:08:45 +08:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using TGS.PathFinding ;
using UnityEngine ;
namespace TGS
{
public partial class Cell
{
public int crossLevel = 0 ;
2024-12-27 15:53:23 +08:00
public bool isVisited = false ;
2025-01-03 13:18:58 +08:00
public bool isInArea = false ;
2023-08-22 19:08:45 +08:00
2024-12-31 12:50:42 +08:00
public Vector3Int mathPointV3 ;
2023-08-22 19:08:45 +08:00
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 < int > 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 < int > 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 < int > 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 < int > results = new List < int > ( ) ;
FindPath ( cellIndexStart , cellIndexEnd , results , crossLevel , out totalCost , maxSearchCost , maxSteps , cellGroupMask , canCrossCheckType , ignoreCellCosts , includeInvisibleCells , minClearance , maxCellCrossCost ) ;
return results ;
}
2025-01-09 13:36:00 +08:00
public void FindPath ( List < int > result ,
int cellIndexStart ,
int cellIndexEnd ,
int crossLevel )
{
FindPath ( cellIndexStart , cellIndexEnd , result , crossLevel , out _ ) ;
}
2023-08-22 19:08:45 +08:00
public int FindPath ( int cellIndexStart , int cellIndexEnd , List < int > 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 < PathFinderNode > 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 ;
}
/// <summary>
/// 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
/// </summary>
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 < int > cellIndices , ref List < Vector3 > worldPositions , int cellGroupMask = - 1 , int lineResolution = 2 , bool exhaustiveCheck = false , bool ignoreCanCrossCheck = false , bool checkLastCell = false )
{
if ( cellIndices = = null )
{
cellIndices = new List < int > ( ) ;
}
else
{
cellIndices . Clear ( ) ;
}
if ( worldPositions = = null )
{
worldPositions = new List < Vector3 > ( ) ;
}
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 < int > cellIndices , ref List < Vector3 > 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 < int > targetCellIndices , int crossLevel , int cellGroupMask = - 1 , int lineResolution = 2 , bool exhaustiveCheck = false , bool ignoreCanCrossCheck = false , bool checkLastCell = false )
{
int count = targetCellIndices . Count ;
List < Vector3 > 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 - - ;
}
}
}
}
}
}