diff --git a/source/simulation2/components/CCmpObstruction.cpp b/source/simulation2/components/CCmpObstruction.cpp index ea6a621b95..c7f189d324 100644 --- a/source/simulation2/components/CCmpObstruction.cpp +++ b/source/simulation2/components/CCmpObstruction.cpp @@ -517,6 +517,38 @@ public: return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass); } + virtual bool CheckDuplicateFoundation() + { + CmpPtr cmpPosition(GetSimContext(), GetEntityId()); + if (!cmpPosition) + return false; // error + + if (!cmpPosition->IsInWorld()) + return false; // no obstruction + + CFixedVector2D pos = cmpPosition->GetPosition2D(); + + CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); + if (!cmpObstructionManager) + return false; // error + + // required precondition to use SkipControlGroupsRequireFlagObstructionFilter + if (m_ControlGroup == INVALID_ENTITY) + { + LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid"); + return false; + } + + // Ignore collisions with entities unless they block foundations and match both control groups. + SkipTagRequireControlGroupsAndFlagObstructionFilter filter(m_Tag, m_ControlGroup, m_ControlGroup2, + ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); + + if (m_Type == UNIT) + return !cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Size0, NULL); + else + return !cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, NULL); + } + virtual std::vector GetConstructionCollisions() { std::vector ret; diff --git a/source/simulation2/components/ICmpObstruction.cpp b/source/simulation2/components/ICmpObstruction.cpp index 62780c381b..cad9d09f7b 100644 --- a/source/simulation2/components/ICmpObstruction.cpp +++ b/source/simulation2/components/ICmpObstruction.cpp @@ -24,6 +24,7 @@ BEGIN_INTERFACE_WRAPPER(Obstruction) DEFINE_INTERFACE_METHOD_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius) DEFINE_INTERFACE_METHOD_1("CheckFoundation", bool, ICmpObstruction, CheckFoundation, std::string) +DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation) DEFINE_INTERFACE_METHOD_0("GetConstructionCollisions", std::vector, ICmpObstruction, GetConstructionCollisions) DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool) DEFINE_INTERFACE_METHOD_3("SetDisableBlockMovementPathfinding", void, ICmpObstruction, SetDisableBlockMovementPathfinding, bool, bool, int32_t) diff --git a/source/simulation2/components/ICmpObstruction.h b/source/simulation2/components/ICmpObstruction.h index d97f2519c2..e08485eaef 100644 --- a/source/simulation2/components/ICmpObstruction.h +++ b/source/simulation2/components/ICmpObstruction.h @@ -51,6 +51,13 @@ public: */ virtual bool CheckFoundation(std::string className) = 0; + /** + * Test whether this entity is colliding with any obstructions that share its + * control groups and block the creation of foundations. + * @return true if foundation is valid (not obstructed) + */ + virtual bool CheckDuplicateFoundation() = 0; + /** * Returns a list of entities that are colliding with this entity, and that * are set to block construction. diff --git a/source/simulation2/components/ICmpObstructionManager.h b/source/simulation2/components/ICmpObstructionManager.h index d2a30899a9..aa7d7922d1 100644 --- a/source/simulation2/components/ICmpObstructionManager.h +++ b/source/simulation2/components/ICmpObstructionManager.h @@ -397,6 +397,42 @@ public: } }; +/** + * Obstruction test filter that will test only against shapes that: + * - are part of both of the specified control groups + * - AND have at least one of the specified flags set. + * + * The first (primary) control group to include shapes from must be specified and valid. + * + * This filter is useful for preventing entities with identical control groups + * from colliding e.g. building a new wall segment on top of an existing wall) + * + * @todo This filter needs test cases. + */ +class SkipTagRequireControlGroupsAndFlagObstructionFilter : public IObstructionTestFilter +{ + bool m_Exclude; + tag_t m_Tag; + entity_id_t m_Group; + entity_id_t m_Group2; + flags_t m_Mask; + +public: + SkipTagRequireControlGroupsAndFlagObstructionFilter(tag_t tag, entity_id_t group1, entity_id_t group2, flags_t mask) : + m_Tag(tag), m_Group(group1), m_Group2(group2), m_Mask(mask) + { + ENSURE(m_Group != INVALID_ENTITY); + } + + virtual bool TestShape(tag_t tag, flags_t flags, entity_id_t group, entity_id_t group2) const + { + // To be included in testing, a shape must not have the specified tag, and must + // match at least one of the flags in m_Mask, as well as both control groups. + return (tag.n != m_Tag.n && (flags & m_Mask) != 0 && ((group == m_Group + && group2 == m_Group2) || (group2 == m_Group && group == m_Group2))); + } +}; + /** * Obstruction test filter that will test only against shapes that do not have the specified tag set. */