LCOV - code coverage report
Current view: top level - source/simulation2/components - CCmpObstruction.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 7 391 1.8 %
Date: 2023-01-19 00:18:29 Functions: 5 50 10.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "simulation2/system/Component.h"
      21             : #include "ICmpObstruction.h"
      22             : 
      23             : #include "simulation2/MessageTypes.h"
      24             : #include "simulation2/components/ICmpObstructionManager.h"
      25             : #include "simulation2/components/ICmpTerrain.h"
      26             : #include "simulation2/components/ICmpUnitMotion.h"
      27             : #include "simulation2/components/ICmpWaterManager.h"
      28             : #include "simulation2/serialization/SerializedTypes.h"
      29             : 
      30             : #include "ps/CLogger.h"
      31             : 
      32             : template<>
      33             : struct SerializeHelper<ICmpObstructionManager::tag_t>
      34             : {
      35             :     template<typename S>
      36           0 :     void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify<S, ICmpObstructionManager::tag_t> value)
      37             :     {
      38           0 :         serialize.NumberU32_Unbounded("tag", value.n);
      39           0 :     }
      40             : };
      41             : 
      42             : /**
      43             :  * Obstruction implementation. This keeps the ICmpPathfinder's model of the world updated when the
      44             :  * entities move and die, with shapes derived from ICmpFootprint.
      45             :  */
      46           0 : class CCmpObstruction final : public ICmpObstruction
      47             : {
      48             : public:
      49         116 :     static void ClassInit(CComponentManager& componentManager)
      50             :     {
      51         116 :         componentManager.SubscribeToMessageType(MT_PositionChanged);
      52         116 :         componentManager.SubscribeToMessageType(MT_Destroy);
      53         116 :     }
      54             : 
      55           0 :     DEFAULT_COMPONENT_ALLOCATOR(Obstruction)
      56             : 
      57             :     typedef ICmpObstructionManager::tag_t tag_t;
      58             :     typedef ICmpObstructionManager::flags_t flags_t;
      59             : 
      60             :     // Template state:
      61             : 
      62             :     EObstructionType m_Type;
      63             : 
      64             :     entity_pos_t m_Size0; // radius or width
      65             :     entity_pos_t m_Size1; // radius or depth
      66             :     flags_t m_TemplateFlags;
      67             :     entity_pos_t m_Clearance;
      68             : 
      69             :     typedef struct {
      70             :         entity_pos_t dx, dz;
      71             :         entity_angle_t da;
      72             :         entity_pos_t size0, size1;
      73             :         flags_t flags;
      74           0 :     } Shape;
      75             : 
      76             :     std::vector<Shape> m_Shapes;
      77             : 
      78             :     // Dynamic state:
      79             : 
      80             :     /// Whether the obstruction is actively obstructing or just an inactive placeholder.
      81             :     bool m_Active;
      82             :     /// Whether the entity associated with this obstruction is currently moving. Only applicable for
      83             :     /// UNIT-type obstructions.
      84             :     bool m_Moving;
      85             :     /// Whether an obstruction's control group should be kept consistent and
      86             :     /// used to set control groups for entities that collide with it.
      87             :     bool m_ControlPersist;
      88             : 
      89             :     // WORKAROUND: While processing Destroy messages, the obstruction component may receive messages
      90             :     // that make it re-enable the obstruction, thus leaving behind dangling obstructions.
      91             :     // To avoid that, if this is true, _never_ reactivate the obstruction.
      92             :     bool m_IsDestroyed = false;
      93             : 
      94             :     /**
      95             :      * Primary control group identifier. Indicates to which control group this entity's shape belongs.
      96             :      * Typically used in combination with obstruction test filters to have member shapes ignore each
      97             :      * other during obstruction tests. Defaults to the entity's ID. Must never be set to INVALID_ENTITY.
      98             :      */
      99             :     entity_id_t m_ControlGroup;
     100             : 
     101             :     /**
     102             :      * Optional secondary control group identifier. Similar to m_ControlGroup; if set to a valid value,
     103             :      * then this field identifies an additional, secondary control group to which this entity's shape
     104             :      * belongs. Set to INVALID_ENTITY to not assign any secondary group. Defaults to INVALID_ENTITY.
     105             :      *
     106             :      * These are only necessary in case it is not sufficient for an entity to belong to only one control
     107             :      * group. Otherwise, they can be ignored.
     108             :      */
     109             :     entity_id_t m_ControlGroup2;
     110             : 
     111             :     /// Identifier of this entity's obstruction shape, as registered in the obstruction manager. Contains
     112             :     /// structure, but should be treated as opaque here.
     113             :     tag_t m_Tag;
     114             :     std::vector<tag_t> m_ClusterTags;
     115             : 
     116             :     /// Set of flags affecting the behaviour of this entity's obstruction shape.
     117             :     flags_t m_Flags;
     118             : 
     119         116 :     static std::string GetSchema()
     120             :     {
     121             :         return
     122             :             "<a:example/>"
     123             :             "<a:help>Causes this entity to obstruct the motion of other units.</a:help>"
     124             :             "<choice>"
     125             :                 "<element name='Static'>"
     126             :                     "<attribute name='width'>"
     127             :                         "<data type='decimal'>"
     128             :                             "<param name='minInclusive'>1.5</param>"
     129             :                         "</data>"
     130             :                     "</attribute>"
     131             :                     "<attribute name='depth'>"
     132             :                         "<data type='decimal'>"
     133             :                             "<param name='minInclusive'>1.5</param>"
     134             :                         "</data>"
     135             :                     "</attribute>"
     136             :                 "</element>"
     137             :                 "<element name='Unit'>"
     138             :                     "<empty/>"
     139             :                 "</element>"
     140             :                 "<element name='Obstructions'>"
     141             :                     "<zeroOrMore>"
     142             :                         "<element>"
     143             :                             "<anyName/>"
     144             :                             "<optional>"
     145             :                                 "<attribute name='x'>"
     146             :                                     "<data type='decimal'/>"
     147             :                                 "</attribute>"
     148             :                             "</optional>"
     149             :                             "<optional>"
     150             :                                 "<attribute name='z'>"
     151             :                                     "<data type='decimal'/>"
     152             :                                 "</attribute>"
     153             :                             "</optional>"
     154             :                             "<attribute name='width'>"
     155             :                                 "<data type='decimal'>"
     156             :                                     "<param name='minInclusive'>1.5</param>"
     157             :                                 "</data>"
     158             :                             "</attribute>"
     159             :                             "<attribute name='depth'>"
     160             :                                 "<data type='decimal'>"
     161             :                                     "<param name='minInclusive'>1.5</param>"
     162             :                                 "</data>"
     163             :                             "</attribute>"
     164             :                         "</element>"
     165             :                     "</zeroOrMore>"
     166             :                 "</element>"
     167             :             "</choice>"
     168             :             "<element name='Active' a:help='If false, this entity will be ignored in collision tests by other units but can still perform its own collision tests'>"
     169             :                 "<data type='boolean'/>"
     170             :             "</element>"
     171             :             "<element name='BlockMovement' a:help='Whether units should be allowed to walk through this entity'>"
     172             :                 "<data type='boolean'/>"
     173             :             "</element>"
     174             :             "<element name='BlockPathfinding' a:help='Whether the long-distance pathfinder should avoid paths through this entity. This should only be set for large stationary obstructions'>"
     175             :                 "<data type='boolean'/>"
     176             :             "</element>"
     177             :             "<element name='BlockFoundation' a:help='Whether players should be unable to place building foundations on top of this entity. If true, BlockConstruction should be true too'>"
     178             :                 "<data type='boolean'/>"
     179             :             "</element>"
     180             :             "<element name='BlockConstruction' a:help='Whether players should be unable to begin constructing buildings placed on top of this entity'>"
     181             :                 "<data type='boolean'/>"
     182             :             "</element>"
     183             :             "<element name='DeleteUponConstruction' a:help='Whether this entity should be deleted when construction on a buildings placed on top of this entity is started.'>"
     184             :                 "<data type='boolean'/>"
     185             :             "</element>"
     186             :             "<element name='DisableBlockMovement' a:help='If true, BlockMovement will be overridden and treated as false. (This is a special case to handle foundations)'>"
     187             :                 "<data type='boolean'/>"
     188             :             "</element>"
     189             :             "<element name='DisableBlockPathfinding' a:help='If true, BlockPathfinding will be overridden and treated as false. (This is a special case to handle foundations)'>"
     190             :                 "<data type='boolean'/>"
     191             :             "</element>"
     192             :             "<optional>"
     193             :                 "<element name='ControlPersist' a:help='If present, the control group of this entity will be given to entities that are colliding with it.'>"
     194             :                     "<empty/>"
     195             :                 "</element>"
     196         116 :             "</optional>";
     197             :     }
     198             : 
     199           0 :     void Init(const CParamNode& paramNode) override
     200             :     {
     201             :         // The minimum obstruction size is the navcell size * sqrt(2)
     202             :         // This is enforced in the schema as a minimum of 1.5
     203           0 :         fixed minObstruction = (Pathfinding::NAVCELL_SIZE.Square() * 2).Sqrt();
     204           0 :         m_TemplateFlags = 0;
     205           0 :         if (paramNode.GetChild("BlockMovement").ToBool())
     206           0 :             m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_MOVEMENT;
     207           0 :         if (paramNode.GetChild("BlockPathfinding").ToBool())
     208           0 :             m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_PATHFINDING;
     209           0 :         if (paramNode.GetChild("BlockFoundation").ToBool())
     210           0 :             m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_FOUNDATION;
     211           0 :         if (paramNode.GetChild("BlockConstruction").ToBool())
     212           0 :             m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION;
     213           0 :         if (paramNode.GetChild("DeleteUponConstruction").ToBool())
     214           0 :             m_TemplateFlags |= ICmpObstructionManager::FLAG_DELETE_UPON_CONSTRUCTION;
     215             : 
     216           0 :         m_Flags = m_TemplateFlags;
     217           0 :         if (paramNode.GetChild("DisableBlockMovement").ToBool())
     218           0 :             m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
     219           0 :         if (paramNode.GetChild("DisableBlockPathfinding").ToBool())
     220           0 :             m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
     221             : 
     222           0 :         if (paramNode.GetChild("Unit").IsOk())
     223             :         {
     224           0 :             m_Type = UNIT;
     225             : 
     226           0 :             CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetEntityHandle());
     227           0 :             if (cmpUnitMotion)
     228           0 :                 m_Clearance = cmpUnitMotion->GetUnitClearance();
     229             :         }
     230           0 :         else if (paramNode.GetChild("Static").IsOk())
     231             :         {
     232           0 :             m_Type = STATIC;
     233           0 :             m_Size0 = paramNode.GetChild("Static").GetChild("@width").ToFixed();
     234           0 :             m_Size1 = paramNode.GetChild("Static").GetChild("@depth").ToFixed();
     235           0 :             ENSURE(m_Size0 > minObstruction);
     236           0 :             ENSURE(m_Size1 > minObstruction);
     237             :         }
     238             :         else
     239             :         {
     240           0 :             m_Type = CLUSTER;
     241           0 :             CFixedVector2D max = CFixedVector2D(fixed::FromInt(0), fixed::FromInt(0));
     242           0 :             CFixedVector2D min = CFixedVector2D(fixed::FromInt(0), fixed::FromInt(0));
     243           0 :             const CParamNode::ChildrenMap& clusterMap = paramNode.GetChild("Obstructions").GetChildren();
     244           0 :             for(CParamNode::ChildrenMap::const_iterator it = clusterMap.begin(); it != clusterMap.end(); ++it)
     245             :             {
     246           0 :                 Shape b;
     247           0 :                 b.size0 = it->second.GetChild("@width").ToFixed();
     248           0 :                 b.size1 = it->second.GetChild("@depth").ToFixed();
     249           0 :                 ENSURE(b.size0 > minObstruction);
     250           0 :                 ENSURE(b.size1 > minObstruction);
     251           0 :                 b.dx = it->second.GetChild("@x").ToFixed();
     252           0 :                 b.dz = it->second.GetChild("@z").ToFixed();
     253           0 :                 b.da = entity_angle_t::FromInt(0);
     254           0 :                 b.flags = m_Flags;
     255           0 :                 m_Shapes.push_back(b);
     256           0 :                 max.X = std::max(max.X, b.dx + b.size0/2);
     257           0 :                 max.Y = std::max(max.Y, b.dz + b.size1/2);
     258           0 :                 min.X = std::min(min.X, b.dx - b.size0/2);
     259           0 :                 min.Y = std::min(min.Y, b.dz - b.size1/2);
     260             :             }
     261           0 :             m_Size0 = fixed::FromInt(2).Multiply(std::max(max.X, -min.X));
     262           0 :             m_Size1 = fixed::FromInt(2).Multiply(std::max(max.Y, -min.Y));
     263             :         }
     264             : 
     265           0 :         m_Active = paramNode.GetChild("Active").ToBool();
     266           0 :         m_ControlPersist = paramNode.GetChild("ControlPersist").IsOk();
     267             : 
     268           0 :         m_Tag = tag_t();
     269           0 :         if (m_Type == CLUSTER)
     270           0 :             m_ClusterTags.clear();
     271           0 :         m_Moving = false;
     272           0 :         m_ControlGroup = GetEntityId();
     273           0 :         m_ControlGroup2 = INVALID_ENTITY;
     274           0 :     }
     275             : 
     276           0 :     void Deinit() override
     277             :     {
     278           0 :     }
     279             : 
     280             :     template<typename S>
     281           0 :     void SerializeCommon(S& serialize)
     282             :     {
     283           0 :         serialize.Bool("active", m_Active);
     284           0 :         serialize.Bool("moving", m_Moving);
     285           0 :         serialize.NumberU32_Unbounded("control group", m_ControlGroup);
     286           0 :         serialize.NumberU32_Unbounded("control group 2", m_ControlGroup2);
     287           0 :         serialize.NumberU32_Unbounded("tag", m_Tag.n);
     288           0 :         serialize.NumberU8_Unbounded("flags", m_Flags);
     289           0 :         if (m_Type == CLUSTER)
     290           0 :             Serializer(serialize, "cluster tags", m_ClusterTags);
     291           0 :         if (m_Type == UNIT)
     292           0 :             serialize.NumberFixed_Unbounded("clearance", m_Clearance);
     293           0 :     }
     294             : 
     295           0 :     void Serialize(ISerializer& serialize) override
     296             :     {
     297           0 :         SerializeCommon(serialize);
     298           0 :     }
     299             : 
     300           0 :     void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override
     301             :     {
     302           0 :         Init(paramNode);
     303             : 
     304           0 :         SerializeCommon(deserialize);
     305           0 :     }
     306             : 
     307           0 :     void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
     308             :     {
     309           0 :         switch (msg.GetType())
     310             :         {
     311           0 :         case MT_PositionChanged:
     312             :         {
     313           0 :             if (!m_Active || m_IsDestroyed)
     314             :                 break;
     315             : 
     316           0 :             const CMessagePositionChanged& data = static_cast<const CMessagePositionChanged&> (msg);
     317             : 
     318           0 :             if (!data.inWorld && !m_Tag.valid())
     319           0 :                 break; // nothing needs to change
     320             : 
     321           0 :             CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     322           0 :             if (!cmpObstructionManager)
     323           0 :                 break; // error
     324             : 
     325           0 :             if (data.inWorld && m_Tag.valid())
     326             :             {
     327           0 :                 cmpObstructionManager->MoveShape(m_Tag, data.x, data.z, data.a);
     328             : 
     329           0 :                 if (m_Type == CLUSTER)
     330             :                 {
     331           0 :                     for (size_t i = 0; i < m_Shapes.size(); ++i)
     332             :                     {
     333           0 :                         Shape& b = m_Shapes[i];
     334           0 :                         fixed s, c;
     335           0 :                         sincos_approx(data.a, s, c);
     336           0 :                         cmpObstructionManager->MoveShape(m_ClusterTags[i], data.x + b.dx.Multiply(c) + b.dz.Multiply(s), data.z + b.dz.Multiply(c) - b.dx.Multiply(s), data.a + b.da);
     337             :                     }
     338             :                 }
     339             :             }
     340           0 :             else if (data.inWorld && !m_Tag.valid())
     341             :             {
     342             :                 // Need to create a new pathfinder shape:
     343           0 :                 if (m_Type == STATIC)
     344           0 :                     m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
     345           0 :                         data.x, data.z, data.a, m_Size0, m_Size1, m_Flags, m_ControlGroup, m_ControlGroup2);
     346           0 :                 else if (m_Type == UNIT)
     347           0 :                     m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(),
     348           0 :                         data.x, data.z, m_Clearance, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup);
     349             :                 else
     350           0 :                     AddClusterShapes(data.x, data.x, data.a);
     351             :             }
     352           0 :             else if (!data.inWorld && m_Tag.valid())
     353             :             {
     354           0 :                 cmpObstructionManager->RemoveShape(m_Tag);
     355           0 :                 m_Tag = tag_t();
     356           0 :                 if(m_Type == CLUSTER)
     357           0 :                     RemoveClusterShapes();
     358             :             }
     359           0 :             break;
     360             :         }
     361           0 :         case MT_Destroy:
     362             :         {
     363             :             // Mark the obstruction as destroyed to prevent reactivating it after this point
     364           0 :             m_IsDestroyed = true;
     365           0 :             m_Active = false;
     366             : 
     367           0 :             if (m_Tag.valid())
     368             :             {
     369           0 :                 CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     370           0 :                 if (!cmpObstructionManager)
     371           0 :                     break; // error
     372             : 
     373           0 :                 cmpObstructionManager->RemoveShape(m_Tag);
     374           0 :                 m_Tag = tag_t();
     375           0 :                 if(m_Type == CLUSTER)
     376           0 :                     RemoveClusterShapes();
     377             :             }
     378           0 :             break;
     379             :         }
     380             :         }
     381           0 :     }
     382             : 
     383           0 :     void SetActive(bool active) override
     384             :     {
     385           0 :         if (active && !m_Active && !m_IsDestroyed)
     386             :         {
     387           0 :             m_Active = true;
     388             : 
     389             :             // Construct the obstruction shape
     390             : 
     391           0 :             CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     392           0 :             if (!cmpObstructionManager)
     393           0 :                 return; // error
     394             : 
     395           0 :             CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     396           0 :             if (!cmpPosition)
     397           0 :                 return; // error
     398             : 
     399           0 :             if (!cmpPosition->IsInWorld())
     400           0 :                 return; // don't need an obstruction
     401             : 
     402             :             // TODO: code duplication from message handlers
     403           0 :             CFixedVector2D pos = cmpPosition->GetPosition2D();
     404           0 :             if (m_Type == STATIC)
     405           0 :                 m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
     406           0 :                     pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, m_Flags, m_ControlGroup, m_ControlGroup2);
     407           0 :             else if (m_Type == UNIT)
     408           0 :                 m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(),
     409           0 :                     pos.X, pos.Y, m_Clearance, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup);
     410             :             else
     411           0 :                 AddClusterShapes(pos.X, pos.Y, cmpPosition->GetRotation().Y);
     412             : 
     413             :             // Used by UnitMotion to activate/deactivate pushing
     414           0 :             if (m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT)
     415             :             {
     416           0 :                 CMessageMovementObstructionChanged msg;
     417           0 :                 GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
     418           0 :             }
     419             :         }
     420           0 :         else if (!active && m_Active)
     421             :         {
     422           0 :             m_Active = false;
     423             : 
     424             :             // Delete the obstruction shape
     425             : 
     426             :             // TODO: code duplication from message handlers
     427           0 :             if (m_Tag.valid())
     428             :             {
     429           0 :                 CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     430           0 :                 if (!cmpObstructionManager)
     431           0 :                     return; // error
     432             : 
     433           0 :                 cmpObstructionManager->RemoveShape(m_Tag);
     434           0 :                 m_Tag = tag_t();
     435           0 :                 if (m_Type == CLUSTER)
     436           0 :                     RemoveClusterShapes();
     437             :             }
     438             : 
     439             :             // Used by UnitMotion to activate/deactivate pushing
     440           0 :             if (m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT)
     441             :             {
     442           0 :                 CMessageMovementObstructionChanged msg;
     443           0 :                 GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
     444             :             }
     445             :         }
     446             :         // else we didn't change the active status
     447             :     }
     448             : 
     449           0 :     void SetDisableBlockMovementPathfinding(bool movementDisabled, bool pathfindingDisabled, int32_t shape) override
     450             :     {
     451           0 :         flags_t *flags = NULL;
     452           0 :         if (shape == -1)
     453           0 :             flags = &m_Flags;
     454           0 :         else if (m_Type == CLUSTER && shape < (int32_t)m_Shapes.size())
     455           0 :             flags = &m_Shapes[shape].flags;
     456             :         else
     457           0 :             return; // error
     458             : 
     459             :         // Remove the blocking / pathfinding flags or
     460             :         // Add the blocking / pathfinding flags if the template had enabled them
     461           0 :         if (movementDisabled)
     462           0 :             *flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
     463             :         else
     464           0 :             *flags |= (flags_t)(m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
     465           0 :         if (pathfindingDisabled)
     466           0 :             *flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
     467             :         else
     468           0 :             *flags |= (flags_t)(m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
     469             : 
     470             :         // Reset the shape with the new flags (kind of inefficiently - we
     471             :         // should have a ICmpObstructionManager::SetFlags function or something)
     472           0 :         if (m_Active)
     473             :         {
     474           0 :             SetActive(false);
     475           0 :             SetActive(true);
     476             :         }
     477             :     }
     478             : 
     479           0 :     bool GetBlockMovementFlag(bool templateOnly) const override
     480             :     {
     481           0 :         return m_Active && ((templateOnly ? m_TemplateFlags : m_Flags) & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT) != 0;
     482             :     }
     483             : 
     484           0 :     EObstructionType GetObstructionType() const override
     485             :     {
     486           0 :         return m_Type;
     487             :     }
     488             : 
     489           0 :     ICmpObstructionManager::tag_t GetObstruction() const override
     490             :     {
     491           0 :         return m_Tag;
     492             :     }
     493             : 
     494           0 :     bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) const override
     495             :     {
     496           0 :         return GetObstructionSquare(out, true);
     497             :     }
     498             : 
     499           0 :     bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) const override
     500             :     {
     501           0 :         return GetObstructionSquare(out, false);
     502             :     }
     503             : 
     504           0 :     virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out, bool previousPosition) const
     505             :     {
     506           0 :         CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     507           0 :         if (!cmpPosition)
     508           0 :             return false; // error
     509             : 
     510           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     511           0 :         if (!cmpObstructionManager)
     512           0 :             return false; // error
     513             : 
     514           0 :         if (!cmpPosition->IsInWorld())
     515           0 :             return false; // no obstruction square
     516             : 
     517           0 :         CFixedVector2D pos;
     518           0 :         if (previousPosition)
     519           0 :             pos = cmpPosition->GetPreviousPosition2D();
     520             :         else
     521           0 :             pos = cmpPosition->GetPosition2D();
     522           0 :         if (m_Type == UNIT)
     523           0 :             out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Clearance);
     524             :         else
     525           0 :             out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
     526           0 :         return true;
     527             :     }
     528             : 
     529           0 :     entity_pos_t GetSize() const override
     530             :     {
     531           0 :         if (m_Type == UNIT)
     532           0 :             return m_Clearance;
     533             :         else
     534           0 :             return CFixedVector2D(m_Size0 / 2, m_Size1 / 2).Length();
     535             :     }
     536             : 
     537           0 :     CFixedVector2D GetStaticSize() const override
     538             :     {
     539           0 :         return m_Type == STATIC ? CFixedVector2D(m_Size0, m_Size1) : CFixedVector2D();
     540             :     }
     541             : 
     542           0 :     void SetUnitClearance(const entity_pos_t& clearance) override
     543             :     {
     544             :         // This doesn't send a MovementObstructionChanged message
     545             :         // because it's a just a workaround init order, and used in UnitMotion directly.
     546           0 :         if (m_Type == UNIT)
     547           0 :             m_Clearance = clearance;
     548           0 :     }
     549             : 
     550           0 :     bool IsControlPersistent() const override
     551             :     {
     552           0 :         return m_ControlPersist;
     553             :     }
     554             : 
     555           0 :     bool CheckShorePlacement() const override
     556             :     {
     557           0 :         ICmpObstructionManager::ObstructionSquare s;
     558           0 :         if (!GetObstructionSquare(s))
     559           0 :             return false;
     560             : 
     561           0 :         CFixedVector2D front = CFixedVector2D(s.x, s.z) + s.v.Multiply(s.hh);
     562           0 :         CFixedVector2D  back = CFixedVector2D(s.x, s.z) - s.v.Multiply(s.hh);
     563             : 
     564           0 :         CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
     565           0 :         CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
     566           0 :         if (!cmpTerrain || !cmpWaterManager)
     567           0 :             return false;
     568             : 
     569             :         // Keep these constants in agreement with the pathfinder.
     570           0 :         return cmpWaterManager->GetWaterLevel(front.X, front.Y) - cmpTerrain->GetGroundLevel(front.X, front.Y) > fixed::FromInt(1) &&
     571           0 :                cmpWaterManager->GetWaterLevel( back.X,  back.Y) - cmpTerrain->GetGroundLevel( back.X,  back.Y) < fixed::FromInt(2);
     572             :     }
     573             : 
     574           0 :     EFoundationCheck CheckFoundation(const std::string& className) const override
     575             :     {
     576           0 :         return  CheckFoundation(className, false);
     577             :     }
     578             : 
     579           0 :     EFoundationCheck CheckFoundation(const std::string& className, bool onlyCenterPoint) const override
     580             :     {
     581           0 :         CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     582           0 :         if (!cmpPosition)
     583           0 :             return FOUNDATION_CHECK_FAIL_ERROR; // error
     584             : 
     585           0 :         if (!cmpPosition->IsInWorld())
     586           0 :             return FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; // no obstruction
     587             : 
     588           0 :         CFixedVector2D pos = cmpPosition->GetPosition2D();
     589             : 
     590           0 :         CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
     591           0 :         if (!cmpPathfinder)
     592           0 :             return FOUNDATION_CHECK_FAIL_ERROR; // error
     593             : 
     594             :         // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
     595           0 :         if (m_ControlGroup == INVALID_ENTITY)
     596             :         {
     597           0 :             LOGERROR("[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid");
     598           0 :             return FOUNDATION_CHECK_FAIL_ERROR;
     599             :         }
     600             : 
     601             :         // Get passability class
     602           0 :         pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className);
     603             : 
     604             :         // Ignore collisions within the same control group, or with other non-foundation-blocking shapes.
     605             :         // Note that, since the control group for each entity defaults to the entity's ID, this is typically
     606             :         // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes.
     607           0 :         SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2,
     608           0 :             ICmpObstructionManager::FLAG_BLOCK_FOUNDATION);
     609             : 
     610           0 :         if (m_Type == UNIT)
     611           0 :             return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Clearance, passClass, onlyCenterPoint);
     612             :         else
     613           0 :             return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass, onlyCenterPoint);
     614             :     }
     615             : 
     616           0 :     bool CheckDuplicateFoundation() const override
     617             :     {
     618           0 :         CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     619           0 :         if (!cmpPosition)
     620           0 :             return false; // error
     621             : 
     622           0 :         if (!cmpPosition->IsInWorld())
     623           0 :             return false; // no obstruction
     624             : 
     625           0 :         CFixedVector2D pos = cmpPosition->GetPosition2D();
     626             : 
     627           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     628           0 :         if (!cmpObstructionManager)
     629           0 :             return false; // error
     630             : 
     631             :         // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
     632           0 :         if (m_ControlGroup == INVALID_ENTITY)
     633             :         {
     634           0 :             LOGERROR("[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid");
     635           0 :             return false;
     636             :         }
     637             : 
     638             :         // Ignore collisions with entities unless they block foundations and match both control groups.
     639           0 :         SkipTagRequireControlGroupsAndFlagObstructionFilter filter(m_Tag, m_ControlGroup, m_ControlGroup2,
     640           0 :             ICmpObstructionManager::FLAG_BLOCK_FOUNDATION);
     641             : 
     642           0 :         if (m_Type == UNIT)
     643           0 :             return !cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Clearance, NULL);
     644             :         else
     645           0 :             return !cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, NULL );
     646             :     }
     647             : 
     648           0 :     std::vector<entity_id_t> GetEntitiesByFlags(flags_t flags) const override
     649             :     {
     650           0 :         std::vector<entity_id_t> ret;
     651             : 
     652           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     653           0 :         if (!cmpObstructionManager)
     654           0 :             return ret; // error
     655             : 
     656             :         // Ignore collisions within the same control group, or with other shapes that don't match the filter.
     657             :         // Note that, since the control group for each entity defaults to the entity's ID, this is typically
     658             :         // equivalent to only ignoring the entity's own shape and other shapes that don't match the filter.
     659           0 :         SkipControlGroupsRequireFlagObstructionFilter filter(false, m_ControlGroup, m_ControlGroup2, flags);
     660             : 
     661           0 :         ICmpObstructionManager::ObstructionSquare square;
     662           0 :         if (!GetObstructionSquare(square))
     663           0 :             return ret; // error
     664             : 
     665           0 :         cmpObstructionManager->GetUnitsOnObstruction(square, ret, filter, false);
     666           0 :         cmpObstructionManager->GetStaticObstructionsOnObstruction(square, ret, filter);
     667             : 
     668           0 :         return ret;
     669             :     }
     670             : 
     671           0 :     std::vector<entity_id_t> GetEntitiesBlockingMovement() const override
     672             :     {
     673           0 :         return GetEntitiesByFlags(ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
     674             :     }
     675             : 
     676           0 :     std::vector<entity_id_t> GetEntitiesBlockingConstruction() const override
     677             :     {
     678           0 :         return GetEntitiesByFlags(ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION);
     679             :     }
     680             : 
     681           0 :     std::vector<entity_id_t> GetEntitiesDeletedUponConstruction() const override
     682             :     {
     683           0 :         return GetEntitiesByFlags(ICmpObstructionManager::FLAG_DELETE_UPON_CONSTRUCTION);
     684             :     }
     685             : 
     686           0 :     void SetMovingFlag(bool enabled) override
     687             :     {
     688           0 :         m_Moving = enabled;
     689             : 
     690           0 :         if (m_Tag.valid() && m_Type == UNIT)
     691             :         {
     692           0 :             CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     693           0 :             if (cmpObstructionManager)
     694           0 :                 cmpObstructionManager->SetUnitMovingFlag(m_Tag, m_Moving);
     695             :         }
     696           0 :     }
     697             : 
     698           0 :     void SetControlGroup(entity_id_t group) override
     699             :     {
     700           0 :         m_ControlGroup = group;
     701           0 :         UpdateControlGroups();
     702           0 :     }
     703             : 
     704           0 :     void SetControlGroup2(entity_id_t group2) override
     705             :     {
     706           0 :         m_ControlGroup2 = group2;
     707           0 :         UpdateControlGroups();
     708           0 :     }
     709             : 
     710           0 :     entity_id_t GetControlGroup() const override
     711             :     {
     712           0 :         return m_ControlGroup;
     713             :     }
     714             : 
     715           0 :     entity_id_t GetControlGroup2() const override
     716             :     {
     717           0 :         return m_ControlGroup2;
     718             :     }
     719             : 
     720           0 :     void UpdateControlGroups()
     721             :     {
     722           0 :         if (m_Tag.valid())
     723             :         {
     724           0 :             CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     725           0 :             if (cmpObstructionManager)
     726             :             {
     727           0 :                 if (m_Type == UNIT)
     728             :                 {
     729           0 :                     cmpObstructionManager->SetUnitControlGroup(m_Tag, m_ControlGroup);
     730             :                 }
     731           0 :                 else if (m_Type == STATIC)
     732             :                 {
     733           0 :                     cmpObstructionManager->SetStaticControlGroup(m_Tag, m_ControlGroup, m_ControlGroup2);
     734             :                 }
     735             :                 else
     736             :                 {
     737           0 :                     cmpObstructionManager->SetStaticControlGroup(m_Tag, m_ControlGroup, m_ControlGroup2);
     738           0 :                     for (size_t i = 0; i < m_ClusterTags.size(); ++i)
     739             :                     {
     740           0 :                         cmpObstructionManager->SetStaticControlGroup(m_ClusterTags[i], m_ControlGroup, m_ControlGroup2);
     741             :                     }
     742             :                 }
     743             :             }
     744             :         }
     745           0 :     }
     746             : 
     747           0 :     void ResolveFoundationCollisions() const override
     748             :     {
     749           0 :         if (m_Type == UNIT)
     750           0 :             return;
     751             : 
     752           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     753           0 :         if (!cmpObstructionManager)
     754           0 :             return;
     755             : 
     756           0 :         CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     757           0 :         if (!cmpPosition)
     758           0 :             return; // error
     759             : 
     760           0 :         if (!cmpPosition->IsInWorld())
     761           0 :             return; // no obstruction
     762             : 
     763           0 :         CFixedVector2D pos = cmpPosition->GetPosition2D();
     764             : 
     765             :         // Ignore collisions within the same control group, or with other non-foundation-blocking shapes.
     766             :         // Note that, since the control group for each entity defaults to the entity's ID, this is typically
     767             :         // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes.
     768           0 :         SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2,
     769           0 :             ICmpObstructionManager::FLAG_BLOCK_FOUNDATION);
     770             : 
     771           0 :         std::vector<entity_id_t> collisions;
     772           0 :         if (cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &collisions))
     773             :         {
     774           0 :             std::vector<entity_id_t> persistentEnts, normalEnts;
     775             : 
     776           0 :             if (m_ControlPersist)
     777           0 :                 persistentEnts.push_back(m_ControlGroup);
     778             :             else
     779           0 :                 normalEnts.push_back(GetEntityId());
     780             : 
     781           0 :             for (std::vector<entity_id_t>::iterator it = collisions.begin(); it != collisions.end(); ++it)
     782             :             {
     783           0 :                 entity_id_t ent = *it;
     784           0 :                 if (ent == INVALID_ENTITY)
     785           0 :                     continue;
     786             : 
     787           0 :                 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
     788           0 :                 if (!cmpObstruction->IsControlPersistent())
     789           0 :                     normalEnts.push_back(ent);
     790             :                 else
     791           0 :                     persistentEnts.push_back(cmpObstruction->GetControlGroup());
     792             :             }
     793             : 
     794             :             // The collision can't be resolved without usable persistent control groups.
     795           0 :             if (persistentEnts.empty())
     796           0 :                 return;
     797             : 
     798             :             // Attempt to replace colliding entities' control groups with a persistent one.
     799           0 :             for (const entity_id_t normalEnt : normalEnts)
     800             :             {
     801           0 :                 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), normalEnt);
     802           0 :                 for (const entity_id_t persistent : normalEnts)
     803             :                 {
     804           0 :                     entity_id_t group = cmpObstruction->GetControlGroup();
     805             : 
     806             :                     // Only clobber 'default' control groups.
     807           0 :                     if (group == normalEnt)
     808           0 :                         cmpObstruction->SetControlGroup(persistent);
     809           0 :                     else if (cmpObstruction->GetControlGroup2() == INVALID_ENTITY && group != persistent)
     810           0 :                         cmpObstruction->SetControlGroup2(persistent);
     811             :                 }
     812             :             }
     813             :         }
     814             :     }
     815             : protected:
     816             : 
     817           0 :     inline void AddClusterShapes(entity_pos_t x, entity_pos_t z, entity_angle_t a)
     818             :     {
     819           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     820           0 :         if (!cmpObstructionManager)
     821           0 :             return; // error
     822             : 
     823           0 :         flags_t flags = m_Flags;
     824             :         // Disable block movement and block pathfinding for the obstruction shape
     825           0 :         flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
     826           0 :         flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
     827             : 
     828           0 :         m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
     829           0 :             x, z, a, m_Size0, m_Size1, flags, m_ControlGroup, m_ControlGroup2);
     830             : 
     831           0 :         fixed s, c;
     832           0 :         sincos_approx(a, s, c);
     833             : 
     834           0 :         for (size_t i = 0; i < m_Shapes.size(); ++i)
     835             :         {
     836           0 :             Shape& b = m_Shapes[i];
     837           0 :             tag_t tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
     838           0 :                 x + b.dx.Multiply(c) + b.dz.Multiply(s), z + b.dz.Multiply(c) - b.dx.Multiply(s), a + b.da, b.size0, b.size1, b.flags, m_ControlGroup, m_ControlGroup2);
     839           0 :             m_ClusterTags.push_back(tag);
     840             :         }
     841             :     }
     842             : 
     843           0 :     inline void RemoveClusterShapes()
     844             :     {
     845           0 :         CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
     846           0 :         if (!cmpObstructionManager)
     847           0 :             return; // error
     848             : 
     849           0 :         for (size_t i = 0; i < m_ClusterTags.size(); ++i)
     850             :         {
     851           0 :             if (m_ClusterTags[i].valid())
     852             :             {
     853           0 :                 cmpObstructionManager->RemoveShape(m_ClusterTags[i]);
     854             :             }
     855             :         }
     856           0 :         m_ClusterTags.clear();
     857             :     }
     858             : 
     859             : };
     860             : 
     861         119 : REGISTER_COMPONENT_TYPE(Obstruction)

Generated by: LCOV version 1.13