Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpPathfinder.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 /**
19  * @file
20  * Common code and setup code for CCmpPathfinder.
21  */
22 
23 #include "precompiled.h"
24 
25 #include "CCmpPathfinder_Common.h"
26 
27 #include "ps/CLogger.h"
28 #include "ps/CStr.h"
29 #include "ps/Profile.h"
30 #include "renderer/Scene.h"
37 
38 // Default cost to move a single tile is a fairly arbitrary number, which should be big
39 // enough to be precise when multiplied/divided and small enough to never overflow when
40 // summing the cost of a whole path.
41 const int DEFAULT_MOVE_COST = 256;
42 
43 REGISTER_COMPONENT_TYPE(Pathfinder)
44 
45 void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
46 {
47  m_MapSize = 0;
48  m_Grid = NULL;
49  m_ObstructionGrid = NULL;
50  m_TerrainDirty = true;
51  m_NextAsyncTicket = 1;
52 
53  m_DebugOverlay = NULL;
54  m_DebugGrid = NULL;
55  m_DebugPath = NULL;
56 
57  m_SameTurnMovesCount = 0;
58 
59  // Since this is used as a system component (not loaded from an entity template),
60  // we can't use the real paramNode (it won't get handled properly when deserializing),
61  // so load the data from a special XML file.
62  CParamNode externalParamNode;
63  CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
64 
65  // Previously all move commands during a turn were
66  // queued up and processed asynchronously at the start
67  // of the next turn. Now we are processing queued up
68  // events several times duing the turn. This improves
69  // responsiveness and units move more smoothly especially.
70  // when in formation. There is still a call at the
71  // beginning of a turn to process all outstanding moves -
72  // this will handle any moves above the MaxSameTurnMoves
73  // threshold.
74  //
75  // TODO - The moves processed at the beginning of the
76  // turn do not count against the maximum moves per turn
77  // currently. The thinking is that this will eventually
78  // happen in another thread. Either way this probably
79  // will require some adjustment and rethinking.
80  const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder");
81  m_MaxSameTurnMoves = (u16)pathingSettings.GetChild("MaxSameTurnMoves").ToInt();
82 
83 
84  const CParamNode::ChildrenMap& passClasses = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses").GetChildren();
85  for (CParamNode::ChildrenMap::const_iterator it = passClasses.begin(); it != passClasses.end(); ++it)
86  {
87  std::string name = it->first;
88  ENSURE((int)m_PassClasses.size() <= PASS_CLASS_BITS);
89  pass_class_t mask = (pass_class_t)(1u << (m_PassClasses.size() + 2));
90  m_PassClasses.push_back(PathfinderPassability(mask, it->second));
91  m_PassClassMasks[name] = mask;
92  }
93 
94 
95  const CParamNode::ChildrenMap& moveClasses = externalParamNode.GetChild("Pathfinder").GetChild("MovementClasses").GetChildren();
96 
97  // First find the set of unit classes used by any terrain classes,
98  // and assign unique tags to terrain classes
99  std::set<std::string> unitClassNames;
100  unitClassNames.insert("default"); // must always have costs for default
101 
102  {
103  size_t i = 0;
104  for (CParamNode::ChildrenMap::const_iterator it = moveClasses.begin(); it != moveClasses.end(); ++it)
105  {
106  std::string terrainClassName = it->first;
107  m_TerrainCostClassTags[terrainClassName] = (cost_class_t)i;
108  ++i;
109 
110  const CParamNode::ChildrenMap& unitClasses = it->second.GetChild("UnitClasses").GetChildren();
111  for (CParamNode::ChildrenMap::const_iterator uit = unitClasses.begin(); uit != unitClasses.end(); ++uit)
112  unitClassNames.insert(uit->first);
113  }
114  }
115 
116  // For each terrain class, set the costs for every unit class,
117  // and assign unique tags to unit classes
118  {
119  size_t i = 0;
120  for (std::set<std::string>::const_iterator nit = unitClassNames.begin(); nit != unitClassNames.end(); ++nit)
121  {
122  m_UnitCostClassTags[*nit] = (cost_class_t)i;
123  ++i;
124 
125  std::vector<u32> costs;
126  std::vector<fixed> speeds;
127 
128  for (CParamNode::ChildrenMap::const_iterator it = moveClasses.begin(); it != moveClasses.end(); ++it)
129  {
130  // Default to the general costs for this terrain class
131  fixed cost = it->second.GetChild("@Cost").ToFixed();
132  fixed speed = it->second.GetChild("@Speed").ToFixed();
133  // Check for specific cost overrides for this unit class
134  const CParamNode& unitClass = it->second.GetChild("UnitClasses").GetChild(nit->c_str());
135  if (unitClass.IsOk())
136  {
137  cost = unitClass.GetChild("@Cost").ToFixed();
138  speed = unitClass.GetChild("@Speed").ToFixed();
139  }
140  costs.push_back((cost * DEFAULT_MOVE_COST).ToInt_RoundToZero());
141  speeds.push_back(speed);
142  }
143 
144  m_MoveCosts.push_back(costs);
145  m_MoveSpeeds.push_back(speeds);
146  }
147  }
148 }
149 
151 {
152  SetDebugOverlay(false); // cleans up memory
153  ResetDebugPath();
154 
155  delete m_Grid;
156  delete m_ObstructionGrid;
157 }
158 
160 {
161  template<typename S>
162  void operator()(S& serialize, const char* UNUSED(name), AsyncLongPathRequest& value)
163  {
164  serialize.NumberU32_Unbounded("ticket", value.ticket);
165  serialize.NumberFixed_Unbounded("x0", value.x0);
166  serialize.NumberFixed_Unbounded("z0", value.z0);
167  SerializeGoal()(serialize, "goal", value.goal);
168  serialize.NumberU16_Unbounded("pass class", value.passClass);
169  serialize.NumberU8_Unbounded("cost class", value.costClass);
170  serialize.NumberU32_Unbounded("notify", value.notify);
171  }
172 };
173 
175 {
176  template<typename S>
177  void operator()(S& serialize, const char* UNUSED(name), AsyncShortPathRequest& value)
178  {
179  serialize.NumberU32_Unbounded("ticket", value.ticket);
180  serialize.NumberFixed_Unbounded("x0", value.x0);
181  serialize.NumberFixed_Unbounded("z0", value.z0);
182  serialize.NumberFixed_Unbounded("r", value.r);
183  serialize.NumberFixed_Unbounded("range", value.range);
184  SerializeGoal()(serialize, "goal", value.goal);
185  serialize.NumberU16_Unbounded("pass class", value.passClass);
186  serialize.Bool("avoid moving units", value.avoidMovingUnits);
187  serialize.NumberU32_Unbounded("group", value.group);
188  serialize.NumberU32_Unbounded("notify", value.notify);
189  }
190 };
191 
193 {
194  SerializeVector<SerializeLongRequest>()(serialize, "long requests", m_AsyncLongPathRequests);
196  serialize.NumberU32_Unbounded("next ticket", m_NextAsyncTicket);
197  serialize.NumberU16_Unbounded("same turn moves count", m_SameTurnMovesCount);
198 }
199 
200 void CCmpPathfinder::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
201 {
202  Init(paramNode);
203 
204  SerializeVector<SerializeLongRequest>()(deserialize, "long requests", m_AsyncLongPathRequests);
205  SerializeVector<SerializeShortRequest>()(deserialize, "short requests", m_AsyncShortPathRequests);
206  deserialize.NumberU32_Unbounded("next ticket", m_NextAsyncTicket);
207  deserialize.NumberU16_Unbounded("same turn moves count", m_SameTurnMovesCount);
208 }
209 
210 void CCmpPathfinder::HandleMessage(const CMessage& msg, bool UNUSED(global))
211 {
212  switch (msg.GetType())
213  {
214  case MT_RenderSubmit:
215  {
216  const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
217  RenderSubmit(msgData.collector);
218  break;
219  }
220  case MT_TerrainChanged:
221  {
222  // TODO: we ought to only bother updating the dirtied region
223  m_TerrainDirty = true;
224  break;
225  }
226  case MT_TurnStart:
227  {
229  break;
230  }
231  }
232 }
233 
235 {
236  for (size_t i = 0; i < m_DebugOverlayShortPathLines.size(); ++i)
237  collector.Submit(&m_DebugOverlayShortPathLines[i]);
238 }
239 
240 
242 {
243  UpdateGrid();
244 
245  u16 i, j;
246  NearestTile(x0, z0, i, j);
247  TerrainTile tileTag = m_Grid->get(i, j);
248  return m_MoveSpeeds.at(costClass).at(GET_COST_CLASS(tileTag));
249 }
250 
252 {
253  if (m_PassClassMasks.find(name) == m_PassClassMasks.end())
254  {
255  LOGERROR(L"Invalid passability class name '%hs'", name.c_str());
256  return 0;
257  }
258 
259  return m_PassClassMasks[name];
260 }
261 
262 std::map<std::string, ICmpPathfinder::pass_class_t> CCmpPathfinder::GetPassabilityClasses()
263 {
264  return m_PassClassMasks;
265 }
266 
268 {
269  if (m_UnitCostClassTags.find(name) == m_UnitCostClassTags.end())
270  {
271  LOGERROR(L"Invalid unit cost class name '%hs'", name.c_str());
272  return m_UnitCostClassTags["default"];
273  }
274 
275  return m_UnitCostClassTags[name];
276 }
277 
279 {
280  switch (goal.type)
281  {
283  return (pos - CFixedVector2D(goal.x, goal.z)).Length();
284 
286  return ((pos - CFixedVector2D(goal.x, goal.z)).Length() - goal.hw).Absolute();
287 
289  {
290  CFixedVector2D halfSize(goal.hw, goal.hh);
291  CFixedVector2D d(pos.X - goal.x, pos.Y - goal.z);
292  return Geometry::DistanceToSquare(d, goal.u, goal.v, halfSize);
293  }
294 
295  default:
296  debug_warn(L"invalid type");
297  return fixed::Zero();
298  }
299 }
300 
302 {
303  UpdateGrid();
304  return *m_Grid;
305 }
306 
308 {
309  CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
310  if (!cmpTerrain)
311  return; // error
312 
313  // If the terrain was resized then delete the old grid data
314  if (m_Grid && m_MapSize != cmpTerrain->GetTilesPerSide())
315  {
318  m_TerrainDirty = true;
319  }
320 
321  // Initialise the terrain data when first needed
322  if (!m_Grid)
323  {
324  m_MapSize = cmpTerrain->GetTilesPerSide();
327  }
328 
329  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
330 
331  bool obstructionsDirty = cmpObstructionManager->Rasterise(*m_ObstructionGrid);
332 
333  if (obstructionsDirty && !m_TerrainDirty)
334  {
335  PROFILE("UpdateGrid obstructions");
336 
337  // Obstructions changed - we need to recompute passability
338  // Since terrain hasn't changed we only need to update the obstruction bits
339  // and can skip the rest of the data
340 
341  // TODO: if ObstructionManager::SetPassabilityCircular was called at runtime
342  // (which should probably never happen, but that's not guaranteed),
343  // then TILE_OUTOFBOUNDS will change and we can't use this fast path, but
344  // currently it'll just set obstructionsDirty and we won't notice
345 
346  for (u16 j = 0; j < m_MapSize; ++j)
347  {
348  for (u16 i = 0; i < m_MapSize; ++i)
349  {
350  TerrainTile& t = m_Grid->get(i, j);
351 
352  u8 obstruct = m_ObstructionGrid->get(i, j);
353 
355  t |= 1;
356  else
357  t &= (TerrainTile)~1;
358 
360  t |= 2;
361  else
362  t &= (TerrainTile)~2;
363  }
364  }
365 
366  ++m_Grid->m_DirtyID;
367  }
368  else if (obstructionsDirty || m_TerrainDirty)
369  {
370  PROFILE("UpdateGrid full");
371 
372  // Obstructions or terrain changed - we need to recompute passability
373  // TODO: only bother recomputing the region that has actually changed
374 
375  CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
376 
377  // TOOD: these bits should come from ICmpTerrain
378  CTerrain& terrain = GetSimContext().GetTerrain();
379 
380  // avoid integer overflow in intermediate calculation
381  const u16 shoreMax = 32767;
382 
383  // First pass - find underwater tiles
384  Grid<bool> waterGrid(m_MapSize, m_MapSize);
385  for (u16 j = 0; j < m_MapSize; ++j)
386  {
387  for (u16 i = 0; i < m_MapSize; ++i)
388  {
389  fixed x, z;
390  TileCenter(i, j, x, z);
391 
392  bool underWater = cmpWaterManager && (cmpWaterManager->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z));
393  waterGrid.set(i, j, underWater);
394  }
395  }
396  // Second pass - find shore tiles
397  Grid<u16> shoreGrid(m_MapSize, m_MapSize);
398  for (u16 j = 0; j < m_MapSize; ++j)
399  {
400  for (u16 i = 0; i < m_MapSize; ++i)
401  {
402  // Find a land tile
403  if (!waterGrid.get(i, j))
404  {
405  if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1))
406  || (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1))
407  || (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1))
408  )
409  { // If it's bordered by water, it's a shore tile
410  shoreGrid.set(i, j, 0);
411  }
412  else
413  {
414  shoreGrid.set(i, j, shoreMax);
415  }
416  }
417  }
418  }
419 
420  // Expand influences on land to find shore distance
421  for (u16 y = 0; y < m_MapSize; ++y)
422  {
423  u16 min = shoreMax;
424  for (u16 x = 0; x < m_MapSize; ++x)
425  {
426  if (!waterGrid.get(x, y))
427  {
428  u16 g = shoreGrid.get(x, y);
429  if (g > min)
430  shoreGrid.set(x, y, min);
431  else if (g < min)
432  min = g;
433 
434  ++min;
435  }
436  }
437  for (u16 x = m_MapSize; x > 0; --x)
438  {
439  if (!waterGrid.get(x-1, y))
440  {
441  u16 g = shoreGrid.get(x-1, y);
442  if (g > min)
443  shoreGrid.set(x-1, y, min);
444  else if (g < min)
445  min = g;
446 
447  ++min;
448  }
449  }
450  }
451  for (u16 x = 0; x < m_MapSize; ++x)
452  {
453  u16 min = shoreMax;
454  for (u16 y = 0; y < m_MapSize; ++y)
455  {
456  if (!waterGrid.get(x, y))
457  {
458  u16 g = shoreGrid.get(x, y);
459  if (g > min)
460  shoreGrid.set(x, y, min);
461  else if (g < min)
462  min = g;
463 
464  ++min;
465  }
466  }
467  for (u16 y = m_MapSize; y > 0; --y)
468  {
469  if (!waterGrid.get(x, y-1))
470  {
471  u16 g = shoreGrid.get(x, y-1);
472  if (g > min)
473  shoreGrid.set(x, y-1, min);
474  else if (g < min)
475  min = g;
476 
477  ++min;
478  }
479  }
480  }
481 
482  // Apply passability classes to terrain
483  for (u16 j = 0; j < m_MapSize; ++j)
484  {
485  for (u16 i = 0; i < m_MapSize; ++i)
486  {
487  fixed x, z;
488  TileCenter(i, j, x, z);
489 
490  TerrainTile t = 0;
491 
492  u8 obstruct = m_ObstructionGrid->get(i, j);
493 
494  fixed height = terrain.GetExactGroundLevelFixed(x, z);
495 
496  fixed water;
497  if (cmpWaterManager)
498  water = cmpWaterManager->GetWaterLevel(x, z);
499 
500  fixed depth = water - height;
501 
502  fixed slope = terrain.GetSlopeFixed(i, j);
503 
504  fixed shoredist = fixed::FromInt(shoreGrid.get(i, j));
505 
507  t |= 1;
508 
510  t |= 2;
511 
513  {
514  // If out of bounds, nobody is allowed to pass
515  for (size_t n = 0; n < m_PassClasses.size(); ++n)
516  t |= m_PassClasses[n].m_Mask;
517  }
518  else
519  {
520  for (size_t n = 0; n < m_PassClasses.size(); ++n)
521  {
522  if (!m_PassClasses[n].IsPassable(depth, slope, shoredist))
523  t |= m_PassClasses[n].m_Mask;
524  }
525  }
526 
527  std::string moveClass = terrain.GetMovementClass(i, j);
528  if (m_TerrainCostClassTags.find(moveClass) != m_TerrainCostClassTags.end())
529  t |= COST_CLASS_MASK(m_TerrainCostClassTags[moveClass]);
530 
531  m_Grid->set(i, j, t);
532  }
533  }
534 
535  m_TerrainDirty = false;
536 
537  ++m_Grid->m_DirtyID;
538  }
539 }
540 
541 //////////////////////////////////////////////////////////
542 
543 // Async path requests:
544 
545 u32 CCmpPathfinder::ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass, cost_class_t costClass, entity_id_t notify)
546 {
547  AsyncLongPathRequest req = { m_NextAsyncTicket++, x0, z0, goal, passClass, costClass, notify };
548  m_AsyncLongPathRequests.push_back(req);
549  return req.ticket;
550 }
551 
552 u32 CCmpPathfinder::ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t group, entity_id_t notify)
553 {
554  AsyncShortPathRequest req = { m_NextAsyncTicket++, x0, z0, r, range, goal, passClass, avoidMovingUnits, group, notify };
555  m_AsyncShortPathRequests.push_back(req);
556  return req.ticket;
557 }
558 
560 {
561  // Save the request queue in case it gets modified while iterating
562  std::vector<AsyncLongPathRequest> longRequests;
563  m_AsyncLongPathRequests.swap(longRequests);
564 
565  std::vector<AsyncShortPathRequest> shortRequests;
566  m_AsyncShortPathRequests.swap(shortRequests);
567 
568  // TODO: we should only compute one path per entity per turn
569 
570  // TODO: this computation should be done incrementally, spread
571  // across multiple frames (or even multiple turns)
572 
573  ProcessLongRequests(longRequests);
574  ProcessShortRequests(shortRequests);
575 }
576 
577 void CCmpPathfinder::ProcessLongRequests(const std::vector<AsyncLongPathRequest>& longRequests)
578 {
579  for (size_t i = 0; i < longRequests.size(); ++i)
580  {
581  const AsyncLongPathRequest& req = longRequests[i];
582  Path path;
583  ComputePath(req.x0, req.z0, req.goal, req.passClass, req.costClass, path);
584  CMessagePathResult msg(req.ticket, path);
586  }
587 }
588 
589 void CCmpPathfinder::ProcessShortRequests(const std::vector<AsyncShortPathRequest>& shortRequests)
590 {
591  for (size_t i = 0; i < shortRequests.size(); ++i)
592  {
593  const AsyncShortPathRequest& req = shortRequests[i];
594  Path path;
596  ComputeShortPath(filter, req.x0, req.z0, req.r, req.range, req.goal, req.passClass, path);
597  CMessagePathResult msg(req.ticket, path);
599  }
600 }
601 
603 {
604  if (!m_AsyncLongPathRequests.empty())
605  {
606  // Figure out how many moves we can do this time
608 
609  if (moveCount <= 0)
610  return;
611 
612  // Copy the long request elements we are going to process into a new array
613  std::vector<AsyncLongPathRequest> longRequests;
614  if ((i32)m_AsyncLongPathRequests.size() <= moveCount)
615  {
616  m_AsyncLongPathRequests.swap(longRequests);
617  moveCount = (i32)longRequests.size();
618  }
619  else
620  {
621  longRequests.resize(moveCount);
622  copy(m_AsyncLongPathRequests.begin(), m_AsyncLongPathRequests.begin() + moveCount, longRequests.begin());
623  m_AsyncLongPathRequests.erase(m_AsyncLongPathRequests.begin(), m_AsyncLongPathRequests.begin() + moveCount);
624  }
625 
626  ProcessLongRequests(longRequests);
627 
628  m_SameTurnMovesCount = (u16)(m_SameTurnMovesCount + moveCount);
629  }
630 
631  if (!m_AsyncShortPathRequests.empty())
632  {
633  // Figure out how many moves we can do now
635 
636  if (moveCount <= 0)
637  return;
638 
639  // Copy the short request elements we are going to process into a new array
640  std::vector<AsyncShortPathRequest> shortRequests;
641  if ((i32)m_AsyncShortPathRequests.size() <= moveCount)
642  {
643  m_AsyncShortPathRequests.swap(shortRequests);
644  moveCount = (i32)shortRequests.size();
645  }
646  else
647  {
648  shortRequests.resize(moveCount);
649  copy(m_AsyncShortPathRequests.begin(), m_AsyncShortPathRequests.begin() + moveCount, shortRequests.begin());
651  }
652 
653  ProcessShortRequests(shortRequests);
654 
655  m_SameTurnMovesCount = (u16)(m_SameTurnMovesCount + moveCount);
656  }
657 }
658 
660  entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass)
661 {
662  return CCmpPathfinder::CheckUnitPlacement(filter, x, z, r, passClass, false);
663 }
664 
666  entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass, bool onlyCenterPoint)
667 {
668  // Check unit obstruction
669  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
670  if (!cmpObstructionManager)
672 
673  if (cmpObstructionManager->TestUnitShape(filter, x, z, r, NULL))
675 
676  // Test against terrain:
677 
678  UpdateGrid();
679 
680  if (onlyCenterPoint)
681  {
682  u16 i, j;
683  NearestTile(x , z, i, j);
684 
685  if (IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
687 
689  }
690 
691  u16 i0, j0, i1, j1;
692  NearestTile(x - r, z - r, i0, j0);
693  NearestTile(x + r, z + r, i1, j1);
694  for (u16 j = j0; j <= j1; ++j)
695  {
696  for (u16 i = i0; i <= i1; ++i)
697  {
698  if (!IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
699  {
701  }
702  }
703  }
705 }
706 
709  entity_pos_t h, entity_id_t id, pass_class_t passClass)
710 {
711  return CCmpPathfinder::CheckBuildingPlacement(filter, x, z, a, w, h, id, passClass, false);
712 }
713 
714 
717  entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint)
718 {
719  // Check unit obstruction
720  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
721  if (!cmpObstructionManager)
723 
724  if (cmpObstructionManager->TestStaticShape(filter, x, z, a, w, h, NULL))
726 
727  // Test against terrain:
728 
729  UpdateGrid();
730 
732  CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
733  if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square))
735 
736  if (onlyCenterPoint)
737  {
738  u16 i, j;
739  NearestTile(x, z, i, j);
740 
741  if (IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
743 
745  }
746 
747  // Expand bounds by 1/sqrt(2) tile (multiply by TERRAIN_TILE_SIZE since we want world coordinates)
749  CFixedVector2D halfSize(square.hw + expand, square.hh + expand);
750  CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize);
751 
752  u16 i0, j0, i1, j1;
753  NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0);
754  NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1);
755  for (u16 j = j0; j <= j1; ++j)
756  {
757  for (u16 i = i0; i <= i1; ++i)
758  {
759  entity_pos_t x, z;
760  TileCenter(i, j, x, z);
761  if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize)
762  && !IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
763  {
765  }
766  }
767  }
768 
770 }
CStr8 GetMovementClass(ssize_t i, ssize_t j) const
Definition: Terrain.cpp:105
An entity initialisation parameter node.
Definition: ParamNode.h:112
std::vector< SOverlayLine > m_DebugOverlayShortPathLines
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.
#define u8
Definition: types.h:39
A simple fixed-point number class.
Definition: Fixed.h:115
ICmpPathfinder::pass_class_t passClass
Interface for ICmpObstructionManager Test functions to filter out unwanted shapes.
Declares CCmpPathfinder, whose implementation is split into multiple source files, and provides common code needed for more than one of those files.
virtual entity_pos_t GetWaterLevel(entity_pos_t x, entity_pos_t z)=0
Get the current water level at the given point.
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
Implementation of ICmpPathfinder.
void UpdateGrid()
Regenerates the grid based on the current obstruction list, if necessary.
Helper templates for serializing/deserializing common objects.
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
virtual void HandleMessage(const CMessage &msg, bool global)
CFixedVector2D v
static fixed DistanceToGoal(CFixedVector2D pos, const CCmpPathfinder::Goal &goal)
static CFixed Zero()
Definition: Fixed.h:127
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
virtual void ResetDebugPath()
#define LOGERROR
Definition: CLogger.h:35
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...
std::vector< PathfinderPassability > m_PassClasses
std::map< std::string, cost_class_t > m_TerrainCostClassTags
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:193
virtual fixed GetMovementSpeed(entity_pos_t x0, entity_pos_t z0, cost_class_t costClass)
Find the speed factor (typically around 1.0) for a unit of the given cost class at the given position...
#define i32
Definition: types.h:36
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
static Status Init()
Definition: h_mgr.cpp:744
virtual void ProcessSameTurnMoves()
Process moves during the same turn they were created in to improve responsiveness.
Obstruction test filter that reject shapes in a given control group, and rejects shapes that don&#39;t bl...
Add renderable objects to the scene collector.
Definition: MessageTypes.h:145
virtual std::map< std::string, pass_class_t > GetPassabilityClasses()
Get the list of all known passability classes.
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)
Check whether a building placed here is valid and doesn&#39;t hit any obstructions or impassable terrain...
void NearestTile(entity_pos_t x, entity_pos_t z, u16 &i, u16 &j)
Returns the tile containing the given position.
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare &out)=0
Gets the square corresponding to this obstruction shape.
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
int ToInt() const
Parses the content of this node as an integer.
Definition: ParamNode.cpp:213
Grid< u8 > * m_ObstructionGrid
const ChildrenMap & GetChildren() const
Returns the names/nodes of the children of this node, ordered by name.
Definition: ParamNode.cpp:244
void operator()(S &serialize, const char *name, AsyncShortPathRequest &value)
Grid< TerrainTile > * m_Grid
#define COST_CLASS_MASK(id)
void RenderSubmit(SceneCollector &collector)
ICmpPathfinder::pass_class_t passClass
std::vector< AsyncShortPathRequest > m_AsyncShortPathRequests
This interface accepts renderable objects.
Definition: Scene.h:82
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:222
static void LoadXML(CParamNode &ret, const XMBFile &file, const wchar_t *sourceIdentifier=NULL)
Loads the XML data specified by file into the node ret.
Definition: ParamNode.cpp:47
size_t m_DirtyID
Definition: Grid.h:104
#define IS_TERRAIN_PASSABLE(item, classmask)
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
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
fixed GetSlopeFixed(ssize_t i, ssize_t j) const
Definition: Terrain.cpp:323
virtual void Serialize(ISerializer &serialize)
virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const Goal &goal, pass_class_t passClass, cost_class_t costClass, entity_id_t notify)
Asynchronous version of ComputePath.
std::vector< std::vector< fixed > > m_MoveSpeeds
CFixedVector2D u
void ProcessLongRequests(const std::vector< AsyncLongPathRequest > &longRequests)
Definition: path.h:75
CFixed Sqrt() const
Definition: Fixed.h:317
CFixed Multiply(CFixed n) const
Multiply by a CFixed.
Definition: Fixed.h:290
std::map< std::string, cost_class_t > m_UnitCostClassTags
#define SAFE_DELETE(p)
delete memory ensuing from new and set the pointer to zero (thus making double-frees safe / a no-op) ...
std::vector< AsyncLongPathRequest > m_AsyncLongPathRequests
virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal &goal, pass_class_t passClass, cost_class_t costClass, Path &ret)
#define PROFILE(name)
Definition: Profile.h:195
ICmpPathfinder::Goal goal
const int DEFAULT_MOVE_COST
virtual void SetDebugOverlay(bool enabled)
Toggle the storage and rendering of debug info.
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
virtual pass_class_t GetPassabilityClass(const std::string &name)
Get the tag for a given passability class name.
std::map< std::string, pass_class_t > m_PassClassMasks
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
fixed GetExactGroundLevelFixed(fixed x, fixed z) const
Definition: Terrain.cpp:398
virtual void Init(const CParamNode &paramNode)
static CFixed FromInt(int n)
Definition: Fixed.h:136
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
virtual u32 ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal &goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t controller, entity_id_t notify)
Asynchronous version of ComputeShortPath (using ControlGroupObstructionFilter).
void PostMessage(entity_id_t ent, const CMessage &msg) const
Send a message, targeted at a particular entity.
#define u16
Definition: types.h:40
ICmpPathfinder::Goal goal
enum ICmpPathfinder::Goal::Type type
ICmpPathfinder::cost_class_t costClass
virtual ICmpObstruction::EFoundationCheck CheckUnitPlacement(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass)
Check whether a unit placed here is valid and doesn&#39;t hit any obstructions or impassable terrain...
#define u32
Definition: types.h:41
Sent by CCmpPathfinder after async path requests.
Definition: MessageTypes.h:354
static void TileCenter(u16 i, u16 j, entity_pos_t &x, entity_pos_t &z)
Returns the position of the center of the given tile.
SceneCollector & collector
Definition: MessageTypes.h:155
virtual void Deinit()
void ProcessShortRequests(const std::vector< AsyncShortPathRequest > &shortRequests)
T & get(int i, int j) const
Definition: Grid.h:93
virtual bool Rasterise(Grid< u8 > &grid)=0
Convert the current set of shapes onto a grid.
#define GET_COST_CLASS(item)
void operator()(S &serialize, const char *name, AsyncLongPathRequest &value)
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
fixed DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:53
virtual u16 GetTilesPerSide()=0
Returns number of tiles per side on the terrain.
u16 TerrainTile
bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Checks if a point is inside the given rotated square or rectangle.
Definition: Geometry.cpp:28
virtual cost_class_t GetCostClass(const std::string &name)
Get the tag for a given movement cost class name.
void NumberU16_Unbounded(const char *name, uint16_t value)
Serialize a number.
Definition: ISerializer.h:161
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
virtual void Submit(CPatch *patch)=0
Submit a terrain patch that is part of the scene.
virtual const Grid< u16 > & GetPassabilityGrid()
const int PASS_CLASS_BITS
std::map< std::string, CParamNode > ChildrenMap
Definition: ParamNode.h:115
virtual void ComputeShortPath(const IObstructionTestFilter &filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal &goal, pass_class_t passClass, Path &ret)
Standard representation for all types of shapes, for use with geometry processing code...
virtual void NumberU16_Unbounded(const char *name, uint16_t &out)
void set(int i, int j, const T &value)
Definition: Grid.h:85
CComponentManager & GetComponentManager() const
Definition: SimContext.cpp:35
CTerrain & GetTerrain() const
Definition: SimContext.cpp:51
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:40
virtual void FinishAsyncRequests()
Finish computing asynchronous path requests and send the CMessagePathResult messages.
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)