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:
parent
4b8f0c9fb9
commit
1709353e2c
@ -1,19 +1,20 @@
|
||||
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
|
||||
##
|
||||
# NOTE: To modify this Relax NG grammar, edit the Relax NG Compact (.rnc) file
|
||||
# and use a converter tool like trang to generate the Relax NG XML (.rng) file
|
||||
##
|
||||
|
||||
element Pathfinder {
|
||||
element MaxSameTurnMoves { xsd:nonNegativeInteger } &
|
||||
element PassabilityClasses {
|
||||
element * {
|
||||
element MinWaterDepth { xsd:decimal }? & # TODO: fixed type
|
||||
element MaxWaterDepth { xsd:decimal }? &
|
||||
element MaxTerrainSlope { xsd:decimal }? &
|
||||
element MinShoreDistance { xsd:decimal }? &
|
||||
element MaxShoreDistance { xsd:decimal }? &
|
||||
element Clearance { xsd:decimal }?
|
||||
}+
|
||||
}
|
||||
}
|
||||
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
|
||||
##
|
||||
# NOTE: To modify this Relax NG grammar, edit the Relax NG Compact (.rnc) file
|
||||
# and use a converter tool like trang to generate the Relax NG XML (.rng) file
|
||||
##
|
||||
|
||||
element Pathfinder {
|
||||
element MaxSameTurnMoves { xsd:nonNegativeInteger } &
|
||||
element PassabilityClasses {
|
||||
element * {
|
||||
element Obstructions { xsd:string } &
|
||||
element MinWaterDepth { xsd:decimal }? & # TODO: fixed type
|
||||
element MaxWaterDepth { xsd:decimal }? &
|
||||
element MaxTerrainSlope { xsd:decimal }? &
|
||||
element MinShoreDistance { xsd:decimal }? &
|
||||
element MaxShoreDistance { xsd:decimal }? &
|
||||
element Clearance { xsd:decimal }?
|
||||
}+
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,9 @@
|
||||
<element>
|
||||
<anyName/>
|
||||
<interleave>
|
||||
<element name="Obstructions">
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<optional>
|
||||
<element name="MinWaterDepth">
|
||||
<data type="decimal"/>
|
||||
|
@ -3,67 +3,67 @@
|
||||
<!-- Sets limit on the number of same turns moves we will process -->
|
||||
<!-- Setting the value to 0 disable this functionality -->
|
||||
<MaxSameTurnMoves>64</MaxSameTurnMoves>
|
||||
|
||||
|
||||
<PassabilityClasses>
|
||||
|
||||
<unrestricted/>
|
||||
|
||||
<!-- 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>
|
||||
<Obstructions>pathfinding</Obstructions>
|
||||
<MaxWaterDepth>2</MaxWaterDepth>
|
||||
<MaxTerrainSlope>1.0</MaxTerrainSlope>
|
||||
<Clearance>1.0</Clearance>
|
||||
</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>
|
||||
<Obstructions>pathfinding</Obstructions>
|
||||
<MaxWaterDepth>2</MaxWaterDepth>
|
||||
<MaxTerrainSlope>1.0</MaxTerrainSlope>
|
||||
<Clearance>4.0</Clearance>
|
||||
</siege-large>
|
||||
<ship>
|
||||
<Obstructions>pathfinding</Obstructions>
|
||||
<MinWaterDepth>1</MinWaterDepth>
|
||||
<Clearance>12.0</Clearance>
|
||||
</ship>
|
||||
<ship-small>
|
||||
<Obstructions>pathfinding</Obstructions>
|
||||
<MinWaterDepth>1</MinWaterDepth>
|
||||
<Clearance>4.0</Clearance>
|
||||
</ship-small>
|
||||
|
||||
<!-- Building construction classes:
|
||||
<!--
|
||||
Building construction classes:
|
||||
* Land is used for most buildings, which must be away
|
||||
from water and not on cliffs or mountains.
|
||||
* Shore is used for docks, which must be near water and
|
||||
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>
|
||||
<Obstructions>foundation</Obstructions>
|
||||
<MaxWaterDepth>0</MaxWaterDepth>
|
||||
<MinShoreDistance>4.0</MinShoreDistance>
|
||||
<MaxTerrainSlope>1.0</MaxTerrainSlope>
|
||||
</building-land>
|
||||
<building-shore>
|
||||
<Obstructions>foundation</Obstructions>
|
||||
<MaxShoreDistance>8.0</MaxShoreDistance>
|
||||
<MaxTerrainSlope>1.25</MaxTerrainSlope>
|
||||
</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>
|
||||
<MaxTerrainSlope>1.0</MaxTerrainSlope>
|
||||
</territory>
|
||||
</default-terrain-only>
|
||||
|
||||
</PassabilityClasses>
|
||||
</Pathfinder>
|
||||
|
@ -1052,7 +1052,7 @@ public:
|
||||
LoadPathfinderClasses(state);
|
||||
std::map<std::string, pass_class_t> passClassMasks;
|
||||
if (cmpPathfinder)
|
||||
passClassMasks = cmpPathfinder->GetPassabilityClasses();
|
||||
passClassMasks = cmpPathfinder->GetPathfindingPassabilityClasses();
|
||||
|
||||
m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state),
|
||||
*passabilityMap, dirtinessInformations,
|
||||
|
@ -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 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 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);
|
||||
}
|
||||
|
||||
void RasterizeHelper(Grid<u16>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, u16 appliedMask, entity_pos_t clearance = fixed::Zero());
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(ObstructionManager)
|
||||
@ -810,83 +812,100 @@ bool CCmpObstructionManager::TestUnitShape(const IObstructionTestFilter& filter,
|
||||
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");
|
||||
|
||||
// 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.
|
||||
// (That ensures the shape's geometric border is always reachable.)
|
||||
|
||||
// Add obstructions onto the grid, for any class with (possibly zero) clearance
|
||||
std::map<entity_pos_t, u16> combinedMasks;
|
||||
// Pass classes will get shapes rasterized on them depending on their Obstruction value.
|
||||
// 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)
|
||||
{
|
||||
if (!passability.m_HasClearance)
|
||||
switch (passability.m_Obstructions)
|
||||
{
|
||||
case PathfinderPassability::PATHFINDING:
|
||||
{
|
||||
auto it = pathfindingMasks.find(passability.m_Clearance);
|
||||
if (it == pathfindingMasks.end())
|
||||
pathfindingMasks[passability.m_Clearance] = passability.m_Mask;
|
||||
else
|
||||
it->second |= passability.m_Mask;
|
||||
break;
|
||||
}
|
||||
case PathfinderPassability::FOUNDATION:
|
||||
foundationMask |= passability.m_Mask;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
|
||||
auto it = combinedMasks.find(passability.m_Clearance);
|
||||
if (it == combinedMasks.end())
|
||||
combinedMasks[passability.m_Clearance] = passability.m_Mask;
|
||||
else
|
||||
it->second |= passability.m_Mask;
|
||||
}
|
||||
|
||||
for (auto& maskPair : combinedMasks)
|
||||
{
|
||||
for (auto& pair : m_StaticShapes)
|
||||
{
|
||||
if (!fullUpdate && std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), pair.first) == m_DirtyStaticShapes.end())
|
||||
continue;
|
||||
|
||||
const StaticShape& shape = pair.second;
|
||||
if (!(shape.flags & requireMask))
|
||||
continue;
|
||||
|
||||
// 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 };
|
||||
SimRasterize::Spans spans;
|
||||
SimRasterize::RasterizeRectWithClearance(spans, square, maskPair.first, Pathfinding::NAVCELL_SIZE);
|
||||
for (SimRasterize::Span& span : spans)
|
||||
{
|
||||
i16 j = span.j;
|
||||
if (j >= 0 && j <= grid.m_H)
|
||||
{
|
||||
i16 i0 = std::max(span.i0, (i16)0);
|
||||
i16 i1 = std::min(span.i1, (i16)grid.m_W);
|
||||
for (i16 i = i0; i < i1; ++i)
|
||||
grid.set(i, j, grid.get(i, j) | maskPair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& pair : m_UnitShapes)
|
||||
{
|
||||
if (!fullUpdate && std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), pair.first) == m_DirtyUnitShapes.end())
|
||||
continue;
|
||||
|
||||
CFixedVector2D center(pair.second.x, pair.second.z);
|
||||
|
||||
if (!(pair.second.flags & requireMask))
|
||||
continue;
|
||||
|
||||
entity_pos_t r = pair.second.r + maskPair.first;
|
||||
|
||||
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, i1, j1, grid.m_W, grid.m_H);
|
||||
for (u16 j = j0+1; j < j1; ++j)
|
||||
for (u16 i = i0+1; i < i1; ++i)
|
||||
grid.set(i, j, grid.get(i, j) | maskPair.second);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!fullUpdate && std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), pair.first) == m_DirtyStaticShapes.end())
|
||||
continue;
|
||||
|
||||
const StaticShape& shape = pair.second;
|
||||
if (!(shape.flags & requireMask))
|
||||
continue;
|
||||
|
||||
// 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 };
|
||||
SimRasterize::Spans spans;
|
||||
SimRasterize::RasterizeRectWithClearance(spans, square, clearance, Pathfinding::NAVCELL_SIZE);
|
||||
for (SimRasterize::Span& span : spans)
|
||||
{
|
||||
i16 j = span.j;
|
||||
if (j >= 0 && j <= grid.m_H)
|
||||
{
|
||||
i16 i0 = std::max(span.i0, (i16)0);
|
||||
i16 i1 = std::min(span.i1, (i16)grid.m_W);
|
||||
for (i16 i = i0; i < i1; ++i)
|
||||
grid.set(i, j, grid.get(i, j) | appliedMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& pair : m_UnitShapes)
|
||||
{
|
||||
if (!fullUpdate && std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), pair.first) == m_DirtyUnitShapes.end())
|
||||
continue;
|
||||
|
||||
CFixedVector2D center(pair.second.x, pair.second.z);
|
||||
|
||||
if (!(pair.second.flags & requireMask))
|
||||
continue;
|
||||
|
||||
entity_pos_t r = pair.second.r + clearance;
|
||||
|
||||
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, i1, j1, grid.m_W, grid.m_H);
|
||||
for (u16 j = j0+1; j < j1; ++j)
|
||||
for (u16 i = i0+1; i < i1; ++i)
|
||||
grid.set(i, j, grid.get(i, j) | appliedMask);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -200,6 +200,16 @@ std::map<std::string, pass_class_t> CCmpPathfinder::GetPassabilityClasses()
|
||||
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
|
||||
{
|
||||
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
|
||||
for (PathfinderPassability& passability : m_PassClasses)
|
||||
{
|
||||
if (!passability.m_HasClearance)
|
||||
if (passability.m_Clearance != fixed::Zero())
|
||||
continue;
|
||||
|
||||
// TODO: if multiple classes have the same clearance, we should
|
||||
@ -592,11 +602,11 @@ void CCmpPathfinder::UpdateGrid()
|
||||
}
|
||||
|
||||
// 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
|
||||
if (m_ObstructionsDirty.globallyDirty)
|
||||
m_LongPathfinder.Reload(m_PassClassMasks, m_Grid);
|
||||
m_LongPathfinder.Reload(GetPathfindingPassabilityClasses(), m_Grid);
|
||||
else
|
||||
m_LongPathfinder.Update(m_Grid, m_ObstructionsDirty.dirtinessGrid);
|
||||
}
|
||||
@ -780,7 +790,7 @@ ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const I
|
||||
|
||||
entity_pos_t expand;
|
||||
const PathfinderPassability* passability = GetPassabilityFromMask(passClass);
|
||||
if (passability && passability->m_HasClearance)
|
||||
if (passability)
|
||||
expand = passability->m_Clearance;
|
||||
|
||||
SimRasterize::Spans spans;
|
||||
|
@ -141,13 +141,14 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
virtual entity_pos_t GetClearance(pass_class_t passClass) const
|
||||
{
|
||||
const PathfinderPassability* passability = GetPassabilityFromMask(passClass);
|
||||
|
||||
if (!passability->m_HasClearance)
|
||||
if (!passability)
|
||||
return fixed::Zero();
|
||||
|
||||
return passability->m_Clearance;
|
||||
@ -158,10 +159,8 @@ public:
|
||||
entity_pos_t max = fixed::Zero();
|
||||
|
||||
for (const PathfinderPassability& passability : m_PassClasses)
|
||||
{
|
||||
if (passability.m_HasClearance && passability.m_Clearance > max)
|
||||
if (passability.m_Clearance > max)
|
||||
max = passability.m_Clearance;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ void CCmpTerritoryManager::CalculateCostGrid()
|
||||
if (!cmpPathfinder)
|
||||
return;
|
||||
|
||||
pass_class_t passClassTerritory = cmpPathfinder->GetPassabilityClass("territory");
|
||||
pass_class_t passClassTerritory = cmpPathfinder->GetPassabilityClass("default-terrain-only");
|
||||
pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
|
||||
|
||||
const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();
|
||||
|
@ -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.
|
||||
* 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
|
||||
|
@ -55,6 +55,11 @@ public:
|
||||
*/
|
||||
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.
|
||||
* Logs an error and returns something acceptable if the name is unrecognised.
|
||||
|
@ -183,7 +183,7 @@ public:
|
||||
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;
|
||||
ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);
|
||||
|
@ -163,10 +163,13 @@ namespace Pathfinding
|
||||
* Passability is determined by water depth, terrain slope, forestness, buildingness.
|
||||
* 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
|
||||
* the class passabilities) so that it can be ignored when doing the accurate short paths.
|
||||
* We use another bit to indicate tiles near obstructions that block construction,
|
||||
* for the AI to plan safe building spots.
|
||||
* Not all pass classes are used for actual pathfinding. The pathfinder calls
|
||||
* CCmpObstructionManager's Rasterize() to add shapes onto the passability grid.
|
||||
* Which shapes are rasterized depend on the value of the m_Obstructions of each passability
|
||||
* class.
|
||||
*
|
||||
* Passabilities not used for unit pathfinding should not use the Clearance attribute, and
|
||||
* will get a zero clearance value.
|
||||
*/
|
||||
class PathfinderPassability
|
||||
{
|
||||
@ -201,7 +204,6 @@ public:
|
||||
|
||||
if (node.GetChild("Clearance").IsOk())
|
||||
{
|
||||
m_HasClearance = true;
|
||||
m_Clearance = node.GetChild("Clearance").ToFixed();
|
||||
|
||||
if (!(m_Clearance % Pathfinding::NAVCELL_SIZE).IsZero())
|
||||
@ -214,10 +216,25 @@ public:
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HasClearance = false;
|
||||
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)
|
||||
@ -227,9 +244,16 @@ public:
|
||||
|
||||
pass_class_t m_Mask;
|
||||
|
||||
bool m_HasClearance; // whether static obstructions are impassable
|
||||
fixed m_Clearance; // min distance from static obstructions
|
||||
|
||||
enum ObstructionHandling
|
||||
{
|
||||
NONE,
|
||||
PATHFINDING,
|
||||
FOUNDATION
|
||||
};
|
||||
ObstructionHandling m_Obstructions;
|
||||
|
||||
private:
|
||||
fixed m_MinDepth;
|
||||
fixed m_MaxDepth;
|
||||
|
Loading…
Reference in New Issue
Block a user