Index: ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp +++ ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp @@ -717,7 +717,17 @@ /** * Returns an appropriate obstruction filter for use with path requests. */ - ControlGroupMovementObstructionFilter GetObstructionFilter() const; + ControlGroupMovementObstructionFilter GetObstructionFilter() const + { + return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), GetGroup()); + } + /** + * Filter a specific tag on top of the existing control groups. + */ + SkipMovingTagAndControlGroupObstructionFilter GetObstructionFilter(const ICmpObstructionManager::tag_t& tag) const + { + return SkipMovingTagAndControlGroupObstructionFilter(tag, GetGroup()); + } /** * Decide whether to approximate the given range from a square target as a circle, @@ -1009,6 +1019,14 @@ fixed timeLeft = dt; fixed zero = fixed::Zero(); + ICmpObstructionManager::tag_t specificIgnore; + if (m_MoveRequest.m_Type == MoveRequest::ENTITY) + { + CmpPtr cmpTargetObstruction(GetSimContext(), m_MoveRequest.m_Entity); + if (cmpTargetObstruction) + specificIgnore = cmpTargetObstruction->GetObstruction(); + } + while (timeLeft > zero) { // If we ran out of path, we have to stop. @@ -1057,7 +1075,7 @@ fixed offsetLength = offset.Length(); if (offsetLength <= maxdist) { - if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) + if (cmpPathfinder->CheckMovement(GetObstructionFilter(specificIgnore), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) { pos = target; @@ -1083,7 +1101,7 @@ offset.Normalize(maxdist); target = pos + offset; - if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) + if (cmpPathfinder->CheckMovement(GetObstructionFilter(specificIgnore), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) pos = target; else return true; @@ -1225,15 +1243,33 @@ CmpPtr cmpUnitMotion(GetSimContext(), moveRequest.m_Entity); if (cmpUnitMotion && cmpUnitMotion->IsMoveRequested() && GetEntityId() < moveRequest.m_Entity) { + // Add predicted movement. + CFixedVector2D tempPos = out + (out - cmpTargetPosition->GetPreviousPosition2D()); + CmpPtr cmpPosition(GetEntityHandle()); if (!cmpPosition || !cmpPosition->IsInWorld()) return true; // Still return true since we don't need a position for the target to have one. - CFixedVector2D tempPos = out + (out - cmpTargetPosition->GetPreviousPosition2D()); + // Fleeing fix: if we anticipate the target to go through us, we'll suddenly turn around, which is bad. + // Pretend that the target is still behind us in those cases. + if (m_MoveRequest.m_MinRange > fixed::Zero()) + { + if ((out - cmpPosition->GetPosition2D()).RelativeOrientation(tempPos - cmpPosition->GetPosition2D()) >= 0) + out = tempPos; + } + else + out = tempPos; + } + else if (cmpUnitMotion && cmpUnitMotion->IsMoveRequested() && GetEntityId() > moveRequest.m_Entity) + { + CmpPtr cmpPosition(GetEntityHandle()); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return true; // Still return true since we don't need a position for the target to have one. - // Check if we anticipate the target to go through us, in which case we shouldn't anticipate - // (or e.g. units fleeing might suddenly turn around towards their attacker). - if ((out - cmpPosition->GetPosition2D()).RelativeOrientation(tempPos - cmpPosition->GetPosition2D()) >= 0) + // Fleeing fix: opposite to above, check if our target has travelled through us already this turn. + CFixedVector2D tempPos = out - (out - cmpTargetPosition->GetPreviousPosition2D()); + if (m_MoveRequest.m_MinRange > fixed::Zero() && + (out - cmpPosition->GetPosition2D()).RelativeOrientation(tempPos - cmpPosition->GetPosition2D()) < 0) out = tempPos; } } @@ -1381,11 +1417,6 @@ } } -ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter() const -{ - return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), GetGroup()); -} - // The pathfinder cannot go to "rounded rectangles" goals, which are what happens with square targets and a non-null range. // Depending on what the best approximation is, we either pretend the target is a circle or a square. // One needs to be careful that the approximated geometry will be in the range. Index: ps/trunk/source/simulation2/components/ICmpObstructionManager.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpObstructionManager.h +++ ps/trunk/source/simulation2/components/ICmpObstructionManager.h @@ -530,6 +530,36 @@ }; /** + * Obstruction test filter that reject shapes in a given control group or with the given tag (if that tag is moving), + * and rejects shapes that don't block unit movement. See D3482 for why this exists. + */ +class SkipMovingTagAndControlGroupObstructionFilter : public IObstructionTestFilter +{ + entity_id_t m_Group; + tag_t m_Tag; + +public: + SkipMovingTagAndControlGroupObstructionFilter(tag_t tag, entity_id_t group) : + m_Tag(tag), m_Group(group) + {} + + virtual bool TestShape(tag_t tag, flags_t flags, entity_id_t group, entity_id_t group2) const + { + if (tag.n == m_Tag.n && (flags & ICmpObstructionManager::FLAG_MOVING)) + return false; + + if (group == m_Group || (group2 != INVALID_ENTITY && group2 == m_Group)) + return false; + + if (!(flags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT)) + return false; + + return true; + } +}; + + +/** * Obstruction test filter that will test only against shapes that: * - do not have the specified tag * - AND have at least one of the specified flags set.