Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpObstruction.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 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 
21 #include "ICmpObstruction.h"
22 
23 #include "ps/CLogger.h"
28 
29 #define MAX(x,y) x>y ? x : y
30 #define MIN(x,y) x>y ? y : x
31 
32 /**
33  * Obstruction implementation. This keeps the ICmpPathfinder's model of the world updated when the
34  * entities move and die, with shapes derived from ICmpFootprint.
35  */
37 {
38 public:
39  static void ClassInit(CComponentManager& componentManager)
40  {
42  componentManager.SubscribeToMessageType(MT_Destroy);
43  }
44 
46 
49 
50  // Template state:
51 
52  enum {
56  } m_Type;
57 
58  entity_pos_t m_Size0; // radius or width
59  entity_pos_t m_Size1; // radius or depth
61 
62  typedef struct {
67  } Shape;
68 
69  std::vector<Shape> m_Shapes;
70 
71  // Dynamic state:
72 
73  /// Whether the obstruction is actively obstructing or just an inactive placeholder.
74  bool m_Active;
75  /// Whether the entity associated with this obstruction is currently moving. Only applicable for
76  /// UNIT-type obstructions.
77  bool m_Moving;
78  /// Whether an obstruction's control group should be kept consistent and
79  /// used to set control groups for entities that collide with it.
81 
82  /**
83  * Primary control group identifier. Indicates to which control group this entity's shape belongs.
84  * Typically used in combination with obstruction test filters to have member shapes ignore each
85  * other during obstruction tests. Defaults to the entity's ID. Must never be set to INVALID_ENTITY.
86  */
88 
89  /**
90  * Optional secondary control group identifier. Similar to m_ControlGroup; if set to a valid value,
91  * then this field identifies an additional, secondary control group to which this entity's shape
92  * belongs. Set to INVALID_ENTITY to not assign any secondary group. Defaults to INVALID_ENTITY.
93  *
94  * These are only necessary in case it is not sufficient for an entity to belong to only one control
95  * group. Otherwise, they can be ignored.
96  */
98 
99  /// Identifier of this entity's obstruction shape, as registered in the obstruction manager. Contains
100  /// structure, but should be treated as opaque here.
102  std::vector<tag_t> m_ClusterTags;
103 
104  /// Set of flags affecting the behaviour of this entity's obstruction shape.
106 
107  static std::string GetSchema()
108  {
109  return
110  "<a:example/>"
111  "<a:help>Causes this entity to obstruct the motion of other units.</a:help>"
112  "<choice>"
113  "<element name='Static'>"
114  "<attribute name='width'>"
115  "<ref name='positiveDecimal'/>"
116  "</attribute>"
117  "<attribute name='depth'>"
118  "<ref name='positiveDecimal'/>"
119  "</attribute>"
120  "</element>"
121  "<element name='Unit'>"
122  "<attribute name='radius'>"
123  "<ref name='positiveDecimal'/>"
124  "</attribute>"
125  "</element>"
126  "<element name='Obstructions'>"
127  "<zeroOrMore>"
128  "<element>"
129  "<anyName/>"
130  "<optional>"
131  "<attribute name='x'>"
132  "<data type='decimal'/>"
133  "</attribute>"
134  "</optional>"
135  "<optional>"
136  "<attribute name='z'>"
137  "<data type='decimal'/>"
138  "</attribute>"
139  "</optional>"
140  "<attribute name='width'>"
141  "<ref name='positiveDecimal'/>"
142  "</attribute>"
143  "<attribute name='depth'>"
144  "<ref name='positiveDecimal'/>"
145  "</attribute>"
146  "</element>"
147  "</zeroOrMore>"
148  "</element>"
149  "</choice>"
150  "<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'>"
151  "<data type='boolean'/>"
152  "</element>"
153  "<element name='BlockMovement' a:help='Whether units should be allowed to walk through this entity'>"
154  "<data type='boolean'/>"
155  "</element>"
156  "<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'>"
157  "<data type='boolean'/>"
158  "</element>"
159  "<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'>"
160  "<data type='boolean'/>"
161  "</element>"
162  "<element name='BlockConstruction' a:help='Whether players should be unable to begin constructing buildings placed on top of this entity'>"
163  "<data type='boolean'/>"
164  "</element>"
165  "<element name='DisableBlockMovement' a:help='If true, BlockMovement will be overridden and treated as false. (This is a special case to handle foundations)'>"
166  "<data type='boolean'/>"
167  "</element>"
168  "<element name='DisableBlockPathfinding' a:help='If true, BlockPathfinding will be overridden and treated as false. (This is a special case to handle foundations)'>"
169  "<data type='boolean'/>"
170  "</element>"
171  "<optional>"
172  "<element name='ControlPersist' a:help='If present, the control group of this entity will be given to entities that are colliding with it.'>"
173  "<empty/>"
174  "</element>"
175  "</optional>";
176  }
177 
178  virtual void Init(const CParamNode& paramNode)
179  {
180  m_TemplateFlags = 0;
181  if (paramNode.GetChild("BlockMovement").ToBool())
183  if (paramNode.GetChild("BlockPathfinding").ToBool())
185  if (paramNode.GetChild("BlockFoundation").ToBool())
187  if (paramNode.GetChild("BlockConstruction").ToBool())
189 
191  if (paramNode.GetChild("DisableBlockMovement").ToBool())
192  m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
193  if (paramNode.GetChild("DisableBlockPathfinding").ToBool())
194  m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
195 
196  if (paramNode.GetChild("Unit").IsOk())
197  {
198  m_Type = UNIT;
199  m_Size0 = m_Size1 = paramNode.GetChild("Unit").GetChild("@radius").ToFixed();
200  }
201  else if (paramNode.GetChild("Static").IsOk())
202  {
203  m_Type = STATIC;
204  m_Size0 = paramNode.GetChild("Static").GetChild("@width").ToFixed();
205  m_Size1 = paramNode.GetChild("Static").GetChild("@depth").ToFixed();
206  }
207  else
208  {
209  m_Type = CLUSTER;
212  const CParamNode::ChildrenMap& clusterMap = paramNode.GetChild("Obstructions").GetChildren();
213  for(CParamNode::ChildrenMap::const_iterator it = clusterMap.begin(); it != clusterMap.end(); ++it)
214  {
215  Shape b;
216  b.size0 = it->second.GetChild("@width").ToFixed();
217  b.size1 = it->second.GetChild("@depth").ToFixed();
218  b.dx = it->second.GetChild("@x").ToFixed();
219  b.dz = it->second.GetChild("@z").ToFixed();
221  b.flags = m_Flags;
222  m_Shapes.push_back(b);
223  max.X = MAX(max.X, b.dx + b.size0/2);
224  max.Y = MAX(max.Y, b.dz + b.size1/2);
225  min.X = MIN(min.X, b.dx - b.size0/2);
226  min.Y = MIN(min.Y, b.dz - b.size1/2);
227  }
228  m_Size0 = fixed::FromInt(2).Multiply(MAX(max.X, -min.X));
229  m_Size1 = fixed::FromInt(2).Multiply(MAX(max.Y, -min.Y));
230  }
231 
232  m_Active = paramNode.GetChild("Active").ToBool();
233  m_ControlPersist = paramNode.GetChild("ControlPersist").IsOk();
234 
235  m_Tag = tag_t();
236  if (m_Type == CLUSTER)
237  m_ClusterTags.clear();
238  m_Moving = false;
241  }
242 
243  virtual void Deinit()
244  {
245  }
246 
248  {
249  template<typename S>
250  void operator()(S& serialize, const char* UNUSED(name), tag_t& value)
251  {
252  serialize.NumberU32_Unbounded("tag", value.n);
253  }
254  };
255 
256  template<typename S>
257  void SerializeCommon(S& serialize)
258  {
259  serialize.Bool("active", m_Active);
260  serialize.Bool("moving", m_Moving);
261  serialize.NumberU32_Unbounded("control group", m_ControlGroup);
262  serialize.NumberU32_Unbounded("control group 2", m_ControlGroup2);
263  serialize.NumberU32_Unbounded("tag", m_Tag.n);
264  serialize.NumberU8_Unbounded("flags", m_Flags);
265  if (m_Type == CLUSTER)
266  SerializeVector<SerializeTag>()(serialize, "cluster tags", m_ClusterTags);
267  }
268 
269  virtual void Serialize(ISerializer& serialize)
270  {
271  SerializeCommon(serialize);
272  }
273 
274  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
275  {
276  Init(paramNode);
277 
278  SerializeCommon(deserialize);
279  }
280 
281  virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
282  {
283  switch (msg.GetType())
284  {
285  case MT_PositionChanged:
286  {
287  if (!m_Active)
288  break;
289 
290  const CMessagePositionChanged& data = static_cast<const CMessagePositionChanged&> (msg);
291 
292  if (!data.inWorld && !m_Tag.valid())
293  break; // nothing needs to change
294 
295  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
296  if (!cmpObstructionManager)
297  break; // error
298 
299  if (data.inWorld && m_Tag.valid())
300  {
301  cmpObstructionManager->MoveShape(m_Tag, data.x, data.z, data.a);
302 
303  if(m_Type == CLUSTER)
304  {
305  for (size_t i = 0; i < m_Shapes.size(); ++i)
306  {
307  Shape& b = m_Shapes[i];
308  fixed s, c;
309  sincos_approx(data.a, s, c);
310  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);
311  }
312  }
313  }
314  else if (data.inWorld && !m_Tag.valid())
315  {
316  // Need to create a new pathfinder shape:
317  if (m_Type == STATIC)
318  m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
319  data.x, data.z, data.a, m_Size0, m_Size1, m_Flags, m_ControlGroup, m_ControlGroup2);
320  else if (m_Type == UNIT)
321  m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(),
323  else
324  AddClusterShapes(data.x, data.x, data.a);
325  }
326  else if (!data.inWorld && m_Tag.valid())
327  {
328  cmpObstructionManager->RemoveShape(m_Tag);
329  m_Tag = tag_t();
330  if(m_Type == CLUSTER)
332  }
333  break;
334  }
335  case MT_Destroy:
336  {
337  if (m_Tag.valid())
338  {
339  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
340  if (!cmpObstructionManager)
341  break; // error
342 
343  cmpObstructionManager->RemoveShape(m_Tag);
344  m_Tag = tag_t();
345  if(m_Type == CLUSTER)
347  }
348  break;
349  }
350  }
351  }
352 
353  virtual void SetActive(bool active)
354  {
355  if (active && !m_Active)
356  {
357  m_Active = true;
358 
359  // Construct the obstruction shape
360 
361  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
362  if (!cmpObstructionManager)
363  return; // error
364 
365  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
366  if (!cmpPosition)
367  return; // error
368 
369  if (!cmpPosition->IsInWorld())
370  return; // don't need an obstruction
371 
372  // TODO: code duplication from message handlers
373  CFixedVector2D pos = cmpPosition->GetPosition2D();
374  if (m_Type == STATIC)
375  m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
376  pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, m_Flags, m_ControlGroup, m_ControlGroup2);
377  else if (m_Type == UNIT)
378  m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(),
379  pos.X, pos.Y, m_Size0, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup);
380  else
381  AddClusterShapes(pos.X, pos.Y, cmpPosition->GetRotation().Y);
382  }
383  else if (!active && m_Active)
384  {
385  m_Active = false;
386 
387  // Delete the obstruction shape
388 
389  // TODO: code duplication from message handlers
390  if (m_Tag.valid())
391  {
392  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
393  if (!cmpObstructionManager)
394  return; // error
395 
396  cmpObstructionManager->RemoveShape(m_Tag);
397  m_Tag = tag_t();
398  if (m_Type == CLUSTER)
400  }
401  }
402  // else we didn't change the active status
403  }
404 
405  virtual void SetDisableBlockMovementPathfinding(bool movementDisabled, bool pathfindingDisabled, int32_t shape)
406  {
407  flags_t *flags = NULL;
408  if (shape == -1)
409  flags = &m_Flags;
410  else if (m_Type == CLUSTER && shape < (int32_t)m_Shapes.size())
411  flags = &m_Shapes[shape].flags;
412  else
413  return; // error
414 
415  // Remove the blocking / pathfinding flags or
416  // Add the blocking / pathfinding flags if the template had enabled them
417  if (movementDisabled)
419  else
421  if (pathfindingDisabled)
423  else
425 
426  // Reset the shape with the new flags (kind of inefficiently - we
427  // should have a ICmpObstructionManager::SetFlags function or something)
428  if (m_Active)
429  {
430  SetActive(false);
431  SetActive(true);
432  }
433  }
434 
435  virtual bool GetBlockMovementFlag()
436  {
438  }
439 
441  {
442  return m_Tag;
443  }
444 
446  {
447  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
448  if (!cmpPosition)
449  return false; // error
450 
451  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
452  if (!cmpObstructionManager)
453  return false; // error
454 
455  if (!cmpPosition->IsInWorld())
456  return false; // no obstruction square
457 
458  CFixedVector2D pos = cmpPosition->GetPosition2D();
459  if (m_Type == STATIC)
460  out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
461  else if (m_Type == UNIT)
462  out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Size0);
463  else
464  out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
465  return true;
466  }
467 
469  {
470  if (m_Type == UNIT)
471  return m_Size0;
472  else
473  return entity_pos_t::Zero();
474  }
475 
476  virtual bool IsControlPersistent()
477  {
478  return m_ControlPersist;
479  }
480 
481  virtual EFoundationCheck CheckFoundation(std::string className)
482  {
483  return CheckFoundation(className, false);
484  }
485 
486  virtual EFoundationCheck CheckFoundation(std::string className, bool onlyCenterPoint)
487  {
488  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
489  if (!cmpPosition)
490  return FOUNDATION_CHECK_FAIL_ERROR; // error
491 
492  if (!cmpPosition->IsInWorld())
493  return FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; // no obstruction
494 
495  CFixedVector2D pos = cmpPosition->GetPosition2D();
496 
497  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
498  if (!cmpPathfinder)
499  return FOUNDATION_CHECK_FAIL_ERROR; // error
500 
501  // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
503  {
504  LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid");
506  }
507 
508  // Get passability class
509  ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className);
510 
511  // Ignore collisions within the same control group, or with other non-foundation-blocking shapes.
512  // Note that, since the control group for each entity defaults to the entity's ID, this is typically
513  // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes.
516 
517  if (m_Type == UNIT)
518  return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Size0, passClass, onlyCenterPoint);
519  else
520  return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass, onlyCenterPoint);
521  }
522 
524  {
525  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
526  if (!cmpPosition)
527  return false; // error
528 
529  if (!cmpPosition->IsInWorld())
530  return false; // no obstruction
531 
532  CFixedVector2D pos = cmpPosition->GetPosition2D();
533 
534  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
535  if (!cmpObstructionManager)
536  return false; // error
537 
538  // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
540  {
541  LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid");
542  return false;
543  }
544 
545  // Ignore collisions with entities unless they block foundations and match both control groups.
548 
549  if (m_Type == UNIT)
550  return !cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Size0, NULL);
551  else
552  return !cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, NULL );
553  }
554 
555  virtual std::vector<entity_id_t> GetEntityCollisions(bool checkStructures, bool checkUnits)
556  {
557  std::vector<entity_id_t> ret;
558 
559  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
560  if (!cmpPosition)
561  return ret; // error
562 
563  if (!cmpPosition->IsInWorld())
564  return ret; // no obstruction
565 
566  CFixedVector2D pos = cmpPosition->GetPosition2D();
567 
568  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
569  if (!cmpObstructionManager)
570  return ret; // error
571 
572  // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
574  {
575  LOGERROR(L"[CmpObstruction] Cannot test for unit or structure obstructions; primary control group must be valid");
576  return ret;
577  }
578 
579  flags_t flags = 0;
580  bool invertMatch = false;
581 
582  // There are four 'block' flags: construction, foundation, movement,
583  // and pathfinding. Structures have all of these flags, while units
584  // block only movement and construction.
585 
586  // The 'block construction' flag is common to both units and structures.
587  if (checkStructures && checkUnits)
589  // The 'block foundation' flag is exclusive to structures.
590  else if (checkStructures)
592  else if (checkUnits)
593  {
594  // As structures block a superset of what units do, matching units
595  // but not structures means excluding entities that contain any of
596  // the structure-specific flags (foundation and pathfinding).
597  invertMatch = true;
599  }
600 
601  // Ignore collisions within the same control group, or with other shapes that don't match the filter.
602  // Note that, since the control group for each entity defaults to the entity's ID, this is typically
603  // equivalent to only ignoring the entity's own shape and other shapes that don't match the filter.
606 
607  if (m_Type == STATIC)
608  cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &ret);
609  else if (m_Type == UNIT)
610  cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Size0, &ret);
611  else
612  cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &ret);
613 
614  return ret;
615  }
616 
617  virtual void SetMovingFlag(bool enabled)
618  {
619  m_Moving = enabled;
620 
621  if (m_Tag.valid() && m_Type == UNIT)
622  {
623  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
624  if (cmpObstructionManager)
625  cmpObstructionManager->SetUnitMovingFlag(m_Tag, m_Moving);
626  }
627  }
628 
629  virtual void SetControlGroup(entity_id_t group)
630  {
631  m_ControlGroup = group;
633  }
634 
635  virtual void SetControlGroup2(entity_id_t group2)
636  {
637  m_ControlGroup2 = group2;
639  }
640 
642  {
643  return m_ControlGroup;
644  }
645 
647  {
648  return m_ControlGroup2;
649  }
650 
652  {
653  if (m_Tag.valid())
654  {
655  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
656  if (cmpObstructionManager)
657  {
658  if (m_Type == UNIT)
659  {
660  cmpObstructionManager->SetUnitControlGroup(m_Tag, m_ControlGroup);
661  }
662  else if (m_Type == STATIC)
663  {
664  cmpObstructionManager->SetStaticControlGroup(m_Tag, m_ControlGroup, m_ControlGroup2);
665  }
666  else
667  {
668  cmpObstructionManager->SetStaticControlGroup(m_Tag, m_ControlGroup, m_ControlGroup2);
669  for (size_t i = 0; i < m_ClusterTags.size(); ++i)
670  {
672  }
673  }
674  }
675  }
676  }
677 
679  {
680  if (m_Type == UNIT)
681  return;
682 
683  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
684  if (!cmpObstructionManager)
685  return;
686 
687  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
688  if (!cmpPosition)
689  return; // error
690 
691  if (!cmpPosition->IsInWorld())
692  return; // no obstruction
693 
694  CFixedVector2D pos = cmpPosition->GetPosition2D();
695 
696  // Ignore collisions within the same control group, or with other non-foundation-blocking shapes.
697  // Note that, since the control group for each entity defaults to the entity's ID, this is typically
698  // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes.
701 
702  std::vector<entity_id_t> collisions;
703  if (cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &collisions))
704  {
705  std::vector<entity_id_t> persistentEnts, normalEnts;
706 
707  if (m_ControlPersist)
708  persistentEnts.push_back(m_ControlGroup);
709  else
710  normalEnts.push_back(GetEntityId());
711 
712  for (std::vector<entity_id_t>::iterator it = collisions.begin(); it != collisions.end(); ++it)
713  {
714  entity_id_t ent = *it;
715  if (ent == INVALID_ENTITY)
716  continue;
717 
718  CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
719  if (!cmpObstruction->IsControlPersistent())
720  normalEnts.push_back(ent);
721  else
722  persistentEnts.push_back(cmpObstruction->GetControlGroup());
723  }
724 
725  // The collision can't be resolved without usable persistent control groups.
726  if (persistentEnts.empty())
727  return;
728 
729  // Attempt to replace colliding entities' control groups with a persistent one.
730  for (std::vector<entity_id_t>::iterator it = normalEnts.begin(); it != normalEnts.end(); ++it)
731  {
732  entity_id_t ent = *it;
733 
734  CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
735  for (std::vector<entity_id_t>::iterator it = persistentEnts.begin(); it != persistentEnts.end(); ++it)
736  {
737  entity_id_t persistent = *it;
738  entity_id_t group = cmpObstruction->GetControlGroup();
739 
740  // Only clobber 'default' control groups.
741  if (group == ent)
742  cmpObstruction->SetControlGroup(persistent);
743  else if (cmpObstruction->GetControlGroup2() == INVALID_ENTITY && group != persistent)
744  cmpObstruction->SetControlGroup2(persistent);
745  }
746  }
747  }
748  }
749 protected:
750 
752  {
753  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
754  if (!cmpObstructionManager)
755  return; // error
756 
757  flags_t flags = m_Flags;
758  // Disable block movement and block pathfinding for the obstruction shape
761 
762  m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
763  x, z, a, m_Size0, m_Size1, flags, m_ControlGroup, m_ControlGroup2);
764 
765  fixed s, c;
766  sincos_approx(a, s, c);
767 
768  for (size_t i = 0; i < m_Shapes.size(); ++i)
769  {
770  Shape& b = m_Shapes[i];
771  tag_t tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
772  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);
773  m_ClusterTags.push_back(tag);
774  }
775  }
776 
777  inline void RemoveClusterShapes()
778  {
779  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
780  if (!cmpObstructionManager)
781  return; // error
782 
783  for (size_t i = 0; i < m_ClusterTags.size(); ++i)
784  {
785  if (m_ClusterTags[i].valid())
786  {
787  cmpObstructionManager->RemoveShape(m_ClusterTags[i]);
788  }
789  }
790  m_ClusterTags.clear();
791  }
792 
793 };
794 
795 REGISTER_COMPONENT_TYPE(Obstruction)
An entity initialisation parameter node.
Definition: ParamNode.h:112
virtual pass_class_t GetPassabilityClass(const std::string &name)=0
Get the tag for a given passability class name.
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
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< entity_id_t > *out)=0
Collision test a static square shape against the current set of shapes.
virtual ICmpObstructionManager::tag_t GetObstruction()
A simple fixed-point number class.
Definition: Fixed.h:115
#define MAX(x, y)
Obstruction test filter that will test only against shapes that:
void SerializeCommon(S &serialize)
virtual void SetControlGroup(entity_id_t group)
Change the control group that the entity belongs to.
Obstruction test filter that will test only against shapes that:
bool m_Moving
Whether the entity associated with this obstruction is currently moving.
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
Helper templates for serializing/deserializing common objects.
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
virtual EFoundationCheck CheckFoundation(std::string className, bool onlyCenterPoint)
virtual void SetUnitControlGroup(tag_t tag, entity_id_t group)=0
Set the control group of a unit shape.
bool ToBool() const
Parses the content of this node as a boolean (&quot;true&quot; == true, anything else == false) ...
Definition: ParamNode.cpp:236
entity_id_t m_ControlGroup2
Optional secondary control group identifier.
static CFixed Zero()
Definition: Fixed.h:127
virtual tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, entity_id_t group2=INVALID_ENTITY)=0
Register a static shape.
#define LOGERROR
Definition: CLogger.h:35
virtual ObstructionSquare GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h)=0
virtual bool TestUnitShape(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector< entity_id_t > *out)=0
Collision test a unit shape against the current set of registered shapes, and optionally writes a lis...
virtual void SetMovingFlag(bool enabled)
virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass)=0
Check whether a building placed here is valid and doesn&#39;t hit any obstructions or impassable terrain...
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:193
virtual void SetControlGroup2(entity_id_t group2)=0
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
std::vector< Shape > m_Shapes
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
virtual entity_pos_t GetUnitRadius()
virtual CFixedVector3D GetRotation()=0
Returns the current rotation (relative to the upwards axis), as Euler angles with X=pitch...
virtual void SetDisableBlockMovementPathfinding(bool movementDisabled, bool pathfindingDisabled, int32_t shape)
virtual void Init(const CParamNode &paramNode)
const ChildrenMap & GetChildren() const
Returns the names/nodes of the children of this node, ordered by name.
Definition: ParamNode.cpp:244
virtual std::vector< entity_id_t > GetEntityCollisions(bool checkStructures, bool checkUnits)
Returns a list of entities that are colliding with this entity, filtered depending on type of entitie...
virtual bool IsControlPersistent()=0
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
virtual bool IsInWorld()=0
Returns true if the entity currently exists at a defined position in the world.
void ResolveFoundationCollisions()
Detects collisions between foundation-blocking entities and tries to fix them by setting control grou...
Obstruction implementation.
virtual void HandleMessage(const CMessage &msg, bool global)
Flags an entity as obstructing movement for other units, and handles the processing of collision quer...
CEntityHandle GetEntityHandle() const
Definition: IComponent.h:45
ICmpObstructionManager::tag_t tag_t
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:222
entity_id_t GetEntityId() const
Definition: IComponent.h:48
virtual int GetType() const =0
const CParamNode & GetChild(const char *name) const
Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none...
Definition: ParamNode.cpp:185
virtual entity_id_t GetControlGroup()
See SetControlGroup.
virtual ICmpObstruction::EFoundationCheck CheckUnitPlacement(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass)=0
Check whether a unit placed here is valid and doesn&#39;t hit any obstructions or impassable terrain...
tag_t m_Tag
Identifier of this entity&#39;s obstruction shape, as registered in the obstruction manager.
virtual void SetUnitMovingFlag(tag_t tag, bool moving)=0
Set whether a unit shape is moving or stationary.
static void ClassInit(CComponentManager &componentManager)
virtual entity_id_t GetControlGroup2()
virtual entity_id_t GetControlGroup2()=0
virtual void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2)=0
Sets the control group of a static shape.
CFixed Multiply(CFixed n) const
Multiply by a CFixed.
Definition: Fixed.h:290
bool m_Active
Whether the obstruction is actively obstructing or just an inactive placeholder.
virtual bool CheckDuplicateFoundation()
Test whether this entity is colliding with any obstructions that share its control groups and block t...
virtual void Deinit()
virtual void SetActive(bool active)
virtual CFixedVector2D GetPosition2D()=0
Returns the current x,z position (no interpolation).
entity_pos_t m_Size1
virtual bool IsControlPersistent()
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
Sent during TurnStart.
Definition: MessageTypes.h:245
virtual void SetControlGroup2(entity_id_t group2)
static std::string GetSchema()
virtual bool GetBlockMovementFlag()
virtual EFoundationCheck CheckFoundation(std::string className)
Test whether this entity is colliding with any obstruction that are set to block the creation of foun...
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
ICmpObstructionManager::flags_t flags_t
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
static CFixed FromInt(int n)
Definition: Fixed.h:136
entity_pos_t m_Size0
virtual ObstructionSquare GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, entity_pos_t r)=0
void AddClusterShapes(entity_pos_t x, entity_pos_t z, entity_angle_t a)
flags_t m_Flags
Set of flags affecting the behaviour of this entity&#39;s obstruction shape.
enum CCmpObstruction::@52 m_Type
void operator()(S &serialize, const char *name, tag_t &value)
void sincos_approx(CFixed_15_16 a, CFixed_15_16 &sin_out, CFixed_15_16 &cos_out)
Compute sin(a) and cos(a).
Definition: Fixed.cpp:187
virtual entity_id_t GetControlGroup()=0
See SetControlGroup.
virtual void RemoveShape(tag_t tag)=0
Remove an existing shape.
virtual void Serialize(ISerializer &serialize)
const entity_id_t INVALID_ENTITY
Invalid entity ID.
Definition: Entity.h:36
#define MIN(x, y)
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare &out)
Gets the square corresponding to this obstruction shape.
virtual void SetControlGroup(entity_id_t group)=0
Change the control group that the entity belongs to.
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
std::vector< tag_t > m_ClusterTags
std::map< std::string, CParamNode > ChildrenMap
Definition: ParamNode.h:115
virtual void MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a)=0
Adjust the position and angle of an existing shape.
Standard representation for all types of shapes, for use with geometry processing code...
Obstruction manager: provides efficient spatial queries over objects in the world.
entity_id_t m_ControlGroup
Primary control group identifier.
bool m_ControlPersist
Whether an obstruction&#39;s control group should be kept consistent and used to set control groups for e...
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
External identifiers for shapes.
virtual tag_t AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t r, flags_t flags, entity_id_t group)=0
Register a unit shape.