Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -4146,32 +4146,55 @@ */ 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); - 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 cmpPosition = Engine.QueryInterface(this.entity, IID_Position) + if (!cmpPosition) + return undefined; - // 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")); + let pos = cmpPosition.GetPosition2D(); + let bestDropsite; + 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 cmpPlayer = QueryOwnerInterface(this.entity); + let players = cmpPlayer && cmpPlayer.HasSharedDropsites() ? cmpPlayer.GetMutualAllies() : [owner]; + let nearbyDropsites = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite); + + let isShip = Engine.QueryInterface(this.entity, IID_Identity).HasClass("Ship"); + let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); + for (let dropsite of nearbyDropsites) + { + // Ships are unable to reach land dropsites and shouldn't attempt to do so. + if (isShip && !Engine.QueryInterface(dropsite, IID_Identity).HasClass("Naval")) + continue; - return nearby.find(ent => { - let cmpResourceDropsite = Engine.QueryInterface(ent, IID_ResourceDropsite); + let cmpResourceDropsite = Engine.QueryInterface(dropsite, IID_ResourceDropsite); if (!cmpResourceDropsite.AcceptsType(genericType)) - return false; - let cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); - return cmpOwnership.GetOwner() == owner || cmpResourceDropsite.IsShared(); - }); + continue; + if (Engine.QueryInterface(dropsite, IID_Ownership).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 resources will be dropped off. + let dist = cmpObstructionManager.DistanceToPoint(dropsite, pos.x, pos.y); + if (dist == -1) + continue; + + if (dist < bestDist) + { + bestDropsite = dropsite; + bestDist = dist; + } + else if (dist > bestDist + maxDifference) + break; + } + return bestDropsite; }; /** Index: ps/trunk/source/simulation2/components/CCmpObstructionManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpObstructionManager.cpp +++ ps/trunk/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,8 @@ } } + virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) 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* 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 +656,20 @@ REGISTER_COMPONENT_TYPE(ObstructionManager) +fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const +{ + CmpPtr cmpPosition(GetSimContext(), ent); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return fixed::FromInt(-1); + + ObstructionSquare s; + CmpPtr cmpObstruction(GetSimContext(), ent); + if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(s)) + return (CFixedVector2D(px, pz) - cmpPosition->GetPosition2D()).Length(); + + return Geometry::DistanceToSquare(CFixedVector2D(px - s.x, pz - s.z), s.u, s.v, CFixedVector2D(s.hw, s.hh)); +} + 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: ps/trunk/source/simulation2/components/ICmpObstructionManager.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpObstructionManager.h +++ ps/trunk/source/simulation2/components/ICmpObstructionManager.h @@ -159,6 +159,11 @@ virtual void RemoveShape(tag_t tag) = 0; /** + * Returns the distance from the obstruction to the point (px, pz), or -1 if the entity is out of the world. + */ + virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const = 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: ps/trunk/source/simulation2/components/ICmpObstructionManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpObstructionManager.cpp +++ ps/trunk/source/simulation2/components/ICmpObstructionManager.cpp @@ -23,5 +23,6 @@ BEGIN_INTERFACE_WRAPPER(ObstructionManager) DEFINE_INTERFACE_METHOD_1("SetPassabilityCircular", void, ICmpObstructionManager, SetPassabilityCircular, bool) +DEFINE_INTERFACE_METHOD_CONST_3("DistanceToPoint", fixed, ICmpObstructionManager, DistanceToPoint, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpObstructionManager, SetDebugOverlay, bool) END_INTERFACE_WRAPPER(ObstructionManager)