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 @@ -4147,32 +4147,61 @@ */ UnitAI.prototype.FindNearestDropsite = function(genericType) { - var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); if (!cmpOwnership || cmpOwnership.GetOwner() == -1) return undefined; - // Find dropsites owned by this unit's player or allied ones if allowed - var owner = cmpOwnership.GetOwner(); - var players = [owner]; - var cmpPlayer = QueryOwnerInterface(this.entity); + let cmpPosition = Engine.QueryInterface(this.entity, IID_Position) + if (!cmpPosition) + return undefined; + + let pos = cmpPosition.GetPosition2D(); + let bestEnt = undefined; + let bestDist = Infinity; + // Maximum distance a point on an obstruction can be from the center of the obstruction. + let maxDifference = 40; + + // Find dropsites owned by this unit's player or allied ones if allowed. + let owner = cmpOwnership.GetOwner(); + let players = [owner]; + let cmpPlayer = QueryOwnerInterface(this.entity); if (cmpPlayer && cmpPlayer.HasSharedDropsites()) players = cmpPlayer.GetMutualAllies(); - var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); - var nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite); + let nearby = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite); // Ships are unable to reach land dropsites and shouldn't attempt to do so. - var excludeLand = Engine.QueryInterface(this.entity, IID_Identity).HasClass("Ship"); - if (excludeLand) - nearby = nearby.filter(e => Engine.QueryInterface(e, IID_Identity).HasClass("Naval")); + if (Engine.QueryInterface(this.entity, IID_Identity).HasClass("Ship")) + nearby = nearby.filter(ent => Engine.QueryInterface(ent, IID_Identity).HasClass("Naval")); - return nearby.find(ent => { + for (let ent of nearby) + { let cmpResourceDropsite = Engine.QueryInterface(ent, IID_ResourceDropsite); if (!cmpResourceDropsite.AcceptsType(genericType)) - return false; + continue; + let cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); - return cmpOwnership.GetOwner() == owner || cmpResourceDropsite.IsShared(); - }); + if (cmpOwnership.GetOwner() != owner && !cmpResourceDropsite.IsShared()) + continue; + + // The range manager sorts entities by the distance to their center, + // but we want the distance to the point where we'll drop off resources. + let dist; + let cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction) + if (cmpObstruction) + dist = cmpObstruction.NearestPoint(pos.x, pos.y).distanceTo(pos); + else + dist = Engine.QueryInterface(ent, IID_Position).GetPosition2D().distanceTo(pos); + + if (dist < bestDist) + { + bestEnt = ent; + bestDist = dist; + } + else if (dist > bestDist + maxDifference) + break; + } + return bestEnt; }; /** Index: source/simulation2/components/CCmpObstruction.cpp =================================================================== --- source/simulation2/components/CCmpObstruction.cpp +++ source/simulation2/components/CCmpObstruction.cpp @@ -24,6 +24,7 @@ #include "simulation2/MessageTypes.h" #include "simulation2/components/ICmpObstructionManager.h" #include "simulation2/components/ICmpUnitMotion.h" +#include "simulation2/helpers/Geometry.h" #include "simulation2/serialization/SerializeTemplates.h" /** @@ -493,6 +494,15 @@ return true; } + virtual CFixedVector2D NearestPoint(entity_pos_t x, entity_pos_t z) const + { + ICmpObstructionManager::ObstructionSquare s; + if (!GetObstructionSquare(s)) + return CFixedVector2D(fixed::FromInt(-1), fixed::FromInt(-1)); + + return CFixedVector2D(s.x, s.z) + Geometry::NearestPointOnSquare(CFixedVector2D(x - s.x, z - s.z), s.u, s.v, CFixedVector2D(s.hw, s.hh)); + } + virtual entity_pos_t GetUnitRadius() const { if (m_Type == UNIT) Index: source/simulation2/components/ICmpObstruction.h =================================================================== --- source/simulation2/components/ICmpObstruction.h +++ source/simulation2/components/ICmpObstruction.h @@ -52,6 +52,11 @@ */ virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) const = 0; + /** + * Returns the nearest point to (x, z) on the obstruction square. + */ + virtual CFixedVector2D NearestPoint(entity_pos_t x, entity_pos_t z) const = 0; + virtual entity_pos_t GetSize() const = 0; virtual entity_pos_t GetUnitRadius() const = 0; Index: source/simulation2/components/ICmpObstruction.cpp =================================================================== --- source/simulation2/components/ICmpObstruction.cpp +++ source/simulation2/components/ICmpObstruction.cpp @@ -46,6 +46,7 @@ } BEGIN_INTERFACE_WRAPPER(Obstruction) +DEFINE_INTERFACE_METHOD_CONST_2("NearestPoint", CFixedVector2D, ICmpObstruction, NearestPoint, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_CONST_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius) DEFINE_INTERFACE_METHOD_CONST_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool) DEFINE_INTERFACE_METHOD_CONST_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)