Changeset View
Standalone View
source/simulation2/components/CCmpObstructionManager.cpp
Show First 20 Lines • Show All 460 Lines • ▼ Show 20 Lines | virtual ObstructionSquare GetObstruction(tag_t tag) const | ||||
{ | { | ||||
const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag)); | const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag)); | ||||
ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh }; | ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh }; | ||||
return o; | return o; | ||||
} | } | ||||
} | } | ||||
virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const; | virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const; | ||||
virtual fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const; | |||||
virtual fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const; | |||||
virtual fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const; | |||||
fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const; | |||||
fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const; | |||||
virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const; | |||||
virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const; | |||||
virtual bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const; | |||||
virtual bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const; | virtual bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const; | ||||
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) const; | 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) const; | ||||
virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out) const; | virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out) const; | ||||
virtual void Rasterize(Grid<NavcellData>& grid, const std::vector<PathfinderPassability>& passClasses, bool fullUpdate); | virtual void Rasterize(Grid<NavcellData>& 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) const; | 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) const; | ||||
virtual void GetUnitObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) const; | virtual void GetUnitObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) const; | ||||
▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | inline bool IsInWorld(const CFixedVector2D& p) const | ||||
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<NavcellData>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, pass_class_t appliedMask, entity_pos_t clearance = fixed::Zero()) const; | void RasterizeHelper(Grid<NavcellData>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, pass_class_t appliedMask, entity_pos_t clearance = fixed::Zero()) const; | ||||
}; | }; | ||||
REGISTER_COMPONENT_TYPE(ObstructionManager) | REGISTER_COMPONENT_TYPE(ObstructionManager) | ||||
/** | |||||
* DistanceTo function family, all end up in calculating a vector length, DistanceBetweenShapes or | |||||
* MaxDistanceBetweenShapes. The MaxFoo family calculates the opposite edge opposite edge distance. | |||||
* When the distance is undefined we return -1. | |||||
*/ | |||||
fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const | fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const | ||||
{ | { | ||||
ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | |||||
if (cmpObstruction && cmpObstruction->GetObstructionSquare(obstruction)) | |||||
{ | |||||
ObstructionSquare point; | |||||
point.x = px; | |||||
point.z = pz; | |||||
return DistanceBetweenShapes(obstruction, point); | |||||
} | |||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return fixed::FromInt(-1); | |||||
return (CFixedVector2D(cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y) - CFixedVector2D(px, pz)).Length(); | |||||
} | |||||
fixed CCmpObstructionManager::MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const | |||||
{ | |||||
ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | |||||
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction)) | |||||
{ | |||||
ObstructionSquare point; | |||||
point.x = px; | |||||
point.z = pz; | |||||
return MaxDistanceBetweenShapes(obstruction, point); | |||||
} | |||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent); | CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return fixed::FromInt(-1); | return fixed::FromInt(-1); | ||||
ObstructionSquare s; | return (CFixedVector2D(cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y) - CFixedVector2D(px, pz)).Length(); | ||||
} | |||||
fixed CCmpObstructionManager::DistanceToTarget(entity_id_t ent, entity_id_t target) const | |||||
{ | |||||
temple: What weirdness? | |||||
Not Done Inline ActionsMainly rounding errors, the position can be shifted a bit due to the navcell grid and in some edgy cases that could result in rough edges. Also f.e. when the minrange is 0 (thus attack always) and we are at the same position (thus distance =0), we should be in range, so we need a little threshold bb: Mainly rounding errors, the position can be shifted a bit due to the navcell grid and in some… | |||||
Not Done Inline ActionsnavcellFix = 1.5m, which seems a lot for rounding errors! Swordsmen have a 2m max range! temple: navcellFix = 1.5m, which seems a lot for rounding errors! Swordsmen have a 2m max range!
We use… | |||||
Not Done Inline ActionsI'm pretty sure I added this because of some peculiarity I ran into when testing the D13 changes, but honestly I haven't documented why and I don't remember. I'd advise "remove it and see if it breaks things". wraitii: I'm pretty sure I added this because of some peculiarity I ran into when testing the D13… | |||||
Not Done Inline ActionsThese function tie in very tied with moving to a certain position, which ofc is based on the pathfinder and thus uses navcells bb: These function tie in very tied with moving to a certain position, which ofc is based on the… | |||||
ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | |||||
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction)) | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return fixed::FromInt(-1); | |||||
return DistanceToPoint(target, cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y); | |||||
} | |||||
ObstructionSquare target_obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstructionTarget(GetSimContext(), target); | |||||
if (!cmpObstructionTarget || !cmpObstructionTarget->GetObstructionSquare(target_obstruction)) | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpPositionTarget(GetSimContext(), target); | |||||
if (!cmpPositionTarget || !cmpPositionTarget->IsInWorld()) | |||||
return fixed::FromInt(-1); | |||||
return DistanceToPoint(ent, cmpPositionTarget->GetPosition2D().X, cmpPositionTarget->GetPosition2D().Y); | |||||
Not Done Inline ActionsI didn't think we had circular obstructions? Are they planned? temple: I didn't think we had circular obstructions? Are they planned? | |||||
Not Done Inline Actionsunits are spherical. wraitii: units are spherical. | |||||
Not Done Inline ActionsI see the change in D13. temple: I see the change in D13. | |||||
Not Done Inline ActionsAh, that could very well be the case indeed. wraitii: Ah, that could very well be the case indeed. | |||||
} | |||||
Not Done Inline ActionsI don't like this definition. So I think we should use the closest distance for both the maximum and minimum range comparisons. (I assume there's other things we'll have to figure out, e.g. I think attack uses the center of footprints, so we might have to change that, when attacking structures at least.) temple: I don't like this definition.
Rhetorical: What purpose does a minimum range have? Obviously if… | |||||
Not Done Inline ActionsThe issue is that I want commutative behaviour (i.e. the source and target order shouldn't always return the same even if swapped). If I want commutative behaviour, we have a bad case for buildings vs units, as I wrote below (and indeed I describe this very issue in the comment L 756). Take a catapult with a min-range. To attack a building, if we check from closest edge to closest edge, then the catapult needs to be some distance away. But maybe the building is really large and the far-side is easily inside our min-range. That being said, buildings ought to fire from well-defined points so the issue is moot here. wraitii: The issue is that I want commutative behaviour (i.e. the source and target order shouldn't… | |||||
Not Done Inline ActionsHow can he hit the far edge without shooting through the near edge? It would be nice to have an attack ground command. Buildings having subobstructions or whatever for BuildingAI makes sense. temple: How can he hit the far edge without shooting through the near edge?
It would be nice to have… | |||||
Not Done Inline Actions
Well, above, or it's a flat building. Not everything is a fortress. I think in general we should avoid shape-shape range detections, and go shape->point which is much easier. But the shape-shape behaviour should be decided on. wraitii: > How can he hit the far edge without shooting through the near edge?
Well, above, or it's a… | |||||
Not Done Inline ActionsThe fact that buildings fire from well defined locations, should be fixed with using turrets when they are implemented The murderholes tech would still work, we probably need to adjust the range a bit, we only take it from the opposite edge now. There are indeed multiple views on this matter:
bb: The fact that buildings fire from well defined locations, should be fixed with using turrets… | |||||
return DistanceBetweenShapes(obstruction, target_obstruction); | |||||
} | |||||
fixed CCmpObstructionManager::MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const | |||||
{ | |||||
ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | ||||
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(s)) | if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction)) | ||||
return (CFixedVector2D(px, pz) - cmpPosition->GetPosition2D()).Length(); | { | ||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return fixed::FromInt(-1); | |||||
return MaxDistanceToPoint(target, cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y); | |||||
} | |||||
ObstructionSquare target_obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstructionTarget(GetSimContext(), target); | |||||
Done Inline ActionsIs GetSimContext() slow ? Maybe we could cache it ? Stan: Is GetSimContext() slow ? Maybe we could cache it ? | |||||
Done Inline Actionsirrelevant imo. wraitii: irrelevant imo. | |||||
if (!cmpObstructionTarget || !cmpObstructionTarget->GetObstructionSquare(target_obstruction)) | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpPositionTarget(GetSimContext(), target); | |||||
if (!cmpPositionTarget || !cmpPositionTarget->IsInWorld()) | |||||
return fixed::FromInt(-1); | |||||
return MaxDistanceToPoint(ent, cmpPositionTarget->GetPosition2D().X, cmpPositionTarget->GetPosition2D().Y); | |||||
} | |||||
return MaxDistanceBetweenShapes(obstruction, target_obstruction); | |||||
} | |||||
fixed CCmpObstructionManager::DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const | |||||
{ | |||||
// Sphere-sphere collision. | |||||
if (source.hh == fixed::Zero() && target.hh == fixed::Zero()) | |||||
return (CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z)).Length() - source.hw - target.hw; | |||||
// Square to square. | |||||
Done Inline ActionsSame here. Stan: Same here. | |||||
if (source.hh != fixed::Zero() && target.hh != fixed::Zero()) | |||||
return Geometry::DistanceSquareToSquare( | |||||
CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z), | |||||
source.u, source.v, CFixedVector2D(source.hw, source.hh), | |||||
target.u, target.v, CFixedVector2D(target.hw, target.hh)); | |||||
Not Done Inline Actionswould like an opinion on that bb: would like an opinion on that | |||||
// To cover both remaining cases, shape a is the square one, shape b is the circular one. | |||||
const ObstructionSquare& a = source.hh == fixed::Zero() ? target : source; | |||||
const ObstructionSquare& b = source.hh == fixed::Zero() ? source : target; | |||||
return Geometry::DistanceToSquare( | |||||
CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z), | |||||
a.u, a.v, CFixedVector2D(a.hw, a.hh), true) - b.hw; | |||||
} | |||||
return Geometry::DistanceToSquare(CFixedVector2D(px - s.x, pz - s.z), s.u, s.v, CFixedVector2D(s.hw, s.hh)); | fixed CCmpObstructionManager::MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const | ||||
{ | |||||
// Sphere-sphere collision. | |||||
if (source.hh == fixed::Zero() && target.hh == fixed::Zero()) | |||||
return (CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z)).Length() + source.hw + target.hw; | |||||
// Square to square. | |||||
if (source.hh != fixed::Zero() && target.hh != fixed::Zero()) | |||||
return Geometry::MaxDistanceSquareToSquare( | |||||
CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z), | |||||
source.u, source.v, CFixedVector2D(source.hw, source.hh), | |||||
Done Inline ActionsWhy check the square after the sphere ? Ifs could be merged :) target.hh == fixed::Zero() is checked every time. Stan: Why check the square after the sphere ?
Ifs could be merged :) target.hh == fixed::Zero() is… | |||||
Done Inline Actionswouldn't improve readability imo. No particular reason on the order except that sphere sphere is faster/simpler. wraitii: wouldn't improve readability imo.
No particular reason on the order except that sphere sphere… | |||||
target.u, target.v, CFixedVector2D(target.hw, target.hh)); | |||||
// To cover both remaining cases, shape a is the square one, shape b is the circular one. | |||||
const ObstructionSquare& a = source.hh == fixed::Zero() ? target : source; | |||||
const ObstructionSquare& b = source.hh == fixed::Zero() ? source : target; | |||||
return Geometry::MaxDistanceToSquare( | |||||
CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z), | |||||
a.u, a.v, CFixedVector2D(a.hw, a.hh), true) + b.hw; | |||||
} | |||||
/** | |||||
* IsInRange function family depending on the DistanceTo family. | |||||
* | |||||
* In range if the edge to edge distance is inferior to maxRange | |||||
* and if the opposite edge to opposite edge distance is greater than minRange when the opposite bool is true | |||||
* or when the opposite bool is false the edge to edge distance is more than minRange. | |||||
* | |||||
* Using the opposite egde for minRange means that a unit is in range of a building if it is farther than | |||||
* clearance-buildingsize, which is generally going to be negative (and thus this returns true). | |||||
Done Inline ActionsMaybe the two functions could be unified somehow, it's basically the same code... with one function difference. Stan: Maybe the two functions could be unified somehow, it's basically the same code... with one… | |||||
Done Inline Actionskind of annoying without if-constexpr. wraitii: kind of annoying without if-constexpr. | |||||
* NB: from a game POV, this means units can easily fire on buildings, which is good, | |||||
* but it also means that buildings can easily fire on units. Buildings are usually meant | |||||
* to fire from the edge, not the opposite edge, so this looks odd. For this reason one can choose | |||||
* to set the opposite bool false and use the edge to egde distance. | |||||
* | |||||
* We don't use squares because the are likely to overflow. | |||||
* We use a 0.0001 margin to avoid rounding errors. | |||||
*/ | |||||
bool CCmpObstructionManager::IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const | |||||
{ | |||||
fixed dist = DistanceToPoint(ent, px, pz); | |||||
return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToPoint(ent, px, pz) : dist) >= minRange - fixed::FromFloat(0.0001); | |||||
} | |||||
bool CCmpObstructionManager::IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const | |||||
{ | |||||
fixed dist = DistanceToTarget(ent, target); | |||||
return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToTarget(ent, target) : dist) >= minRange- fixed::FromFloat(0.0001); | |||||
} | |||||
bool CCmpObstructionManager::IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const | |||||
{ | |||||
entity_pos_t distance = (CFixedVector2D(x, z) - CFixedVector2D(px, pz)).Length(); | |||||
return distance <= maxRange + fixed::FromFloat(0.0001) && distance >= minRange - fixed::FromFloat(0.0001); | |||||
} | } | ||||
bool CCmpObstructionManager::TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits) const | bool CCmpObstructionManager::TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits) const | ||||
{ | { | ||||
PROFILE("TestLine"); | PROFILE("TestLine"); | ||||
// Check that both end points are within the world (which means the whole line must be) | // Check that both end points are within the world (which means the whole line must be) | ||||
if (!IsInWorld(x0, z0, r) || !IsInWorld(x1, z1, r)) | if (!IsInWorld(x0, z0, r) || !IsInWorld(x1, z1, r)) | ||||
▲ Show 20 Lines • Show All 486 Lines • Show Last 20 Lines |
What weirdness?