Changes the general behavior of non-pathfinding passability classes, in order to make the handling of foundation obstructions less difficult. This will allow the AI to be fixed, as reported in #3295.

Also some cleanup and comments updates.

Refs #3295.

This was SVN commit r16784.
This commit is contained in:
Nicolas Auvray 2015-06-17 20:19:53 +00:00
parent 4b8f0c9fb9
commit 1709353e2c
12 changed files with 186 additions and 126 deletions

View File

@ -8,6 +8,7 @@ element Pathfinder {
element MaxSameTurnMoves { xsd:nonNegativeInteger } & element MaxSameTurnMoves { xsd:nonNegativeInteger } &
element PassabilityClasses { element PassabilityClasses {
element * { element * {
element Obstructions { xsd:string } &
element MinWaterDepth { xsd:decimal }? & # TODO: fixed type element MinWaterDepth { xsd:decimal }? & # TODO: fixed type
element MaxWaterDepth { xsd:decimal }? & element MaxWaterDepth { xsd:decimal }? &
element MaxTerrainSlope { xsd:decimal }? & element MaxTerrainSlope { xsd:decimal }? &

View File

@ -15,6 +15,9 @@
<element> <element>
<anyName/> <anyName/>
<interleave> <interleave>
<element name="Obstructions">
<data type="string"/>
</element>
<optional> <optional>
<element name="MinWaterDepth"> <element name="MinWaterDepth">
<data type="decimal"/> <data type="decimal"/>

View File

@ -6,64 +6,64 @@
<PassabilityClasses> <PassabilityClasses>
<unrestricted/>
<!-- Unit pathfinding classes: --> <!-- Unit pathfinding classes: -->
<!-- 'default-terrain-only' is used for wall building placement
and by the AI pathfinder; it must be kept in sync with 'default'
-->
<default> <default>
<Obstructions>pathfinding</Obstructions>
<MaxWaterDepth>2</MaxWaterDepth> <MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope> <MaxTerrainSlope>1.0</MaxTerrainSlope>
<Clearance>1.0</Clearance> <Clearance>1.0</Clearance>
</default> </default>
<default-no-clearance>
<MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope>
<Clearance>0.0</Clearance>
</default-no-clearance>
<default-terrain-only>
<MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope>
</default-terrain-only>
<siege-large> <siege-large>
<Obstructions>pathfinding</Obstructions>
<MaxWaterDepth>2</MaxWaterDepth> <MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope> <MaxTerrainSlope>1.0</MaxTerrainSlope>
<Clearance>4.0</Clearance> <Clearance>4.0</Clearance>
</siege-large> </siege-large>
<ship> <ship>
<Obstructions>pathfinding</Obstructions>
<MinWaterDepth>1</MinWaterDepth> <MinWaterDepth>1</MinWaterDepth>
<Clearance>12.0</Clearance> <Clearance>12.0</Clearance>
</ship> </ship>
<ship-small> <ship-small>
<Obstructions>pathfinding</Obstructions>
<MinWaterDepth>1</MinWaterDepth> <MinWaterDepth>1</MinWaterDepth>
<Clearance>4.0</Clearance> <Clearance>4.0</Clearance>
</ship-small> </ship-small>
<!-- Building construction classes: <!--
Building construction classes:
* Land is used for most buildings, which must be away * Land is used for most buildings, which must be away
from water and not on cliffs or mountains. from water and not on cliffs or mountains.
* Shore is used for docks, which must be near water and * Shore is used for docks, which must be near water and
land, yet shallow enough for builders to approach. land, yet shallow enough for builders to approach.
(These should not use <Clearance>, because the foundation placement
checker already does precise obstruction-shape-based checking.)
--> -->
<building-land> <building-land>
<Obstructions>foundation</Obstructions>
<MaxWaterDepth>0</MaxWaterDepth> <MaxWaterDepth>0</MaxWaterDepth>
<MinShoreDistance>4.0</MinShoreDistance> <MinShoreDistance>4.0</MinShoreDistance>
<MaxTerrainSlope>1.0</MaxTerrainSlope> <MaxTerrainSlope>1.0</MaxTerrainSlope>
</building-land> </building-land>
<building-shore> <building-shore>
<Obstructions>foundation</Obstructions>
<MaxShoreDistance>8.0</MaxShoreDistance> <MaxShoreDistance>8.0</MaxShoreDistance>
<MaxTerrainSlope>1.25</MaxTerrainSlope> <MaxTerrainSlope>1.25</MaxTerrainSlope>
</building-shore> </building-shore>
<!-- Territory growth influences: --> <!--
<territory> Unrestricted: only off-world limits.
Default-terrain-only: used by the AI, for wall-building
placement and for territory influence growth.
It must be kept in sync with "default".
-->
<unrestricted>
<Obstructions>none</Obstructions>
</unrestricted>
<default-terrain-only>
<Obstructions>none</Obstructions>
<MaxWaterDepth>2</MaxWaterDepth> <MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope> <MaxTerrainSlope>1.0</MaxTerrainSlope>
</territory> </default-terrain-only>
</PassabilityClasses> </PassabilityClasses>
</Pathfinder> </Pathfinder>

View File

@ -1052,7 +1052,7 @@ public:
LoadPathfinderClasses(state); LoadPathfinderClasses(state);
std::map<std::string, pass_class_t> passClassMasks; std::map<std::string, pass_class_t> passClassMasks;
if (cmpPathfinder) if (cmpPathfinder)
passClassMasks = cmpPathfinder->GetPassabilityClasses(); passClassMasks = cmpPathfinder->GetPathfindingPassabilityClasses();
m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state), m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state),
*passabilityMap, dirtinessInformations, *passabilityMap, dirtinessInformations,

View File

@ -462,7 +462,7 @@ public:
virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out); virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out);
virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out); virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out);
virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate); virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, bool fullUpdate);
virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares); virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares);
virtual void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter); virtual void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter);
@ -635,6 +635,8 @@ private:
{ {
return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1); return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1);
} }
void RasterizeHelper(Grid<u16>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, u16 appliedMask, entity_pos_t clearance = fixed::Zero());
}; };
REGISTER_COMPONENT_TYPE(ObstructionManager) REGISTER_COMPONENT_TYPE(ObstructionManager)
@ -810,31 +812,52 @@ bool CCmpObstructionManager::TestUnitShape(const IObstructionTestFilter& filter,
return false; // didn't collide, if we got this far return false; // didn't collide, if we got this far
} }
void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate) void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, bool fullUpdate)
{ {
PROFILE3("Rasterize"); PROFILE3("Rasterize");
// The update informations are only updated when pathfinding/foundation blocking shapes are modified.
ENSURE(!(requireMask & ~(FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION)));
// Cells are only marked as blocked if the whole cell is strictly inside the shape. // Cells are only marked as blocked if the whole cell is strictly inside the shape.
// (That ensures the shape's geometric border is always reachable.) // (That ensures the shape's geometric border is always reachable.)
// Add obstructions onto the grid, for any class with (possibly zero) clearance // Pass classes will get shapes rasterized on them depending on their Obstruction value.
std::map<entity_pos_t, u16> combinedMasks; // Classes with another value than "pathfinding" should not use Clearance.
std::map<entity_pos_t, u16> pathfindingMasks;
u16 foundationMask = 0;
for (const PathfinderPassability& passability : passClasses) for (const PathfinderPassability& passability : passClasses)
{ {
if (!passability.m_HasClearance) switch (passability.m_Obstructions)
continue; {
case PathfinderPassability::PATHFINDING:
auto it = combinedMasks.find(passability.m_Clearance); {
if (it == combinedMasks.end()) auto it = pathfindingMasks.find(passability.m_Clearance);
combinedMasks[passability.m_Clearance] = passability.m_Mask; if (it == pathfindingMasks.end())
pathfindingMasks[passability.m_Clearance] = passability.m_Mask;
else else
it->second |= passability.m_Mask; it->second |= passability.m_Mask;
break;
}
case PathfinderPassability::FOUNDATION:
foundationMask |= passability.m_Mask;
break;
default:
continue;
}
} }
for (auto& maskPair : combinedMasks) // FLAG_BLOCK_PATHFINDING and FLAG_BLOCK_FOUNDATION are the only flags taken into account by MakeDirty* functions,
// so they should be the only ones rasterized using with the help of m_Dirty*Shapes vectors.
for (auto& maskPair : pathfindingMasks)
RasterizeHelper(grid, FLAG_BLOCK_PATHFINDING, fullUpdate, maskPair.second, maskPair.first);
RasterizeHelper(grid, FLAG_BLOCK_FOUNDATION, fullUpdate, foundationMask);
m_DirtyStaticShapes.clear();
m_DirtyUnitShapes.clear();
}
void CCmpObstructionManager::RasterizeHelper(Grid<u16>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, u16 appliedMask, entity_pos_t clearance)
{ {
for (auto& pair : m_StaticShapes) for (auto& pair : m_StaticShapes)
{ {
@ -848,7 +871,7 @@ void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<Pathfi
// TODO: it might be nice to rasterize with rounded corners for large 'expand' values. // TODO: it might be nice to rasterize with rounded corners for large 'expand' values.
ObstructionSquare square = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh }; ObstructionSquare square = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
SimRasterize::Spans spans; SimRasterize::Spans spans;
SimRasterize::RasterizeRectWithClearance(spans, square, maskPair.first, Pathfinding::NAVCELL_SIZE); SimRasterize::RasterizeRectWithClearance(spans, square, clearance, Pathfinding::NAVCELL_SIZE);
for (SimRasterize::Span& span : spans) for (SimRasterize::Span& span : spans)
{ {
i16 j = span.j; i16 j = span.j;
@ -857,7 +880,7 @@ void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<Pathfi
i16 i0 = std::max(span.i0, (i16)0); i16 i0 = std::max(span.i0, (i16)0);
i16 i1 = std::min(span.i1, (i16)grid.m_W); i16 i1 = std::min(span.i1, (i16)grid.m_W);
for (i16 i = i0; i < i1; ++i) for (i16 i = i0; i < i1; ++i)
grid.set(i, j, grid.get(i, j) | maskPair.second); grid.set(i, j, grid.get(i, j) | appliedMask);
} }
} }
} }
@ -872,21 +895,17 @@ void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<Pathfi
if (!(pair.second.flags & requireMask)) if (!(pair.second.flags & requireMask))
continue; continue;
entity_pos_t r = pair.second.r + maskPair.first; entity_pos_t r = pair.second.r + clearance;
u16 i0, j0, i1, j1; u16 i0, j0, i1, j1;
Pathfinding::NearestNavcell(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H); Pathfinding::NearestNavcell(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H);
Pathfinding::NearestNavcell(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H); Pathfinding::NearestNavcell(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H);
for (u16 j = j0+1; j < j1; ++j) for (u16 j = j0+1; j < j1; ++j)
for (u16 i = i0+1; i < i1; ++i) for (u16 i = i0+1; i < i1; ++i)
grid.set(i, j, grid.get(i, j) | maskPair.second); grid.set(i, j, grid.get(i, j) | appliedMask);
} }
} }
m_DirtyStaticShapes.clear();
m_DirtyUnitShapes.clear();
}
void CCmpObstructionManager::GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) void CCmpObstructionManager::GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares)
{ {
PROFILE("GetObstructionsInRange"); PROFILE("GetObstructionsInRange");

View File

@ -200,6 +200,16 @@ std::map<std::string, pass_class_t> CCmpPathfinder::GetPassabilityClasses()
return m_PassClassMasks; return m_PassClassMasks;
} }
std::map<std::string, pass_class_t> CCmpPathfinder::GetPathfindingPassabilityClasses()
{
std::map<std::string, pass_class_t> pathfindingClasses;
for (auto& pair : m_PassClassMasks)
if (GetPassabilityFromMask(pair.second)->m_Obstructions == PathfinderPassability::PATHFINDING)
pathfindingClasses[pair.first] = pair.second;
return pathfindingClasses;
}
const PathfinderPassability* CCmpPathfinder::GetPassabilityFromMask(pass_class_t passClass) const const PathfinderPassability* CCmpPathfinder::GetPassabilityFromMask(pass_class_t passClass) const
{ {
for (const PathfinderPassability& passability : m_PassClasses) for (const PathfinderPassability& passability : m_PassClasses)
@ -556,7 +566,7 @@ void CCmpPathfinder::UpdateGrid()
// so that we can stop units getting too close to impassable navcells // so that we can stop units getting too close to impassable navcells
for (PathfinderPassability& passability : m_PassClasses) for (PathfinderPassability& passability : m_PassClasses)
{ {
if (!passability.m_HasClearance) if (passability.m_Clearance != fixed::Zero())
continue; continue;
// TODO: if multiple classes have the same clearance, we should // TODO: if multiple classes have the same clearance, we should
@ -592,11 +602,11 @@ void CCmpPathfinder::UpdateGrid()
} }
// Add obstructions onto the grid // Add obstructions onto the grid
cmpObstructionManager->Rasterize(*m_Grid, m_PassClasses, ICmpObstructionManager::FLAG_BLOCK_PATHFINDING, m_ObstructionsDirty.globalRecompute); cmpObstructionManager->Rasterize(*m_Grid, m_PassClasses, m_ObstructionsDirty.globalRecompute);
// Update the long-range pathfinder // Update the long-range pathfinder
if (m_ObstructionsDirty.globallyDirty) if (m_ObstructionsDirty.globallyDirty)
m_LongPathfinder.Reload(m_PassClassMasks, m_Grid); m_LongPathfinder.Reload(GetPathfindingPassabilityClasses(), m_Grid);
else else
m_LongPathfinder.Update(m_Grid, m_ObstructionsDirty.dirtinessGrid); m_LongPathfinder.Update(m_Grid, m_ObstructionsDirty.dirtinessGrid);
} }
@ -780,7 +790,7 @@ ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const I
entity_pos_t expand; entity_pos_t expand;
const PathfinderPassability* passability = GetPassabilityFromMask(passClass); const PathfinderPassability* passability = GetPassabilityFromMask(passClass);
if (passability && passability->m_HasClearance) if (passability)
expand = passability->m_Clearance; expand = passability->m_Clearance;
SimRasterize::Spans spans; SimRasterize::Spans spans;

View File

@ -141,13 +141,14 @@ public:
virtual std::map<std::string, pass_class_t> GetPassabilityClasses(); virtual std::map<std::string, pass_class_t> GetPassabilityClasses();
virtual std::map<std::string, pass_class_t> GetPathfindingPassabilityClasses();
const PathfinderPassability* GetPassabilityFromMask(pass_class_t passClass) const; const PathfinderPassability* GetPassabilityFromMask(pass_class_t passClass) const;
virtual entity_pos_t GetClearance(pass_class_t passClass) const virtual entity_pos_t GetClearance(pass_class_t passClass) const
{ {
const PathfinderPassability* passability = GetPassabilityFromMask(passClass); const PathfinderPassability* passability = GetPassabilityFromMask(passClass);
if (!passability)
if (!passability->m_HasClearance)
return fixed::Zero(); return fixed::Zero();
return passability->m_Clearance; return passability->m_Clearance;
@ -158,10 +159,8 @@ public:
entity_pos_t max = fixed::Zero(); entity_pos_t max = fixed::Zero();
for (const PathfinderPassability& passability : m_PassClasses) for (const PathfinderPassability& passability : m_PassClasses)
{ if (passability.m_Clearance > max)
if (passability.m_HasClearance && passability.m_Clearance > max)
max = passability.m_Clearance; max = passability.m_Clearance;
}
return max; return max;
} }

View File

@ -328,7 +328,7 @@ void CCmpTerritoryManager::CalculateCostGrid()
if (!cmpPathfinder) if (!cmpPathfinder)
return; return;
pass_class_t passClassTerritory = cmpPathfinder->GetPassabilityClass("territory"); pass_class_t passClassTerritory = cmpPathfinder->GetPassabilityClass("default-terrain-only");
pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted"); pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid(); const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();

View File

@ -215,12 +215,11 @@ public:
}; };
/** /**
* Convert the current set of shapes onto a navcell grid. * Convert the current set of shapes onto a navcell grid, for all passability classes contained in @p passClasses.
* If @p fullUpdate is false, the function will only go through dirty shapes. * If @p fullUpdate is false, the function will only go through dirty shapes.
* Shapes are expanded by the @p passClasses clearances, by ORing their masks onto the @p grid. * Shapes are expanded by the @p passClasses clearances, by ORing their masks onto the @p grid.
* Only shapes with at least one of the flags from @p requireMask will be considered.
*/ */
virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate) = 0; virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, bool fullUpdate) = 0;
/** /**
* Gets dirtiness information and resets it afterwards. Then it's the role of CCmpPathfinder * Gets dirtiness information and resets it afterwards. Then it's the role of CCmpPathfinder

View File

@ -55,6 +55,11 @@ public:
*/ */
virtual std::map<std::string, pass_class_t> GetPassabilityClasses() = 0; virtual std::map<std::string, pass_class_t> GetPassabilityClasses() = 0;
/**
* Get the list of pathfinding passability classes.
*/
virtual std::map<std::string, pass_class_t> GetPathfindingPassabilityClasses() = 0;
/** /**
* Get the tag for a given passability class name. * Get the tag for a given passability class name.
* Logs an error and returns something acceptable if the name is unrecognised. * Logs an error and returns something acceptable if the name is unrecognised.

View File

@ -183,7 +183,7 @@ public:
m_DebugPassClass = passClass; m_DebugPassClass = passClass;
} }
void Reload(std::map<std::string, pass_class_t> passClassMasks, Grid<NavcellData>* passabilityGrid) void Reload(const std::map<std::string, pass_class_t>& passClassMasks, Grid<NavcellData>* passabilityGrid)
{ {
m_Grid = passabilityGrid; m_Grid = passabilityGrid;
ASSERT(passabilityGrid->m_H == passabilityGrid->m_W); ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);

View File

@ -163,10 +163,13 @@ namespace Pathfinding
* Passability is determined by water depth, terrain slope, forestness, buildingness. * Passability is determined by water depth, terrain slope, forestness, buildingness.
* We need at least one bit per class per tile to represent passability. * We need at least one bit per class per tile to represent passability.
* *
* We use a separate bit to indicate building obstructions (instead of folding it into * Not all pass classes are used for actual pathfinding. The pathfinder calls
* the class passabilities) so that it can be ignored when doing the accurate short paths. * CCmpObstructionManager's Rasterize() to add shapes onto the passability grid.
* We use another bit to indicate tiles near obstructions that block construction, * Which shapes are rasterized depend on the value of the m_Obstructions of each passability
* for the AI to plan safe building spots. * class.
*
* Passabilities not used for unit pathfinding should not use the Clearance attribute, and
* will get a zero clearance value.
*/ */
class PathfinderPassability class PathfinderPassability
{ {
@ -201,7 +204,6 @@ public:
if (node.GetChild("Clearance").IsOk()) if (node.GetChild("Clearance").IsOk())
{ {
m_HasClearance = true;
m_Clearance = node.GetChild("Clearance").ToFixed(); m_Clearance = node.GetChild("Clearance").ToFixed();
if (!(m_Clearance % Pathfinding::NAVCELL_SIZE).IsZero()) if (!(m_Clearance % Pathfinding::NAVCELL_SIZE).IsZero())
@ -214,11 +216,26 @@ public:
} }
} }
else else
{
m_HasClearance = false;
m_Clearance = fixed::Zero(); m_Clearance = fixed::Zero();
if (node.GetChild("Obstructions").IsOk())
{
std::wstring obstructions = node.GetChild("Obstructions").ToString();
if (obstructions == L"none")
m_Obstructions = NONE;
else if (obstructions == L"pathfinding")
m_Obstructions = PATHFINDING;
else if (obstructions == L"foundation")
m_Obstructions = FOUNDATION;
else
{
LOGERROR("Invalid value for Obstructions in pathfinder.xml for pass class %d", mask);
m_Obstructions = NONE;
} }
} }
else
m_Obstructions = NONE;
}
bool IsPassable(fixed waterdepth, fixed steepness, fixed shoredist) bool IsPassable(fixed waterdepth, fixed steepness, fixed shoredist)
{ {
@ -227,9 +244,16 @@ public:
pass_class_t m_Mask; pass_class_t m_Mask;
bool m_HasClearance; // whether static obstructions are impassable
fixed m_Clearance; // min distance from static obstructions fixed m_Clearance; // min distance from static obstructions
enum ObstructionHandling
{
NONE,
PATHFINDING,
FOUNDATION
};
ObstructionHandling m_Obstructions;
private: private:
fixed m_MinDepth; fixed m_MinDepth;
fixed m_MaxDepth; fixed m_MaxDepth;