forked from 0ad/0ad
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
@ -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 }? &
|
||||||
|
@ -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"/>
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user