Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -1707,8 +1707,8 @@ "MoveStarted": function(msg) { // Adapt the speed to the one of the target if needed - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - if (cmpUnitMotion.IsInTargetRange(this.isGuardOf, 0, 3*this.guardRange)) + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + if (cmpObstructionManager.IsInTargetRange(this.entity, this.isGuardOf, 0, 3 * this.guardRange)) { var cmpUnitAI = Engine.QueryInterface(this.isGuardOf, IID_UnitAI); if (cmpUnitAI) @@ -4479,8 +4479,8 @@ UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max) { - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - return cmpUnitMotion.IsInPointRange(x, z, min, max); + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + return cmpObstructionManager.IsInPointRange(this.entity, x, z, min, max); }; UnitAI.prototype.CheckTargetRange = function(target, iid, type) @@ -4490,8 +4490,8 @@ return false; var range = cmpRanged.GetRange(type); - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - return cmpUnitMotion.IsInTargetRange(target, range.min, range.max); + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max); }; /** @@ -4539,14 +4539,14 @@ if (maxRangeSq < 0) return false; - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - return cmpUnitMotion.IsInTargetRange(target, range.min, Math.sqrt(maxRangeSq)); + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, Math.sqrt(maxRangeSq)); }; UnitAI.prototype.CheckTargetRangeExplicit = function(target, min, max) { - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - return cmpUnitMotion.IsInTargetRange(target, min, max); + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + return cmpObstructionManager.IsInTargetRange(this.entity, target, min, max); }; UnitAI.prototype.CheckGarrisonRange = function(target) @@ -4560,8 +4560,8 @@ if (cmpObstruction) range.max += cmpObstruction.GetUnitRadius()*1.5; // multiply by something larger than sqrt(2) - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - return cmpUnitMotion.IsInTargetRange(target, range.min, range.max); + var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max); }; /** Index: binaries/data/mods/public/simulation/components/UnitMotionFlying.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitMotionFlying.js +++ binaries/data/mods/public/simulation/components/UnitMotionFlying.js @@ -276,29 +276,6 @@ return true; }; -UnitMotionFlying.prototype.IsInPointRange = function(x, y, minRange, maxRange) -{ - var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); - var pos = cmpPosition.GetPosition2D(); - - var distFromTarget = Math.euclidDistance2D(x, y, pos.x, pos.y); - if (minRange <= distFromTarget && distFromTarget <= maxRange) - return true; - - return false; -}; - -UnitMotionFlying.prototype.IsInTargetRange = function(target, minRange, maxRange) -{ - var cmpTargetPosition = Engine.QueryInterface(target, IID_Position); - if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) - return false; - - var targetPos = cmpTargetPosition.GetPosition2D(); - - return this.IsInPointRange(targetPos.x, targetPos.y, minRange, maxRange); -}; - UnitMotionFlying.prototype.GetWalkSpeed = function() { return +this.template.MaxSpeed; Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -63,6 +63,9 @@ GetEnemies: function() { return []; }, }); + AddMock(SYSTEM_ENTITY, IID_ObstructionManager, { + IsInTargetRange: function(ent, target, min, max) { return true; } + }); var unitAI = ConstructComponent(unit, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" }); @@ -85,7 +88,6 @@ AddMock(unit, IID_UnitMotion, { GetWalkSpeed: function() { return 1; }, MoveToFormationOffset: function(target, x, z) { }, - IsInTargetRange: function(target, min, max) { return true; }, MoveToTargetRange: function(target, min, max) { }, StopMoving: function() { }, GetPassabilityClassName: function() { return "default"; }, @@ -204,6 +206,10 @@ GetNumPlayers: function() { return 2; }, }); + AddMock(SYSTEM_ENTITY, IID_ObstructionManager, { + IsInTargetRange: function(ent, target, min, max) { return true; } + }); + AddMock(playerEntity, IID_Player, { IsAlly: function() { return false; }, IsEnemy: function() { return true; }, @@ -236,7 +242,6 @@ AddMock(unit + i, IID_UnitMotion, { GetWalkSpeed: function() { return 1; }, MoveToFormationOffset: function(target, x, z) { }, - IsInTargetRange: function(target, min, max) { return true; }, MoveToTargetRange: function(target, min, max) { }, StopMoving: function() { }, GetPassabilityClassName: function() { return "default"; }, @@ -282,7 +287,6 @@ AddMock(controller, IID_UnitMotion, { SetSpeed: function(speed) { }, MoveToPointRange: function(x, z, minRange, maxRange) { }, - IsInTargetRange: function(target, min, max) { return true; }, GetPassabilityClassName: function() { return "default"; }, }); Index: binaries/data/mods/public/simulation/components/tests/test_UnitMotionFlying.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_UnitMotionFlying.js +++ binaries/data/mods/public/simulation/components/tests/test_UnitMotionFlying.js @@ -54,10 +54,6 @@ "GetPosition2D": () => { return { "x": 100, "y": 200 }; } }); -TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 10, 112), true); -TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 50, 111), false); -TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 112, 200), false); - AddMock(entity, IID_GarrisonHolder, { "AllowGarrisoning": () => {} }); Index: source/maths/FixedVector2D.h =================================================================== --- source/maths/FixedVector2D.h +++ source/maths/FixedVector2D.h @@ -117,6 +117,14 @@ } /** + * Returns the squared length of the vector. + */ + fixed LengthSquared() const + { + return this->Dot(*this); + } + + /** * Returns -1, 0, +1 depending on whether length is less/equal/greater * than the argument. * Avoids sqrting and overflowing. Index: source/maths/tests/test_FixedVector2D.h =================================================================== --- source/maths/tests/test_FixedVector2D.h +++ source/maths/tests/test_FixedVector2D.h @@ -66,6 +66,22 @@ TS_ASSERT_DELTA(v3.Length().ToDouble(), sqrt(2.0)*large.ToDouble(), 0.01); } + void test_LengthSquared() + { + CFixedVector2D v1 (fixed::FromInt(3), fixed::FromInt(4)); + TS_ASSERT_EQUALS(v1.LengthSquared().ToDouble(), 25.0); + + fixed max; + max.SetInternalValue((i32)0x7fffffff); + CFixedVector2D v2 (max.Sqrt(), fixed::FromInt(0)); + TS_ASSERT_DELTA(v2.LengthSquared().ToDouble(), max.ToDouble(), 0.01); + + fixed large; + large.SetInternalValue((i32)((double)0x7fffffff/2.0)); // largest value that shouldn't cause overflow + CFixedVector2D v3 (large.Sqrt(), large.Sqrt()); + TS_ASSERT_DELTA(v3.LengthSquared().ToDouble(), 2.0*large.ToDouble(), 0.01); + } + void test_Normalize() { CFixedVector2D v0 (fixed::FromInt(0), fixed::FromInt(0)); Index: source/simulation2/components/CCmpObstructionManager.cpp =================================================================== --- source/simulation2/components/CCmpObstructionManager.cpp +++ source/simulation2/components/CCmpObstructionManager.cpp @@ -21,6 +21,7 @@ #include "ICmpObstructionManager.h" #include "ICmpTerrain.h" +#include "ICmpPosition.h" #include "simulation2/MessageTypes.h" #include "simulation2/helpers/Geometry.h" @@ -464,6 +465,11 @@ } } + virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange); + virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange); + 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); + bool AreShapesInRange(const ObstructionSquare& source, const ObstructionSquare& target, entity_pos_t minRange, entity_pos_t maxRange); + 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* out) const; virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector* out) const; @@ -653,6 +659,124 @@ REGISTER_COMPONENT_TYPE(ObstructionManager) +// Is In Range family of functions. Those either end up in IsPointInPointRange or AreShapesInRange + +bool CCmpObstructionManager::IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) +{ + CmpPtr cmpPosition(GetSimContext(), ent); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return false; + + ObstructionSquare obstruction; + CmpPtr cmpObstruction(GetSimContext(), ent); + if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction)) + return IsPointInPointRange(cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y, px, pz, minRange, maxRange); + + ObstructionSquare point; + point.x = px; + point.z = pz; + return AreShapesInRange(obstruction, point, minRange, maxRange); +} + +bool CCmpObstructionManager::IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) +{ + CmpPtr cmpPosition(GetSimContext(), ent); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return false; + + ObstructionSquare obstruction; + CmpPtr cmpObstruction(GetSimContext(), ent); + if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction)) + return IsInPointRange(target, cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y, minRange, maxRange); + + CmpPtr cmpPositionTarget(GetSimContext(), target); + if (!cmpPositionTarget || !cmpPositionTarget->IsInWorld()) + return false; + + ObstructionSquare target_obstruction; + CmpPtr cmpObstructionTarget(GetSimContext(), target); + if (!cmpObstructionTarget || !cmpObstructionTarget->GetObstructionSquare(target_obstruction)) + return IsInPointRange(ent, cmpPositionTarget->GetPosition2D().X, cmpPositionTarget->GetPosition2D().Y, minRange, maxRange); + + return AreShapesInRange(obstruction, target_obstruction, minRange, maxRange); +} + +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) +{ + // In this function, we will give about a navcell worth of leeway to avoid weirdness + fixed navcellFix = Pathfinding::NAVCELL_SIZE * 3 / 2; // A little above √2, it's not important. + + // We compare squares for performance + entity_pos_t distanceSq = (CFixedVector2D(x, z) - CFixedVector2D(px, pz)).LengthSquared(); + if (distanceSq > (maxRange + navcellFix).Square() || (distanceSq < (minRange - navcellFix).Square() && minRange - navcellFix > fixed::Zero())) + return false; + return true; +} + +bool CCmpObstructionManager::AreShapesInRange(const ObstructionSquare& source, const ObstructionSquare& target, entity_pos_t minRange, entity_pos_t maxRange) +{ + // In this function, we will give about a navcell worth of leeway to avoid weirdness + fixed navcellFix = Pathfinding::NAVCELL_SIZE * 3 / 2; // A little above √2, it's not important. + + // We compare squares for performance + if (source.hh == fixed::Zero() && target.hh == fixed::Zero()) + { + // Sphere-sphere collision. + // Source is in range if the edge to edge distance is inferior to maxRange + // and the opposite edge to opposite edge distance is bigger than minRange. + // TODO: figure out whether we actually want that + fixed distanceSq = (CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z)).LengthSquared(); + if (distanceSq > (maxRange + source.hw + target.hw + navcellFix).Square() || + (distanceSq < (minRange - source.hw - target.hw - navcellFix).Square() && + minRange - source.hw - target.hw - navcellFix > fixed::Zero())) + return false; + return true; + } + else if (source.hh != fixed::Zero() && target.hh != fixed::Zero()) + { + // Square to square. + if (Geometry::DistanceSquareToSquareSquared( + 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)) > (maxRange + navcellFix).Square() || + (Geometry::MaxDistanceSquareToSquareSquared( + 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)) < (minRange - navcellFix).Square() && + minRange - navcellFix > fixed::Zero())) + return false; + return true; + } + else + { + // 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; + + // In range if the edge to edge distance is inferior to maxRange + // and if the opposite edge to opposite edge distance is more than minRange. + // This 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). + // 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. + // I don't really see an easy way to fix this tbh. Depending on the case, the JS code should call + // IsPointInTargetRange with the center/correct position (so the real distance is counted) + // or just add the min(hw,hh) to the minRange of the building. + // We could instead of using MaxDistanceToSquareSquared, simply use DistanceToSquareSquared so we + // count the minDistance from the point closest by. + + if (Geometry::DistanceToSquareSquared( + CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z), + a.u, a.v, CFixedVector2D(a.hw, a.hh), true) > (maxRange + b.hw + navcellFix).Square() || + (Geometry::MaxDistanceToSquareSquared( + CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z), + a.u, a.v, CFixedVector2D(a.hw, a.hh)) < (minRange - b.hw - navcellFix).Square() && + minRange - b.hw - navcellFix > fixed::Zero())) + return false; + return true; + } +} + 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"); Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -494,9 +494,7 @@ } virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange); - virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const; virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange); - virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const; virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z); virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z); @@ -1455,49 +1453,6 @@ return true; } -bool CCmpUnitMotion::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const -{ - CmpPtr cmpPosition(GetEntityHandle()); - if (!cmpPosition || !cmpPosition->IsInWorld()) - return false; - - CFixedVector2D pos = cmpPosition->GetPosition2D(); - - bool hasObstruction = false; - CmpPtr cmpObstructionManager(GetSystemEntity()); - ICmpObstructionManager::ObstructionSquare obstruction; -//TODO if (cmpObstructionManager) -// hasObstruction = cmpObstructionManager->FindMostImportantObstruction(GetObstructionFilter(), x, z, m_Radius, obstruction); - - if (minRange.IsZero() && maxRange.IsZero() && hasObstruction) - { - // Handle the non-ranged mode: - CFixedVector2D halfSize(obstruction.hw, obstruction.hh); - entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); - - // See if we're too close to the target square - if (distance < minRange) - return false; - - // See if we're close enough to the target square - if (maxRange < entity_pos_t::Zero() || distance <= maxRange) - return true; - - return false; - } - else - { - entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length(); - - if (distance < minRange) - return false; - else if (maxRange >= entity_pos_t::Zero() && distance > maxRange) - return false; - else - return true; - } -} - bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const { // Given a square, plus a target range we should reach, the shape at that distance @@ -1668,76 +1623,6 @@ return true; } -bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const -{ - // This function closely mirrors MoveToTargetRange - it needs to return true - // after that Move has completed - - CmpPtr cmpPosition(GetEntityHandle()); - if (!cmpPosition || !cmpPosition->IsInWorld()) - return false; - - CFixedVector2D pos = cmpPosition->GetPosition2D(); - - CmpPtr cmpObstructionManager(GetSystemEntity()); - if (!cmpObstructionManager) - return false; - - bool hasObstruction = false; - ICmpObstructionManager::ObstructionSquare obstruction; - CmpPtr cmpObstruction(GetSimContext(), target); - if (cmpObstruction) - hasObstruction = cmpObstruction->GetObstructionSquare(obstruction); - - if (hasObstruction) - { - CFixedVector2D halfSize(obstruction.hw, obstruction.hh); - entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true); - - // Compare with previous obstruction - ICmpObstructionManager::ObstructionSquare previousObstruction; - cmpObstruction->GetPreviousObstructionSquare(previousObstruction); - entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true); - - // See if we're too close to the target square - bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero(); - if ((distance < minRange && previousDistance < minRange) || inside) - return false; - - // See if we're close enough to the target square - if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange) - return true; - - entity_pos_t circleRadius = halfSize.Length(); - - if (ShouldTreatTargetAsCircle(maxRange, circleRadius)) - { - // The target is small relative to our range, so pretend it's a circle - // and see if we're close enough to that. - // Also check circle around previous position. - entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius; - entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; - - return circleDistance <= maxRange || previousCircleDistance <= maxRange; - } - - // take minimal clearance required in MoveToTargetRange into account, multiplying by 3/2 for diagonals - entity_pos_t maxDist = std::max(maxRange, (m_Clearance + entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/16)*3/2); - return distance <= maxDist || previousDistance <= maxDist; - } - else - { - CmpPtr cmpTargetPosition(GetSimContext(), target); - if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) - return false; - - CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D(); - entity_pos_t distance = (pos - targetPos).Length(); - - return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange); - } -} - void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) { CmpPtr cmpPosition(GetSimContext(), target); Index: source/simulation2/components/ICmpObstructionManager.h =================================================================== --- source/simulation2/components/ICmpObstructionManager.h +++ source/simulation2/components/ICmpObstructionManager.h @@ -159,6 +159,21 @@ virtual void RemoveShape(tag_t tag) = 0; /** + * Check if the given point is in range of the other point given those parameters + */ + 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) = 0; + + /** + * Check if the given entity is in range of the other point given those parameters + */ + virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) = 0; + + /** + * Check if the given entity is in range of the target given those parameters + */ + virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0; + + /** * Collision test a flat-ended thick line against the current set of shapes. * The line caps extend by @p r beyond the end points. * Only intersections going from outside to inside a shape are counted. Index: source/simulation2/components/ICmpObstructionManager.cpp =================================================================== --- source/simulation2/components/ICmpObstructionManager.cpp +++ source/simulation2/components/ICmpObstructionManager.cpp @@ -24,4 +24,6 @@ BEGIN_INTERFACE_WRAPPER(ObstructionManager) DEFINE_INTERFACE_METHOD_1("SetPassabilityCircular", void, ICmpObstructionManager, SetPassabilityCircular, bool) DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpObstructionManager, SetDebugOverlay, bool) +DEFINE_INTERFACE_METHOD_5("IsInPointRange", bool, ICmpObstructionManager, IsInPointRange, entity_id_t, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t) +DEFINE_INTERFACE_METHOD_4("IsInTargetRange", bool, ICmpObstructionManager, IsInTargetRange, entity_id_t, entity_id_t, entity_pos_t, entity_pos_t) END_INTERFACE_WRAPPER(ObstructionManager) Index: source/simulation2/components/ICmpUnitMotion.h =================================================================== --- source/simulation2/components/ICmpUnitMotion.h +++ source/simulation2/components/ICmpUnitMotion.h @@ -47,18 +47,6 @@ virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0; /** - * Determine wether the givven point is within the given range, using the same measurement - * as MoveToPointRange. - */ - virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const = 0; - - /** - * Determine whether the target is within the given range, using the same measurement - * as MoveToTargetRange. - */ - virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const = 0; - - /** * Attempt to walk into range of a given target entity, or as close as possible. * The range is measured between approximately the edges of the unit and the target, so that * maxRange=0 is not unreachably close to the target. Index: source/simulation2/components/ICmpUnitMotion.cpp =================================================================== --- source/simulation2/components/ICmpUnitMotion.cpp +++ source/simulation2/components/ICmpUnitMotion.cpp @@ -24,8 +24,6 @@ BEGIN_INTERFACE_WRAPPER(UnitMotion) DEFINE_INTERFACE_METHOD_4("MoveToPointRange", bool, ICmpUnitMotion, MoveToPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t) -DEFINE_INTERFACE_METHOD_CONST_4("IsInPointRange", bool, ICmpUnitMotion, IsInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t) -DEFINE_INTERFACE_METHOD_CONST_3("IsInTargetRange", bool, ICmpUnitMotion, IsInTargetRange, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_3("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_2("FaceTowardsPoint", void, ICmpUnitMotion, FaceTowardsPoint, entity_pos_t, entity_pos_t) @@ -51,16 +49,6 @@ return m_Script.Call("MoveToPointRange", x, z, minRange, maxRange); } - virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const - { - return m_Script.Call("IsInPointRange", x, z, minRange, maxRange); - } - - virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const - { - return m_Script.Call("IsInTargetRange", target, minRange, maxRange); - } - virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) { return m_Script.Call("MoveToTargetRange", target, minRange, maxRange); Index: source/simulation2/helpers/Geometry.h =================================================================== --- source/simulation2/helpers/Geometry.h +++ source/simulation2/helpers/Geometry.h @@ -91,6 +91,51 @@ CFixedVector2D NearestPointOnSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); +/** + * Returns the shortest squared distance between two squares. + */ +fixed DistanceSquareToSquareSquared(const CFixedVector2D& relativePos, + const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, + const CFixedVector2D& u2,const CFixedVector2D& v2, const CFixedVector2D& halfSize2); +/** + * Similar to the above, only take the sqrt. + */ +fixed DistanceSquareToSquare(const CFixedVector2D& relativePos, + const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, + const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2); + +/** + * Returns the greatest straight line distance from a point to a square. + * + * If @p countInsideAsZero is true, and the point is inside the rectangle, + * it will return 0. + * If @p countInsideAsZero is false, the greatest (positive) distance to the boundary + * will be returned regardless of where the point is. + */ +fixed MaxDistanceToSquareSquared(const CFixedVector2D& point, + const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize, + bool countInsideAsZero = false); + +/** + * Similar to the above, only take the sqrt. + */ +fixed MaxDistanceToSquare(const CFixedVector2D& point, + const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize, + bool countInsideAsZero = false); + +/** + * Return the greatest straight line distance between two squares. + */ +fixed MaxDistanceSquareToSquareSquared(const CFixedVector2D& relativePos, + const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, + const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2); +/** + * Similar to the above, only take the sqrt. + */ +fixed MaxDistanceSquareToSquare(const CFixedVector2D& relativePos, + const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, + const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2); + bool TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); bool TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& halfSize); Index: source/simulation2/helpers/Geometry.cpp =================================================================== --- source/simulation2/helpers/Geometry.cpp +++ source/simulation2/helpers/Geometry.cpp @@ -186,6 +186,82 @@ } } +fixed Geometry::DistanceSquareToSquareSquared(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2) +{ + /* + * The shortest distance between two squared equals the distance between a corner + * and other square. Thus calculating all 8 those distances and taking the smallest. + */ + fixed hw1 = halfSize1.X; + fixed hh1 = halfSize1.Y; + fixed hw2 = halfSize2.X; + fixed hh2 = halfSize2.Y; + + return std::min(std::min(std::min( + DistanceToSquareSquared(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true), + DistanceToSquareSquared(relativePos + u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true)), + std::min( + DistanceToSquareSquared(relativePos - u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true), + DistanceToSquareSquared(relativePos - u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true))), + std::min(std::min( + DistanceToSquareSquared(relativePos + u2.Multiply(hw2) + v2.Multiply(hh2), u1, v1, halfSize1, true), + DistanceToSquareSquared(relativePos + u2.Multiply(hw2) - v2.Multiply(hh2), u1, v1, halfSize1, true)), + std::min( + DistanceToSquareSquared(relativePos + u2.Multiply(hw2) + v2.Multiply(hh2), u1, v1, halfSize1, true), + DistanceToSquareSquared(relativePos + u2.Multiply(hw2) - v2.Multiply(hh2), u1, v1, halfSize1, true)))); +} + +fixed Geometry::DistanceSquareToSquare(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2) +{ + return DistanceSquareToSquareSquared(relativePos, u1, v1, halfSize1, u2, v2, halfSize2).Sqrt(); +} + +fixed Geometry::MaxDistanceToSquareSquared(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize, bool countInsideAsZero) +{ + fixed hw = halfSize.X; + fixed hh = halfSize.Y; + + if (point.Dot(u).Absolute() < hw && point.Dot(v).Absolute() < hh && countInsideAsZero) + return fixed::Zero(); + + /* + * The maximum distance from a point to a square equals the greatest distance + * from the point to the a corner. Thus calculating all and taking the greatest. + */ + return std::max(std::max( + (point + u.Multiply(hw) + v.Multiply(hh)).LengthSquared(), + (point + u.Multiply(hw) - v.Multiply(hh)).LengthSquared()), + std::max( + (point - u.Multiply(hw) + v.Multiply(hh)).LengthSquared(), + (point - u.Multiply(hw) - v.Multiply(hh)).LengthSquared())); +} + +fixed Geometry::MaxDistanceToSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize, bool countInsideAsZero) +{ + return MaxDistanceToSquareSquared(point, u, v, halfSize, countInsideAsZero).Sqrt(); +} + +fixed Geometry::MaxDistanceSquareToSquareSquared(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2) +{ + /* + * The maximum distance from an edge of a square to the edge of another square + * equals the greatest distance from the any of the 16 corner corner distances. + */ + fixed hw1 = halfSize1.X; + fixed hh1 = halfSize1.Y; + + return std::max(std::max( + MaxDistanceToSquareSquared(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true), + MaxDistanceToSquareSquared(relativePos + u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true)), + std::max(MaxDistanceToSquareSquared(relativePos - u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true), + MaxDistanceToSquareSquared(relativePos - u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true))); +} + +fixed Geometry::MaxDistanceSquareToSquare(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2) +{ + return MaxDistanceSquareToSquareSquared(relativePos, u1, v1, halfSize1, u2, v2, halfSize2).Sqrt(); +} + bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) { /*