UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
routing.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 Copyright (C) 1997-2001 Id Software, Inc.
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 See the GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 
26 */
27 
28 #include "tracing.h"
29 #include "routing.h"
30 #include "common.h"
31 
32 /*
33 ===============================================================================
34 MAP TRACING DEBUGGING TABLES
35 ===============================================================================
36 */
37 
38 bool debugTrace = false;
39 
40 /*
41 ==========================================================
42  LOCAL CONSTANTS
43 ==========================================================
44 */
45 
46 #define RT_NO_OPENING -1
47 
48 /* Width of the box required to stand in a cell by an actor's feet. */
49 #define halfMicrostepSize (PATHFINDING_MICROSTEP_SIZE / 2 - DIST_EPSILON)
50 /* This is a template for the extents of the box used by an actor's feet. */
52 
53 /* Width of the box required to stand in a cell by an actor's torso. */
54 #define half1x1Width (UNIT_SIZE * 1 / 2 - WALL_SIZE - DIST_EPSILON)
55 #define half2x2Width (UNIT_SIZE * 2 / 2 - WALL_SIZE - DIST_EPSILON)
56 /* These are templates for the extents of the box used by an actor's torso. */
59 
60 /*
61 ==========================================================
62  LOCAL TYPES
63 ==========================================================
64 */
65 #if defined(COMPILE_MAP)
66  #define RT_COMPLETEBOXTRACE_SIZE(mapTiles, b, list, lvl) TR_SingleTileBoxTrace((mapTiles), Line(), (b), (lvl), MASK_NO_LIGHTCLIP, 0)
67  #define RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, line, b, list) TR_SingleTileBoxTrace((mapTiles), (line), (b), TRACE_ALL_LEVELS, MASK_IMPASSABLE, MASK_PASSABLE)
68 
69 #elif defined(COMPILE_UFO)
70  #define RT_COMPLETEBOXTRACE_SIZE(mapTiles, b, list, lvl) CM_EntCompleteBoxTrace((mapTiles), Line(), (b), (lvl), MASK_NO_LIGHTCLIP, 0, (list))
71  #define RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, line, b, list) CM_EntCompleteBoxTrace((mapTiles), (line), (b), TRACE_ALL_LEVELS, MASK_IMPASSABLE, MASK_PASSABLE, (list))
72 
73 #else
74  #error Either COMPILE_MAP or COMPILE_UFO must be defined in order for tracing.c to work.
75 #endif
76 
81 {
82 public:
86  const char** list;
88  RoutingData (mapTiles_t* mapTiles, Routing& r, actorSizeEnum_t actorSize, const char** list);
89 };
90 
91 RoutingData::RoutingData (mapTiles_t* mapTiles, Routing& r, actorSizeEnum_t actorSize, const char** list) : routing(r)
92 {
93  this->mapTiles = mapTiles;
94  this->actorSize = actorSize;
95  this->list = list;
96 }
97 
98 static inline void RT_StepupSet (RoutingData* rtd, const int x, const int y, const int z, const int dir, const int val)
99 {
100  rtd->routing.setStepup(rtd->actorSize, x, y, z, dir, val);
101 }
102 
103 static inline void RT_ConnSetNoGo (RoutingData* rtd, const int x, const int y, const int z, const int dir)
104 {
105  rtd->routing.setConn(rtd->actorSize, x, y, z, dir, 0);
106  rtd->routing.setStepup(rtd->actorSize, x, y, z, dir, PATHFINDING_NO_STEPUP);
107 }
108 
113 typedef struct place_s {
115  int floor;
116  int ceiling;
117  int floorZ;
118  bool usable;
120  inline bool isUsable (void) const
121  {
122  return usable;
123  }
124 } place_t;
125 
126 static inline void RT_PlaceInit (const Routing& routing, const actorSizeEnum_t actorSize, place_t* p, const int x, const int y, const int z)
127 {
128  p->cell[0] = x;
129  p->cell[1] = y;
130  p->cell[2] = z;
131  const int relCeiling = routing.getCeiling(actorSize, p->cell);
132  p->floor = routing.getFloor(actorSize, x, y, z) + z * CELL_HEIGHT;
133  p->ceiling = relCeiling + z * CELL_HEIGHT;
134  p->floorZ = std::max(0, p->floor / CELL_HEIGHT) ;
135  p->usable = (relCeiling && p->floor > -1 && p->ceiling - p->floor >= PATHFINDING_MIN_OPENING) ? true : false;
136 }
137 
138 static inline bool RT_PlaceDoesIntersectEnough (const place_t* p, const place_t* other)
139 {
140  return (std::min(p->ceiling, other->ceiling) - std::max(p->floor, other->floor) >= PATHFINDING_MIN_OPENING);
141 }
142 
149 static inline int RT_PlaceIsShifted (const place_t* p, const place_t* other)
150 {
151  if (!p->isUsable() || !other->isUsable())
152  return 0;
153  if (p->floor < other->floor && p->ceiling < other->ceiling)
154  return 1; /* stepping up */
155  if (p->floor > other->floor && p->ceiling > other->ceiling)
156  return 2; /* stepping down */
157  return 0;
158 }
159 
164 typedef struct opening_s {
165  int size;
166  int base;
167  int stepup;
168  int invstepup;
169 } opening_t;
170 
171 /*
172 ==========================================================
173  GRID ORIENTED MOVEMENT AND SCANNING
174 ==========================================================
175 */
176 
177 #ifdef DEBUG
178 
189 static void RT_DumpMap (const Routing& routing, actorSizeEnum_t actorSize, int lx, int ly, int lz, int hx, int hy, int hz)
190 {
191  Com_Printf("\nRT_DumpMap (%i %i %i) (%i %i %i)\n", lx, ly, lz, hx, hy, hz);
192  for (int z = hz; z >= lz; --z) {
193  Com_Printf("\nLayer %i:\n ", z);
194  for (int x = lx; x <= hx; ++x) {
195  Com_Printf("%9i", x);
196  }
197  Com_Printf("\n");
198  for (int y = hy; y >= ly; --y) {
199  Com_Printf("%3i ", y);
200  for (int x = lx; x <= hx; ++x) {
201  Com_Printf("%s%s%s%s "
202  , RT_CONN_NX(routing, actorSize, x, y, z) ? "w" : " "
203  , RT_CONN_PY(routing, actorSize, x, y, z) ? "n" : " "
204  , RT_CONN_NY(routing, actorSize, x, y, z) ? "s" : " "
205  , RT_CONN_PX(routing, actorSize, x, y, z) ? "e" : " "
206  );
207  }
208  Com_Printf("\n");
209  }
210  }
211 }
212 
217 void RT_DumpWholeMap (mapTiles_t* mapTiles, const Routing& routing)
218 {
219  AABB mapBox;
220  RT_GetMapSize(mapTiles, mapBox);
221 
222  /* convert the coords */
223  pos3_t start, end;
224  VecToPos(mapBox.mins, start);
225  VecToPos(mapBox.maxs, end);
226  start[0]--;
227  start[1]--;
228 
229  /* Dump the client map */
230  RT_DumpMap(routing, ACTOR_SIZE_NORMAL, start[0], start[1], start[2], end[0], end[1], end[2]);
231 }
232 #endif
233 
237 bool RT_CanActorStandHere (const Routing& routing, const int actorSize, const pos3_t pos)
238 {
239  if (routing.getCeiling(actorSize, pos) - routing.getFloor(actorSize, pos) >= PLAYER_STANDING_HEIGHT / QUANT)
240  return true;
241  else
242  return false;
243 }
244 
254 {
255  const vec3_t normal = {UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2};
256  pos3_t start, end, test;
257 
258  /* Initialize start, end, and normal */
259  VectorClear(start);
261 
262  for (int i = 0; i < 3; i++) {
263  AABB box;
264  /* Lower positive boundary */
265  while (end[i] > start[i]) {
266  /* Adjust ceiling */
267  VectorCopy(start, test);
268  test[i] = end[i];
269  /* Prep boundary box */
270  PosToVec(test, box.mins);
271  VectorSubtract(box.mins, normal, box.mins);
272  PosToVec(end, box.maxs);
273  VectorAdd(box.maxs, normal, box.maxs);
274  /* Test for stuff in a small box, if there is something then exit while */
275  const trace_t trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, &box, nullptr, TRACE_VISIBLE_LEVELS);
276  if (trace.fraction < 1.0)
277  break;
278  /* There is nothing, lower the boundary. */
279  end[i]--;
280  }
281 
282  /* Raise negative boundary */
283  while (end[i] > start[i]) {
284  /* Adjust ceiling */
285  VectorCopy(end, test);
286  test[i] = start[i];
287 
288  /* Prep boundary box */
289  PosToVec(start, box.mins);
290  VectorSubtract(box.mins, normal, box.mins);
291  PosToVec(test, box.maxs);
292  VectorAdd(box.maxs, normal, box.maxs);
293  box.maxs[i] -= DIST_EPSILON; /* stay away from the upper bound just a little bit, so we don't catch a touching brush */
294 
295  /* Test for stuff in the box, if there is something then exit while */
296  const trace_t trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, &box, nullptr, TRACE_VISIBLE_LEVELS);
297  if (trace.fraction < 1.0)
298  break;
299  /* There is nothing, raise the boundary. */
300  start[i]++;
301  }
302  }
303 
304  /* Com_Printf("Extents: (%i, %i, %i) to (%i, %i, %i)\n", start[0], start[1], start[2], end[0], end[1], end[2]); */
305 
306  /* convert to vectors */
307  PosToVec(start, mapBox.mins);
308  PosToVec(end, mapBox.maxs);
309 
310  /* Stretch to the exterior edges of our extents */
311  VectorSubtract(mapBox.mins, normal, mapBox.mins);
312  VectorAdd(mapBox.maxs, normal, mapBox.maxs);
313 }
314 
315 
316 /*
317 ===============================================================================
318 NEW MAP TRACING FUNCTIONS
319 ===============================================================================
320 */
321 
331 bool RT_AllCellsBelowAreFilled (const Routing& routing, const int actorSize, const pos3_t pos)
332 {
333  /* the -1 level is considered solid ground */
334  if (pos[2] == 0)
335  return true;
336 
337  for (int i = 0; i < pos[2]; i++) {
338  if (routing.getCeiling(actorSize, pos[0], pos[1], i) != 0)
339  return false;
340  }
341  return true;
342 }
343 
360 int RT_CheckCell (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int z, const char** list)
361 {
362  /* Width of the box required to stand in a cell by an actor's torso. */
363  const float halfActorWidth = UNIT_SIZE * actorSize / 2 - WALL_SIZE - DIST_EPSILON;
364  /* This is a template for the extents of the box used by an actor's legs. */
365  const AABB legBox(-halfMicrostepSize, -halfMicrostepSize, 0,
367  /* This is a template for the extents of the box used by an actor's torso. */
368  const AABB torsoBox(-halfActorWidth, -halfActorWidth, QuantToModel(PATHFINDING_LEGROOMHEIGHT),
369  halfActorWidth, halfActorWidth, QuantToModel(PATHFINDING_MIN_OPENING) - DIST_EPSILON * 2);
370  /* This is a template for the ceiling trace after an actor's torso space has been found. */
371  const AABB ceilBox(-halfActorWidth, -halfActorWidth, 0,
372  halfActorWidth, halfActorWidth, 0);
373 
374  vec3_t start, end; /* Start and end of the downward traces. */
375  vec3_t tstart, tend; /* Start and end of the upward traces. */
376  AABB box; /* Holds the exact bounds to be traced for legs and torso. */
377  pos3_t pos;
378  float bottom, top; /* Floor and ceiling model distances from the cell base. (in mapunits) */
379 
380  assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
381  /* x and y cannot exceed PATHFINDING_WIDTH - actorSize */
382  assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
383  assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
384  assert(z < PATHFINDING_HEIGHT);
385 
386  /* calculate tracing coordinates */
387  VectorSet(pos, x, y, z);
388  SizedPosToVec(pos, actorSize, end); /* end is now at the center of the cells the actor occupies. */
389 
390  /* prepare fall down check */
391  VectorCopy(end, start);
392  /*
393  * Adjust these points so that start to end is from the top of the cell to the bottom of the model.
394  */
395 #ifdef DEBUG
396  float initial = start[2] + UNIT_HEIGHT / 2; /* This is the top-most starting point in this cell. */
397 #endif
398  start[2] += UNIT_HEIGHT / 2 - QUANT; /* This one QUANT unit below initial. */
399  end[2] = -UNIT_HEIGHT * 2; /* To the bottom of the model! (Plus some for good measure) */
400 
401  /*
402  * Trace for a floor. Steps:
403  * 1. Start at the top of the designated cell and scan toward the model's base.
404  * 2. If we do not find a brush, then this cell is bottomless and not enterable.
405  * 3. We have found an upward facing brush. Scan up PATHFINDING_LEGROOMHEIGHT height.
406  * 4. If we find anything, then this space is too small of an opening. Restart just below our current floor.
407  * 5. Trace up towards the model ceiling with a box as large as the actor. The first obstruction encountered
408  * marks the ceiling. If there are no obstructions, the model ceiling is the ceiling.
409  * 6. If the opening between the floor and the ceiling is not at least PATHFINDING_MIN_OPENING tall, then
410  * restart below the current floor.
411  */
412  for (;;) { /* Loop forever, we will exit if we hit the model bottom or find a valid floor. */
413 
414  /* Check if a previous iteration has brought us too close to the bottom of the model. */
415  if (start[2] <= QuantToModel(PATHFINDING_MIN_OPENING)) {
416  /* There is no useable brush underneath this starting point. */
417  routing.setFilled(actorSize, x, y, 0, z); /* mark all cells to the model base as filled. */
418  return 0; /* return (a z-value of)0 to indicate we just scanned the model bottom. */
419  }
420 
421  trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(start, end), &footBox, list);
422  if (tr.fraction >= 1.0) { /* If there is no brush underneath this starting point, */
423  routing.setFilled(actorSize, x, y, 0, z); /* mark all cells to the model base as filled. */
424  return 0; /* return (a z-value of)0 to indicate we just scanned the model bottom. */
425  }
426 
427  /* We have hit a brush that faces up and can be stood on. A potential floor. Look for a ceiling. */
428  bottom = tr.endpos[2]; /* record the floor position. */
429 
430 #ifdef DEBUG
431  assert(initial > bottom);
432 #endif
433 
434  /* Record the hit position in tstart for later use. */
435  VectorCopy(tr.endpos, tstart);
436 
437  /* Prep the start and end of the "leg room" test. */
438  box.set(legBox);
439  box.shift(tstart); /* Now box has the lower and upper required foot space extent */
440 
441  tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(), &box, list);
442  if (tr.fraction < 1.0) {
443  /*
444  * There is a premature obstruction. We can't use this as a floor.
445  * Check under start. We need to have at least the minimum amount of clearance from our ceiling,
446  * So start at that point.
447  */
448  start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
449  /* Restart with the new start[] value */
450  continue;
451  }
452 
453  /* Prep the start and end of the "torso room" test. */
454  box.set(torsoBox);
455  box.shift(tstart); /* Now box has the lower and upper required torso space extent */
456 
457  tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(), &box, list);
458  if (tr.fraction < 1.0) {
459  /*
460  * There is a premature obstruction. We can't use this as a floor.
461  * Check under start. We need to have at least the minimum amount of clearance from our ceiling,
462  * So start at that point.
463  */
464  start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
465  /* Restart */
466  continue;
467  }
468 
469  /*
470  * If we are here, then the immediate floor is unobstructed MIN_OPENING units high.
471  * This is a valid floor. Find the actual ceiling.
472  */
473 
474  tstart[2] = box.maxs[2]; /* The box trace for height starts at the top of the last trace. */
475  VectorCopy(tstart, tend);
476  tend[2] = PATHFINDING_HEIGHT * UNIT_HEIGHT; /* tend now reaches the model ceiling. */
477 
478  tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(tstart, tend), &ceilBox, list);
479 
480  /* We found the ceiling. */
481  top = tr.endpos[2];
482 
483  /*
484  * There is one last possibility:
485  * If our found ceiling is above the cell we started the scan in, then we may have scanned up through another
486  * floor (one sided brush). If this is the case, we set the ceiling to QUANT below the floor of the above
487  * ceiling if it is lower than our found ceiling.
488  */
489  if (tr.endpos[2] > (z + 1) * UNIT_HEIGHT) {
490  const float topf = (z + 1) * UNIT_HEIGHT + QuantToModel(routing.getFloor(actorSize, x, y, z + 1) - 1);
491  top = std::min(tr.endpos[2], topf);
492  }
493 
494  /* We found the ceiling. */
495  top = tr.endpos[2];
496 
497  /* exit the infinite while loop */
498  break;
499  }
500 
501  UFO_assert(bottom <= top, "\nassert(bottom <= top): x=%i y=%i bottom=%f top=%f\n", x, y, bottom, top);
502 
503  /* top and bottom are absolute model heights. Find the actual cell z coordinates for these heights.
504  * ...but before rounding, give back the DIST_EPSILON that was added by the trace.
505  * Actually, we have to give back two DIST_EPSILON to prevent rounding issues */
506  bottom -= 2 * DIST_EPSILON;
507  top += 2 * DIST_EPSILON;
508  const int bottomQ = ModelFloorToQuant(bottom); /* Convert to QUANT units to ensure the floor is rounded up to the correct value. */
509  const int topQ = ModelCeilingToQuant(top); /* Convert to QUANT units to ensure the floor is rounded down to the correct value. */
510  const int fz = floorf(bottomQ / CELL_HEIGHT); /* Ensure we round down to get the bottom-most affected cell */
512  const int cz = std::min(z, (int)(floorf((topQ - 1) / CELL_HEIGHT))); /* Use the lower of z or the calculated ceiling */
513 
514  assert(fz <= cz);
515 
516  /* Last, update the floors and ceilings of cells from (x, y, fz) to (x, y, cz) */
517  for (int i = fz; i <= cz; i++) {
518  /* Round up floor to keep feet out of model. */
519  routing.setFloor(actorSize, x, y, i, bottomQ - i * CELL_HEIGHT);
520  /* Round down ceiling to heep head out of model. Also offset by floor and max at 255. */
521  routing.setCeiling(actorSize, x, y, i, topQ - i * CELL_HEIGHT);
522  }
523 
524  /* Also, update the floors of any filled cells immediately above the ceiling up to our original cell. */
525  routing.setFilled(actorSize, x, y, cz + 1, z);
526 
527  /* Return the lowest z coordinate that we updated floors for. */
528  return fz;
529 }
530 
531 
541 static int RT_FillPassageData (RoutingData* rtd, const int dir, const int x, const int y, const int z, const int openingSize, const int openingBase, const int stepup)
542 {
543  const int openingTop = openingBase + openingSize;
544 
545  /* Final interpretation:
546  * We now have the floor and the ceiling of the passage traveled between the two cells.
547  * This span may cover many cells vertically. We can use this to our advantage:
548  * +Like in the floor tracing, we can assign the direction value for multiple cells and
549  * skip some scans.
550  * +The value of each current cell will list the max allowed height of an actor in the passageway,
551  * which also can be used to see if an actor can fly upward.
552  * +The allowed height will be based off the floor in the cell or the bottom of the cell; we do not
553  * want super tall characters to fly through ceilings.
554  * +To see if an actor can fly down, we check the cells on level down to see if the diagonal movement
555  * can be made and that both have ceilings above the current level.
556  */
557 
559  const int fz = z;
560  int cz = ceil((float)openingTop / CELL_HEIGHT) - 1;
561  cz = std::min(PATHFINDING_HEIGHT - 1, cz);
562 
563  /* last chance- if cz < z, then bail (and there is an error with the ceiling data somewhere */
564  if (cz < z) {
565  /* We can't go this way. */
566  RT_ConnSetNoGo(rtd, x, y, z, dir); /* Passage found but below current cell */
567  return z;
568  }
569 
570  /* Passage found */
571 
572  assert(fz <= z && z <= cz);
573 
574  /* Last, update the connections of cells from (x, y, fz) to (x, y, cz) for direction dir */
575  for (int i = fz; i <= cz; i++) {
576  /* Offset from the floor or the bottom of the current cell, whichever is higher. */
577  const int oh = openingTop - std::max(openingBase, i * CELL_HEIGHT);
578  /* Only if > 0 */
579  assert (oh >= 0);
580  rtd->routing.setConn(rtd->actorSize, x, y, i, dir, oh);
581  /* The stepup is 0 for all cells that are not at the floor. */
582  RT_StepupSet(rtd, x, y, i, dir, 0);
583  }
584 
585  RT_StepupSet(rtd, x, y, z, dir, stepup);
586 
587  /*
588  * Return the highest z coordinate scanned- cz if fz==cz, z==cz, or the floor in cz is negative.
589  * Otherwise cz - 1 to recheck cz in case there is a floor in cz with its own ceiling.
590  */
591  if (fz == cz || z == cz || rtd->routing.getFloor(rtd->actorSize, x, y, cz) < 0)
592  return cz;
593  return cz - 1;
594 }
595 
603 static trace_t RT_ObstructedTrace (const RoutingData* rtd, const Line& traceLine, int hi, int lo)
604 {
605  AABB box;
606  const float halfActorWidth = UNIT_SIZE * rtd->actorSize / 2 - WALL_SIZE - DIST_EPSILON;
607 
608  /* Configure the box trace extents. The box is relative to the original floor. */
609  VectorSet(box.maxs, halfActorWidth, halfActorWidth, QuantToModel(hi) - DIST_EPSILON);
610  VectorSet(box.mins, -halfActorWidth, -halfActorWidth, QuantToModel(lo) + DIST_EPSILON);
611 
612  /* perform the trace, then return true if the trace was obstructed. */
613  return RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, traceLine, &box, rtd->list);
614 }
615 
616 
626 static int RT_FindOpeningFloorFrac (const RoutingData* rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
627 {
628  vec3_t mstart, mend;
629  const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
630 
631  /* Position mstart and mend at the fraction point */
632  VectorInterpolation(start, end, frac, mstart);
633  VectorCopy(mstart, mend);
634  mstart[2] = QuantToModel(startingHeight) + (QUANT / 2); /* Set at the starting height, plus a little more to keep us off a potential surface. */
635  mend[2] = -QUANT; /* Set below the model. */
636 
637  const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(mstart, mend), box, rtd->list);
638 
639  /* OK, now we have the floor height value in tr.endpos[2].
640  * Divide by QUANT and round up.
641  */
642  return ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
643 }
644 
645 
655 static int RT_FindOpeningCeilingFrac (const RoutingData* rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
656 {
657  vec3_t mstart, mend;
658  const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
660  /* Position mstart and mend at the midpoint */
661  VectorInterpolation(start, end, frac, mstart);
662  VectorCopy(mstart, mend);
663  mstart[2] = QuantToModel(startingHeight) - (QUANT / 2); /* Set at the starting height, minus a little more to keep us off a potential surface. */
664  mend[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT + QUANT; /* Set above the model. */
665 
666  const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(mstart, mend), box, rtd->list);
667 
668  /* OK, now we have the floor height value in tr.endpos[2].
669  * Divide by QUANT and round down. */
670  return ModelCeilingToQuant(tr.endpos[2] + DIST_EPSILON);
671 }
672 
673 
683 static int RT_FindOpeningFloor (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int floorLimit)
684 {
685  /* Look for additional space below init_bottom, down to lowest_bottom. */
686  int midfloor;
687 
688  if (start[0] == end[0] || start[1] == end[1]) {
689  /* For orthogonal dirs, find the height at the midpoint. */
690  midfloor = RT_FindOpeningFloorFrac(rtd, start, end, 0.5, startingHeight);
691  } else {
692  /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
693  /* 1/3 point */
694  midfloor = RT_FindOpeningFloorFrac(rtd, start, end, 0.33, startingHeight);
695 
696  /* 2/3 point */
697  const int midfloor2 = RT_FindOpeningFloorFrac(rtd, start, end, 0.66, startingHeight);
698  midfloor = std::max(midfloor, midfloor2);
699  }
700 
701  /* return the highest floor. */
702  return std::max(floorLimit, midfloor);
703 }
704 
705 
715 static int RT_FindOpeningCeiling (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int ceilLimit)
716 {
717  int midceil;
718 
719  if (start[0] == end[0] || start[1] == end[1]) {
720  /* For orthogonal dirs, find the height at the midpoint. */
721  midceil = RT_FindOpeningCeilingFrac(rtd, start, end, 0.5, startingHeight);
722  } else {
723  /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
724  /* 1/3 point */
725  midceil = RT_FindOpeningCeilingFrac(rtd, start, end, 0.33, startingHeight);
726 
727  /* 2/3 point */
728  const int midceil2 = RT_FindOpeningCeilingFrac(rtd, start, end, 0.66, startingHeight);
729  midceil = std::min(midceil, midceil2);
730  }
731 
732  /* return the lowest ceiling. */
733  return std::min(ceilLimit, midceil);
734 }
735 
736 
737 static int RT_CalcNewZ (const RoutingData* rtd, const int ax, const int ay, const int top, const int hi)
738 {
739  int temp_z = floorf((hi - 1) / CELL_HEIGHT);
740  temp_z = std::min(temp_z, PATHFINDING_HEIGHT - 1);
741  int adj_lo = rtd->routing.getFloor(rtd->actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
742  if (adj_lo > hi) {
743  temp_z--;
744  adj_lo = rtd->routing.getFloor(rtd->actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
745  }
751  if (adj_lo >= 0 && top - adj_lo >= PATHFINDING_MIN_OPENING - PATHFINDING_MIN_STEPUP) {
752  return floorf(adj_lo / CELL_HEIGHT); /* Found floor in destination cell */
753  }
754 
755  return RT_NO_OPENING; /* Skipping found floor in destination cell- not enough opening */
756 }
757 
758 
774 static int RT_TraceOpening (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int ax, const int ay, const int bottom, const int top, int lo, int hi, int* foundLow, int* foundHigh)
775 {
776  const trace_t tr = RT_ObstructedTrace(rtd, Line(start, end), hi, lo);
777  if (tr.fraction >= 1.0) {
778  lo = RT_FindOpeningFloor(rtd, start, end, lo, bottom);
779  hi = RT_FindOpeningCeiling(rtd, start, end, hi, top);
780  if (hi - lo >= PATHFINDING_MIN_OPENING) {
781  if (lo == -1) {
782  /* Bailing- no floor in destination cell. */
783  *foundLow = *foundHigh = 0;
784  return RT_NO_OPENING;
785  }
786  /* This opening works, use it! */
787  *foundLow = lo;
788  *foundHigh = hi;
789  /* Find the floor for the highest adjacent cell in this passage. */
790  const int tempZ = RT_CalcNewZ(rtd, ax, ay, top, hi);
791  if (tempZ != RT_NO_OPENING)
792  return tempZ;
793  }
794  }
795  *foundLow = *foundHigh = hi;
796  return RT_NO_OPENING;
797 }
798 
799 
812 static int RT_FindOpening (RoutingData* rtd, const place_t* from, const int ax, const int ay, const int bottom, const int top, int* foundLow, int* foundHigh)
813 {
814  if (bottom == -1) {
815  /* Bailing- no floor in current cell. */
816  *foundLow = *foundHigh = 0;
817  return RT_NO_OPENING;
818  }
819 
820  vec3_t start, end;
821  pos3_t pos;
822 
823  /* Initialize the starting vector */
824  SizedPosToVec(from->cell, rtd->actorSize, start);
825 
826  /* Initialize the ending vector */
827  VectorSet(pos, ax, ay, from->cell[2]);
828  SizedPosToVec(pos, rtd->actorSize, end);
829 
830  /* Initialize the z component of both vectors */
831  start[2] = end[2] = 0;
832 
833  /* ----- sky trace ------ */
834  /* shortcut: if both ceilings are the sky, we can check for walls
835  * AND determine the bottom of the passage in just one trace */
836  if (from->ceiling >= PATHFINDING_HEIGHT * CELL_HEIGHT
837  && from->cell[2] * CELL_HEIGHT + rtd->routing.getCeiling(rtd->actorSize, ax, ay, from->cell[2]) >= PATHFINDING_HEIGHT * CELL_HEIGHT) {
838  vec3_t sky, earth;
839  const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
840  int tempBottom;
841 
842  VectorInterpolation(start, end, 0.5, sky); /* center it halfway between the cells */
843  VectorCopy(sky, earth);
844  sky[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT; /* Set to top of model. */
845  earth[2] = QuantToModel(bottom);
846 
847  const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(sky, earth), box, rtd->list);
848  tempBottom = ModelFloorToQuant(tr.endpos[2]);
849  if (tempBottom <= bottom + PATHFINDING_MIN_STEPUP) {
850  const int hi = bottom + PATHFINDING_MIN_OPENING;
851  /* Found opening with sky trace. */
852  *foundLow = tempBottom;
853  *foundHigh = CELL_HEIGHT * PATHFINDING_HEIGHT;
854  return RT_CalcNewZ(rtd, ax, ay, top, hi);
855  }
856  }
857  /* Warning: never try to make this an 'else if', or 'arched entry' situations will fail !! */
858 
859  /* ----- guaranteed opening trace ------ */
860  /* Now calculate the "guaranteed" opening, if any. If the opening from
861  * the floor to the ceiling is not too tall, there must be a section that
862  * will always be vacant if there is a usable passage of any size and at
863  * any height. */
864  int tempZ;
865  if (top - bottom < PATHFINDING_MIN_OPENING * 2) {
866  const int lo = top - PATHFINDING_MIN_OPENING;
867  const int hi = bottom + PATHFINDING_MIN_OPENING;
868 
869  tempZ = RT_TraceOpening(rtd, start, end, ax, ay, bottom, top, lo, hi, foundLow, foundHigh);
870  } else {
871  /* ----- brute force trace ------ */
872  /* There is no "guaranteed" opening, brute force search. */
873  int lo = bottom;
874  tempZ = 0;
875  while (lo <= top - PATHFINDING_MIN_OPENING) {
876  /* Check for a 1 QUANT opening. */
877  tempZ = RT_TraceOpening(rtd, start, end, ax, ay, bottom, top, lo, lo + 1, foundLow, foundHigh);
878  if (tempZ != RT_NO_OPENING)
879  break;
880  /* Credit to Duke: We skip the minimum opening, as if there is a
881  * viable opening, even one slice above, that opening would be open. */
883  }
884  }
885  return tempZ;
886 }
887 
888 
900 static int RT_MicroTrace (RoutingData* rtd, const place_t* from, const int ax, const int ay, const int az, const int stairwaySituation, opening_t* opening)
901 {
902  /* OK, now we have a viable shot across. Run microstep tests now. */
903  /* Now calculate the stepup at the floor using microsteps. */
904  const int top = opening->base + opening->size;
905  signed char bases[UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE + 1];
906  /* Shortcut the value of UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE. */
907  const int steps = UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE;
908  vec3_t start, end;
909  pos3_t pos;
910 
911  /* First prepare the two known end values. */
912  bases[0] = from->floor;
913  const int floorVal = rtd->routing.getFloor(rtd->actorSize, ax, ay, az);
914  bases[steps] = std::max(0, floorVal) + az * CELL_HEIGHT;
915 
916  /* Initialize the starting vector */
917  SizedPosToVec(from->cell, rtd->actorSize, start);
918 
919  /* Initialize the ending vector */
920  VectorSet(pos, ax, ay, az);
921  SizedPosToVec(pos, rtd->actorSize, end);
922 
923  /* Now prep the z values for start and end. */
924  start[2] = QuantToModel(opening->base) + 1;
925  end[2] = -QUANT;
926 
927  /* Memorize the start and end x,y points */
928  const float sx = start[0];
929  const float sy = start[1];
930  const float ex = end[0];
931  const float ey = end[1];
932 
933  int newBottom = std::max(bases[0], bases[steps]);
934  /* Now calculate the rest of the microheights. */
935  for (int i = 1; i < steps; i++) {
936  start[0] = end[0] = sx + (ex - sx) * (i / (float)steps);
937  start[1] = end[1] = sy + (ey - sy) * (i / (float)steps);
938 
939  /* perform the trace, then return true if the trace was obstructed. */
940  const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(start, end), &footBox, rtd->list);
941  if (tr.fraction >= 1.0f) {
942  bases[i] = -1;
943  } else {
944  bases[i] = ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
945  /* Walking through glass fix:
946  * It is possible to have an obstruction that can be skirted around diagonally
947  * because the microtraces are so tiny. But, we have a full size trace in opening->base
948  * that apporoximates where legroom ends. If the found floor of the middle microtrace is
949  * too low, then set it to the worst case scenario floor based on base->opening.
950  */
951  if (i == floor(steps / 2.0) && bases[i] < opening->base - PATHFINDING_MIN_STEPUP) {
952  if (debugTrace)
953  Com_Printf("Adjusting middle trace- the known base is too high. \n");
954  bases[i] = opening->base - PATHFINDING_MIN_STEPUP;
955  }
956  }
957 
958  if (debugTrace)
959  Com_Printf("Microstep %i from (%f, %f, %f) to (%f, %f, %f) = %i [%f]\n",
960  i, start[0], start[1], start[2], end[0], end[1], end[2], bases[i], tr.endpos[2]);\
961 
962  newBottom = std::max(newBottom, (int)bases[i]);
963  }
964 
965  if (debugTrace)
966  Com_Printf("z:%i az:%i bottom:%i new_bottom:%i top:%i bases[0]:%i bases[%i]:%i\n", from->cell[2], az, opening->base, newBottom, top, bases[0], steps, bases[steps]);
967 
968 
970  /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
971  /* Initialize stepup. */
972  int current_h = bases[0];
973  int highest_h = -1;
974  int highest_i = 1;
975  opening->stepup = 0;
976  int skipped = 0;
977  for (int i = 1; i <= steps; i++) {
978  if (debugTrace)
979  Com_Printf("Tracing forward i:%i h:%i\n", i, current_h);
980  /* If there is a rise, use it. */
981  if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
982  if (skipped == PATHFINDING_MICROSTEP_SKIP) {
983  i = highest_i;
984  if (debugTrace)
985  Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
986  }
987  opening->stepup = std::max(opening->stepup, bases[i] - current_h);
988  current_h = bases[i];
989  highest_h = -2;
990  highest_i = i + 1;
991  skipped = 0;
992  if (debugTrace)
993  Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->stepup);
994  } else {
995  /* We are skipping this step in case the actor can step over this lower step. */
996  /* Record the step in case it is the highest of the low steps. */
997  if (bases[i] > highest_h) {
998  highest_h = bases[i];
999  highest_i = i;
1000  }
1001  if (debugTrace)
1002  Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
1003  /* If this is the last iteration, make sure we go back and get our last stepup tests. */
1004  if (i == steps) {
1005  skipped = PATHFINDING_MICROSTEP_SKIP;
1006  i = highest_i - 1;
1007  if (debugTrace)
1008  Com_Printf(" Tripping skip counter to perform last tests.\n");
1009  }
1010  }
1011  }
1012 
1014  /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
1015  /* Initialize stepup. */
1016  current_h = bases[steps];
1017  highest_h = -1;
1018  highest_i = steps - 1;
1019  opening->invstepup = 0;
1020  skipped = 0;
1021  for (int i = steps - 1; i >= 0; i--) {
1022  if (debugTrace)
1023  Com_Printf("Tracing backward i:%i h:%i\n", i, current_h);
1024  /* If there is a rise, use it. */
1025  if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
1026  if (skipped == PATHFINDING_MICROSTEP_SKIP) {
1027  i = highest_i;
1028  if (debugTrace)
1029  Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
1030  }
1031  opening->invstepup = std::max(opening->invstepup, bases[i] - current_h);
1032  current_h = bases[i];
1033  highest_h = -2;
1034  highest_i = i - 1;
1035  skipped = 0;
1036  if (debugTrace)
1037  Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->invstepup);
1038  } else {
1039  /* We are skipping this step in case the actor can step over this lower step. */
1040  /* Record the step in case it is the highest of the low steps. */
1041  if (bases[i] > highest_h) {
1042  highest_h = bases[i];
1043  highest_i = i;
1044  }
1045  if (debugTrace)
1046  Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
1047  /* If this is the last iteration, make sure we go back and get our last stepup tests. */
1048  if (i == 0) {
1049  skipped = PATHFINDING_MICROSTEP_SKIP;
1050  i = highest_i + 1;
1051  if (debugTrace)
1052  Com_Printf(" Tripping skip counter to perform last tests.\n");
1053  }
1054  }
1055  }
1056 
1057  if (stairwaySituation) {
1058  const int middle = bases[4]; /* terrible hack by Duke. This relies on PATHFINDING_MICROSTEP_SIZE being set to 4 !! */
1059 
1060  if (stairwaySituation == 1) { /* stepping up */
1061  if (bases[1] <= middle && /* if nothing in the 1st part of the passage is higher than what's at the border */
1062  bases[2] <= middle &&
1063  bases[3] <= middle ) {
1064  if (debugTrace)
1065  Com_Printf("Addition granted by ugly stair hack-stepping up.\n");
1066  return opening->base - middle;
1067  }
1068  } else if (stairwaySituation == 2) {/* stepping down */
1069  if (bases[5] <= middle && /* same for the 2nd part of the passage */
1070  bases[6] <= middle &&
1071  bases[7] <= middle ) {
1072  if (debugTrace)
1073  Com_Printf("Addition granted by ugly stair hack-stepping down.\n");
1074  return opening->base - middle;
1075  }
1076  }
1077  }
1078 
1079  /* Return the confirmed passage opening. */
1080  return opening->base - newBottom;
1081 }
1082 
1083 
1092 static int RT_TraceOnePassage (RoutingData* rtd, const place_t* from, const place_t* to, opening_t* opening)
1093 {
1094  int hi;
1095  const int z = from->cell[2];
1096  const int lower = std::max(from->floor, to->floor);
1097  const int upper = std::min(from->ceiling, to->ceiling);
1098  const int ax = to->cell[0];
1099  const int ay = to->cell[1];
1100 
1101  RT_FindOpening(rtd, from, ax, ay, lower, upper, &opening->base, &hi);
1102  /* calc opening found so far and set stepup */
1103  opening->size = hi - opening->base;
1104  const int az = to->floorZ;
1106  /* We subtract MIN_STEPUP because that is foot space-
1107  * the opening there only needs to be the microtrace
1108  * wide and not the usual dimensions.
1109  */
1111  const int srcFloor = from->floor;
1112  const int dstFloor = rtd->routing.getFloor(rtd->actorSize, ax, ay, az) + az * CELL_HEIGHT;
1113  /* if we already have enough headroom, try to skip microtracing */
1114  if (opening->size < ACTOR_MAX_HEIGHT
1115  || abs(srcFloor - opening->base) > PATHFINDING_MIN_STEPUP
1116  || abs(dstFloor - opening->base) > PATHFINDING_MIN_STEPUP) {
1117  const int stairway = RT_PlaceIsShifted(from, to);
1118  /* This returns the total opening height, as the
1119  * microtrace may reveal more passage height from the foot space. */
1120  const int bonusSize = RT_MicroTrace(rtd, from, ax, ay, az, stairway, opening);
1121  opening->base -= bonusSize;
1122  opening->size = hi - opening->base; /* re-calculate */
1123  } else {
1124  /* Skipping microtracing, just set the stepup values. */
1125  opening->stepup = std::max(0, opening->base - srcFloor);
1126  opening->invstepup = std::max(0, opening->base - dstFloor);
1127  }
1128 
1129  /* Now place an upper bound on stepup */
1130  if (opening->stepup > PATHFINDING_MAX_STEPUP) {
1131  opening->stepup = PATHFINDING_NO_STEPUP;
1132  } else {
1133  /* Add rise/fall bit as needed. */
1134  if (az < z && opening->invstepup <= PATHFINDING_MAX_STEPUP)
1135  /* BIG_STEPDOWN indicates 'walking down', don't set it if we're 'falling' */
1136  opening->stepup |= PATHFINDING_BIG_STEPDOWN;
1137  else if (az > z)
1138  opening->stepup |= PATHFINDING_BIG_STEPUP;
1139  }
1140 
1141  /* Now place an upper bound on stepup */
1142  if (opening->invstepup > PATHFINDING_MAX_STEPUP) {
1143  opening->invstepup = PATHFINDING_NO_STEPUP;
1144  } else {
1145  /* Add rise/fall bit as needed. */
1146  if (az > z)
1147  opening->invstepup |= PATHFINDING_BIG_STEPDOWN;
1148  else if (az < z)
1149  opening->invstepup |= PATHFINDING_BIG_STEPUP;
1150  }
1151 
1152  if (opening->size >= PATHFINDING_MIN_OPENING) {
1153  return opening->size;
1154  }
1155  }
1156 
1157  opening->stepup = PATHFINDING_NO_STEPUP;
1158  opening->invstepup = PATHFINDING_NO_STEPUP;
1159  return 0;
1160 }
1161 
1169 static void RT_TracePassage (RoutingData* rtd, const int x, const int y, const int z, const int ax, const int ay, opening_t* opening)
1170 {
1172  place_t from, to, above;
1173  const place_t* placeToCheck = nullptr;
1174 
1175  RT_PlaceInit(rtd->routing, rtd->actorSize, &from, x, y, z);
1176  RT_PlaceInit(rtd->routing, rtd->actorSize, &to, ax, ay, z);
1177 
1178  const int aboveCeil = (z < PATHFINDING_HEIGHT - 1) ? rtd->routing.getCeiling(rtd->actorSize, ax, ay, z + 1) + (z + 1) * CELL_HEIGHT : to.ceiling;
1179  const int lowCeil = std::min(from.ceiling, (rtd->routing.getCeiling(rtd->actorSize, ax, ay, z) == 0 || to.ceiling - from.floor < PATHFINDING_MIN_OPENING) ? aboveCeil : to.ceiling);
1180 
1181  /*
1182  * First check the ceiling for the cell beneath the adjacent floor to see
1183  * if there is a potential opening. The difference between the
1184  * ceiling and the floor is at least PATHFINDING_MIN_OPENING tall, then
1185  * scan it to see if we can use it. If we can, then one of two things
1186  * will happen:
1187  * - The actual adjacent cell has no floor of its own, and we will walk
1188  * or fall into the cell below the adjacent cell anyway.
1189  * - There is a floor in the adjacent cell, but we will not be able to
1190  * walk into it anyway because there cannot be any steps if there is
1191  * a passage. An actor can walk down into the cell ONLY IF it's
1192  * negative stepup meets or exceeds the change in floor height.
1193  * No actors will be allowed to fall because they cannot temporarily
1194  * occupy the space beneath the floor in the adjacent cell to fall
1195  * (all actors in the cell must be ON TOP of the floor in the cell).
1196  * If there is no passage, then the obstruction may be used as steps to
1197  * climb up to the adjacent floor.
1198  */
1199  if (to.isUsable() && RT_PlaceDoesIntersectEnough(&from, &to)) {
1200  placeToCheck = &to;
1201  } else if (z < PATHFINDING_HEIGHT - 1) {
1202  RT_PlaceInit(rtd->routing, rtd->actorSize, &above, ax, ay, z + 1);
1203  if (above.isUsable() && RT_PlaceDoesIntersectEnough(&from, &above)) {
1204  placeToCheck = &above;
1205  }
1206  }
1207  if (!placeToCheck) {
1208  /* If we got here, then there is no opening from floor to ceiling. */
1209  opening->stepup = PATHFINDING_NO_STEPUP;
1210  opening->invstepup = PATHFINDING_NO_STEPUP;
1211  opening->base = lowCeil;
1212  opening->size = 0;
1213  return;
1214  }
1215 
1216  /*
1217  * Now that we got here, we know that either the opening between the
1218  * ceiling below the adjacent cell and the current floor is too small or
1219  * obstructed. Try to move onto the adjacent floor.
1220  */
1221  RT_TraceOnePassage(rtd, &from, placeToCheck, opening);
1222  if (opening->size < PATHFINDING_MIN_OPENING) {
1223  /* If we got here, then there is no useable opening from floor to ceiling. */
1224  opening->stepup = PATHFINDING_NO_STEPUP;
1225  opening->invstepup = PATHFINDING_NO_STEPUP;
1226  opening->base = lowCeil;
1227  opening->size = 0;
1228  }
1229 }
1230 
1231 
1240 static int RT_UpdateConnection (RoutingData* rtd, const int x, const int y, const int ax, const int ay, const int z, const int dir)
1241 {
1243  const int ceiling = rtd->routing.getCeiling(rtd->actorSize, x, y, z);
1244  const int adjCeiling = rtd->routing.getCeiling(rtd->actorSize, ax, ay, z);
1245  const int upperAdjCeiling = (z < PATHFINDING_HEIGHT - 1) ? rtd->routing.getCeiling(rtd->actorSize, ax, ay, z + 1) : adjCeiling;
1246 
1247  if ((adjCeiling == 0 && upperAdjCeiling == 0) || ceiling == 0) {
1248  /* We can't go this way. */
1249  RT_ConnSetNoGo(rtd, x, y, z, dir);
1250  return z;
1251  }
1252 
1257  const int absCeiling = ceiling + z * CELL_HEIGHT;
1258 // const int absAdjCeiling = adjCeiling + z * CELL_HEIGHT; /* temporarily unused */
1259  const int absExtAdjCeiling = (z < PATHFINDING_HEIGHT - 1) ? adjCeiling + (z + 1) * CELL_HEIGHT : absCeiling;
1260  const int absFloor = rtd->routing.getFloor(rtd->actorSize, x, y, z) + z * CELL_HEIGHT;
1261  const int absAdjFloor = rtd->routing.getFloor(rtd->actorSize, ax, ay, z) + z * CELL_HEIGHT;
1262 
1263  if (absCeiling < absAdjFloor || absExtAdjCeiling < absFloor) {
1264  /* We can't go this way. */
1265  RT_ConnSetNoGo(rtd, x, y, z, dir);
1266  return z;
1267  }
1268 
1270  opening_t opening;
1271  RT_TracePassage(rtd, x, y, z, ax, ay, &opening);
1272 
1276  const int newZ = RT_FillPassageData(rtd, dir, x, y, z, opening.size, opening.base, opening.stepup);
1277 
1278  return newZ;
1279 }
1280 
1281 
1292 void RT_UpdateConnectionColumn (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int dir, const char** list, const int minZ, const int maxZ)
1293 {
1294  int z = minZ;
1295  /* the essential data passed down the calltree */
1296  RoutingData rtd(mapTiles, routing, actorSize, list);
1297 
1298  /* get the neighbor cell's coordinates */
1299  const int ax = x + dvecs[dir][0];
1300  const int ay = y + dvecs[dir][1];
1301 
1302  assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
1303  assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
1304  assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
1305 
1306 #ifdef DEBUG
1307 
1308  /* just a place to place a breakpoint */
1309  if (x == 126 && y == 129 && dir == 2) {
1310  z = 7;
1311  }
1312 #endif
1313 
1314  /* if our destination cell is out of bounds, bail. */
1315  if (ax < 0 || ax > PATHFINDING_WIDTH - actorSize || ay < 0 || ay > PATHFINDING_WIDTH - actorSize) {
1316  /* We can't go this way. */
1317  RT_ConnSetNoGo(&rtd, x, y, z, dir);
1318  return;
1319  }
1320 
1321  /* Main loop */
1322  for (z = minZ; z < maxZ; z++) {
1323  /* The last z value processed by the tracing function. */
1324  const int new_z = RT_UpdateConnection(&rtd, x, y, ax, ay, z, dir);
1325  assert(new_z >= z);
1326  z = new_z;
1327  }
1328 }
1329 
1330 void RT_WriteCSVFiles (const Routing& routing, const char* baseFilename, const GridBox& box)
1331 {
1332  char filename[MAX_OSPATH], ext[MAX_OSPATH];
1333 
1334  /* An elevation files- dumps the floor and ceiling levels relative to each cell. */
1335  for (int i = 1; i <= ACTOR_MAX_SIZE; i++) {
1336  strncpy(filename, baseFilename, sizeof(filename) - 1);
1337  sprintf(ext, ".%i.elevation.csv", i);
1338  Com_DefaultExtension(filename, sizeof(filename), ext);
1339  ScopedFile f;
1340  FS_OpenFile(filename, &f, FILE_WRITE);
1341  if (!f)
1342  Sys_Error("Could not open file %s.", filename);
1343  FS_Printf(&f, ",");
1344  for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++)
1345  FS_Printf(&f, "x:%i,", x);
1346  FS_Printf(&f, "\n");
1347  for (int z = box.getMaxZ(); z >= box.getMinZ(); z--) {
1348  for (int y = box.getMaxY(); y >= box.getMinY() - i + 1; y--) {
1349  FS_Printf(&f, "z:%i y:%i,", z ,y);
1350  for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++) {
1351  /* compare results */
1352  FS_Printf(&f, "h:%i c:%i,", routing.getFloor(i, x, y, z), routing.getCeiling(i, x, y, z));
1353  }
1354  FS_Printf(&f, "\n");
1355  }
1356  FS_Printf(&f, "\n");
1357  }
1358  }
1359 
1360  /* Output the walls/passage files. */
1361  for (int i = 1; i <= ACTOR_MAX_SIZE; i++) {
1362  strncpy(filename, baseFilename, sizeof(filename) - 1);
1363  sprintf(ext, ".%i.walls.csv", i);
1364  Com_DefaultExtension(filename, sizeof(filename), ext);
1365  ScopedFile f;
1366  FS_OpenFile(filename, &f, FILE_WRITE);
1367  if (!f)
1368  Sys_Error("Could not open file %s.", filename);
1369  FS_Printf(&f, ",");
1370  for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++)
1371  FS_Printf(&f, "x:%i,", x);
1372  FS_Printf(&f, "\n");
1373  for (int z = box.getMaxZ(); z >= box.getMinZ(); z--) {
1374  for (int y = box.getMaxY(); y >= box.getMinY() - i + 1; y--) {
1375  FS_Printf(&f, "z:%i y:%i,", z, y);
1376  for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++) {
1377  /* compare results */
1378  FS_Printf(&f, "\"");
1379 
1380  /* NW corner */
1381  FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_PY(routing, i, x, y, z), RT_STEPUP_NX_PY(routing, i, x, y, z));
1382 
1383  /* N side */
1384  FS_Printf(&f, "%3i-%3i ", RT_CONN_PY(routing, i, x, y, z), RT_STEPUP_PY(routing, i, x, y, z));
1385 
1386  /* NE corner */
1387  FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_PY(routing, i, x, y, z), RT_STEPUP_PX_PY(routing, i, x, y, z));
1388 
1389  FS_Printf(&f, "\n");
1390 
1391  /* W side */
1392  FS_Printf(&f, "%3i-%3i ", RT_CONN_NX(routing, i, x, y, z), RT_STEPUP_NX(routing, i, x, y, z));
1393 
1394  /* Center - display floor height */
1395  FS_Printf(&f, "_%+2i_ ", routing.getFloor(i, x, y, z));
1396 
1397  /* E side */
1398  FS_Printf(&f, "%3i-%3i ", RT_CONN_PX(routing, i, x, y, z), RT_STEPUP_PX(routing, i, x, y, z));
1399 
1400  FS_Printf(&f, "\n");
1401 
1402  /* SW corner */
1403  FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_NY(routing, i, x, y, z), RT_STEPUP_NX_NY(routing, i, x, y, z));
1404 
1405  /* S side */
1406  FS_Printf(&f, "%3i-%3i ", RT_CONN_NY(routing, i, x, y, z), RT_STEPUP_NY(routing, i, x, y, z));
1407 
1408  /* SE corner */
1409  FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_NY(routing, i, x, y, z), RT_STEPUP_PX_NY(routing, i, x, y, z));
1410 
1411  FS_Printf(&f, "\",");
1412  }
1413  FS_Printf(&f, "\n");
1414  }
1415  FS_Printf(&f, "\n");
1416  }
1417  }
1418 }
1419 
1420 #ifdef DEBUG
1421 
1430 int RT_DebugSpecial (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int dir, const char** list)
1431 {
1432  RoutingData rtd(mapTiles, routing, actorSize, list); /* the essential data passed down the calltree */
1433 
1434  /* get the neighbor cell's coordinates */
1435  const int ax = x + dvecs[dir][0];
1436  const int ay = y + dvecs[dir][1];
1437 
1439  const int new_z = RT_UpdateConnection(&rtd, x, y, ax, ay, 0, dir);
1440  return new_z;
1441 }
1442 
1448 void RT_DebugPathDisplay (Routing& routing, actorSizeEnum_t actorSize, int x, int y, int z)
1449 {
1450  Com_Printf("data at cursor XYZ(%i, %i, %i) Floor(%i) Ceiling(%i)\n", x, y, z,
1451  routing.getFloor(actorSize, x, y, z),
1452  routing.getCeiling(actorSize, x, y, z) );
1453  Com_Printf("connections ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
1454  RT_CONN_PX(routing, actorSize, x, y, z), /* dir = 0 */
1455  RT_CONN_NX(routing, actorSize, x, y, z), /* 1 */
1456  RT_CONN_PY(routing, actorSize, x, y, z), /* 2 */
1457  RT_CONN_NY(routing, actorSize, x, y, z) ); /* 3 */
1458  Com_Printf("connections diago: (PX_PY=%i, NX_NY=%i, NX_PY=%i, PX_NY=%i))\n",
1459  RT_CONN_PX_PY(routing, actorSize, x, y, z), /* dir = 4 */
1460  RT_CONN_NX_NY(routing, actorSize, x, y, z), /* 5 */
1461  RT_CONN_NX_PY(routing, actorSize, x, y, z), /* 6 */
1462  RT_CONN_PX_NY(routing, actorSize, x, y, z) ); /* 7 */
1463  Com_Printf("stepup ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
1464  RT_STEPUP_PX(routing, actorSize, x, y, z), /* dir = 0 */
1465  RT_STEPUP_NX(routing, actorSize, x, y, z), /* 1 */
1466  RT_STEPUP_PY(routing, actorSize, x, y, z), /* 2 */
1467  RT_STEPUP_NY(routing, actorSize, x, y, z) ); /* 3 */
1468 }
1469 
1470 #endif
#define PATHFINDING_MIN_STEPUP
Definition: defines.h:314
mapTiles_t * mapTiles
Definition: routing.cpp:83
#define SizedPosToVec(p, actorSize, v)
SizedPosToVect locates the center of an actor based on size and position.
Definition: routing.h:84
An 'opening' describes the connection between two adjacent spaces where an actor can exist in a cell...
Definition: routing.cpp:164
#define VectorCopy(src, dest)
Definition: vector.h:51
int ceiling
Definition: routing.cpp:116
#define PATHFINDING_BIG_STEPDOWN
Definition: typedefs.h:246
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
pos_t getMinZ() const
Definition: mathlib.h:180
#define VectorSet(v, x, y, z)
Definition: vector.h:59
void setCeiling(const actorSizeEnum_t actorSize, const int x, const int y, const int z, const int val)
Definition: typedefs.h:269
#define half2x2Width
Definition: routing.cpp:55
#define ModelCeilingToQuant(x)
Definition: routing.h:77
void setFloor(const int actorSize, const int x, const int y, const int z, const int val)
Definition: typedefs.h:259
pos_t getMaxY() const
Definition: mathlib.h:186
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition: files.cpp:162
static void RT_TracePassage(RoutingData *rtd, const int x, const int y, const int z, const int ax, const int ay, opening_t *opening)
Performs traces to find a passage between two points.
Definition: routing.cpp:1169
#define RT_STEPUP_PY(r, actorSize, x, y, z)
Definition: routing.h:64
pos_t getMaxX() const
Definition: mathlib.h:183
#define RT_CONN_PX_PY(r, actorSize, x, y, z)
Definition: routing.h:57
void RT_WriteCSVFiles(const Routing &routing, const char *baseFilename, const GridBox &box)
Definition: routing.cpp:1330
#define ACTOR_MAX_SIZE
Definition: defines.h:305
static int RT_TraceOnePassage(RoutingData *rtd, const place_t *from, const place_t *to, opening_t *opening)
Performs traces to find a passage between two points given an upper and lower bound.
Definition: routing.cpp:1092
#define RT_CONN_NY(r, actorSize, x, y, z)
Definition: routing.h:55
static int RT_FillPassageData(RoutingData *rtd, const int dir, const int x, const int y, const int z, const int openingSize, const int openingBase, const int stepup)
Performs traces to find a passage between two points given an upper and lower bound.
Definition: routing.cpp:541
#define RT_CONN_NX_PY(r, actorSize, x, y, z)
Definition: routing.h:59
int32_t actorSizeEnum_t
Definition: ufotypes.h:77
#define RT_CONN_PX_NY(r, actorSize, x, y, z)
Definition: routing.h:58
vec3_t endpos
Definition: tracing.h:59
#define WALL_SIZE
Definition: defines.h:128
#define RT_STEPUP_PX_NY(r, actorSize, x, y, z)
Definition: routing.h:68
struct opening_s opening_t
An 'opening' describes the connection between two adjacent spaces where an actor can exist in a cell...
Definition: aabb.h:42
const char * filename
Definition: ioapi.h:41
bool debugTrace
Definition: routing.cpp:38
void setFilled(const actorSizeEnum_t actorSize, const int x, const int y, const int lowZ, const int highZ)
Definition: typedefs.h:279
#define RT_STEPUP_NX_NY(r, actorSize, x, y, z)
Definition: routing.h:70
#define UNIT_HEIGHT
Definition: defines.h:122
const vec4_t dvecs[PATHFINDING_DIRECTIONS]
Definition: mathlib.cpp:58
static void RT_StepupSet(RoutingData *rtd, const int x, const int y, const int z, const int dir, const int val)
Definition: routing.cpp:98
int floor
Definition: routing.cpp:115
static int RT_FindOpeningCeiling(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int ceilLimit)
Performs traces to find the approximate ceiling of a passage.
Definition: routing.cpp:715
void set(const AABB &other)
Copies the values from the given aabb.
Definition: aabb.h:60
#define MAX_OSPATH
Definition: filesys.h:44
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
A 'place' is a part of a grid column where an actor can exist Unlike for a grid-cell, floor and ceiling are absolute values.
Definition: routing.cpp:113
vec3_t maxs
Definition: aabb.h:258
#define RT_STEPUP_PX_PY(r, actorSize, x, y, z)
Definition: routing.h:67
int stepup
Definition: routing.cpp:167
void setStepup(const int actorSize, const int x, const int y, const int z, const int dir, const int val)
Definition: typedefs.h:298
#define ACTOR_MAX_HEIGHT
The tallest actor's height in QUANT sized units.
Definition: defines.h:298
int invstepup
Definition: routing.cpp:168
#define CELL_HEIGHT
A cell's height in QUANT sized units.
Definition: defines.h:296
int base
Definition: routing.cpp:166
float fraction
Definition: tracing.h:58
static int RT_TraceOpening(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int ax, const int ay, const int bottom, const int top, int lo, int hi, int *foundLow, int *foundHigh)
Performs actual trace to find a passage between two points given an upper and lower bound...
Definition: routing.cpp:774
static int RT_MicroTrace(RoutingData *rtd, const place_t *from, const int ax, const int ay, const int az, const int stairwaySituation, opening_t *opening)
Performs small traces to find places when an actor can step up.
Definition: routing.cpp:900
static void RT_PlaceInit(const Routing &routing, const actorSizeEnum_t actorSize, place_t *p, const int x, const int y, const int z)
Definition: routing.cpp:126
void UFO_assert(bool condition, const char *fmt,...)
Definition: shared.cpp:627
static trace_t RT_ObstructedTrace(const RoutingData *rtd, const Line &traceLine, int hi, int lo)
Helper function to trace for walls.
Definition: routing.cpp:603
static const AABB footBox(-halfMicrostepSize,-halfMicrostepSize, 0, halfMicrostepSize, halfMicrostepSize, 0)
#define PATHFINDING_WIDTH
absolute max
Definition: defines.h:292
Routing & routing
Definition: routing.cpp:84
#define RT_CONN_PX(r, actorSize, x, y, z)
Some macros to access routing table values as described above.
Definition: routing.h:52
#define RT_STEPUP_NX(r, actorSize, x, y, z)
Definition: routing.h:63
#define PATHFINDING_MAX_STEPUP
Definition: defines.h:317
actorSizeEnum_t actorSize
Definition: routing.cpp:85
int size
Definition: routing.cpp:165
pos_t getMinY() const
Definition: mathlib.h:177
#define RT_CONN_PY(r, actorSize, x, y, z)
Definition: routing.h:54
pos_t getMinX() const
Definition: mathlib.h:174
bool RT_AllCellsBelowAreFilled(const Routing &routing, const int actorSize, const pos3_t pos)
Check if pos is on solid ground.
Definition: routing.cpp:331
#define VecToPos(v, p)
Map boundary is +/- MAX_WORLD_WIDTH - to get into the positive area we add the possible max negative ...
Definition: mathlib.h:100
signed char getFloor(const actorSizeEnum_t actorSize, const pos3_t pos) const
Definition: typedefs.h:262
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition: mathlib.h:110
int FS_Printf(qFILE *f, const char *msg,...)
Can print chunks for 1024 chars into a file.
Definition: files.cpp:1495
static transfer_t tr
#define PATHFINDING_MICROSTEP_SKIP
The number of microsteps that can be stepped over by an actor. Used to allow an actor to stepup when ...
Definition: defines.h:328
Definition: line.h:31
RoutingData(mapTiles_t *mapTiles, Routing &r, actorSizeEnum_t actorSize, const char **list)
Definition: routing.cpp:91
#define VectorInterpolation(p1, p2, frac, mid)
Definition: vector.h:80
static int RT_CalcNewZ(const RoutingData *rtd, const int ax, const int ay, const int top, const int hi)
Definition: routing.cpp:737
#define PATHFINDING_MICROSTEP_SIZE
The size (in model units) of a microstep. Must be a power of 2 and less than UNIT_SIZE.
Definition: defines.h:325
pos3_t cell
Definition: routing.cpp:114
pos_t pos3_t[3]
Definition: ufotypes.h:58
byte getCeiling(const int actorSize, const pos3_t pos) const
Definition: typedefs.h:272
grid pathfinding and routing
static int RT_FindOpeningFloorFrac(const RoutingData *rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
Performs a trace to find the floor of a passage a fraction of the way from start to end...
Definition: routing.cpp:626
static int RT_FindOpening(RoutingData *rtd, const place_t *from, const int ax, const int ay, const int bottom, const int top, int *foundLow, int *foundHigh)
Performs traces to find a passage between two points given an upper and lower bound.
Definition: routing.cpp:812
#define ModelFloorToQuant(x)
These macros are meant to correctly convert from model units to QUANT units and back.
Definition: routing.h:75
#define RT_STEPUP_PX(r, actorSize, x, y, z)
Definition: routing.h:62
static bool RT_PlaceDoesIntersectEnough(const place_t *p, const place_t *other)
Definition: routing.cpp:138
#define UNIT_SIZE
Definition: defines.h:121
#define RT_CONN_NX_NY(r, actorSize, x, y, z)
Definition: routing.h:60
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define VectorClear(a)
Definition: vector.h:55
void RT_UpdateConnectionColumn(mapTiles_t *mapTiles, Routing &routing, const int actorSize, const int x, const int y, const int dir, const char **list, const int minZ, const int maxZ)
Routing Function to update the connection between two fields.
Definition: routing.cpp:1292
struct place_s place_t
A 'place' is a part of a grid column where an actor can exist Unlike for a grid-cell, floor and ceiling are absolute values.
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
#define VectorAdd(a, b, dest)
Definition: vector.h:47
#define PATHFINDING_LEGROOMHEIGHT
Definition: defines.h:311
#define PATHFINDING_MIN_OPENING
Definition: defines.h:323
#define RT_STEPUP_NY(r, actorSize, x, y, z)
Definition: routing.h:65
void Com_DefaultExtension(char *path, size_t len, const char *extension)
Sets a default extension if there is none.
Definition: shared.cpp:297
#define QuantToModel(x)
Definition: routing.h:79
#define PATHFINDING_BIG_STEPUP
The home of the routing tables.
Definition: typedefs.h:244
QGL_EXTERN GLint i
Definition: r_gl.h:113
pos_t getMaxZ() const
Definition: mathlib.h:189
void setConn(const int actorSize, const int x, const int y, const int z, const int dir, const int val)
Definition: typedefs.h:288
static int RT_UpdateConnection(RoutingData *rtd, const int x, const int y, const int ax, const int ay, const int z, const int dir)
Routing Function to update the connection between two fields.
Definition: routing.cpp:1240
void RT_GetMapSize(mapTiles_t *mapTiles, AABB &mapBox)
Calculate the map size via model data and store grid size in map_min and map_max. This is done with e...
Definition: routing.cpp:253
static const AABB actor2x2Box(-half2x2Width,-half2x2Width, 0, half2x2Width, half2x2Width, 0)
#define half1x1Width
Definition: routing.cpp:54
int floorZ
Definition: routing.cpp:117
#define DIST_EPSILON
Definition: defines.h:377
#define RT_CONN_NX(r, actorSize, x, y, z)
Definition: routing.h:53
vec_t vec3_t[3]
Definition: ufotypes.h:39
int RT_CheckCell(mapTiles_t *mapTiles, Routing &routing, const int actorSize, const int x, const int y, const int z, const char **list)
This function looks to see if an actor of a given size can occupy a cell(s) and if so identifies the ...
Definition: routing.cpp:360
definitions common between client and server, but not game lib
void shift(const vec3_t shiftVec)
shove the whole box by the given vector
Definition: aabb.h:246
static void RT_ConnSetNoGo(RoutingData *rtd, const int x, const int y, const int z, const int dir)
Definition: routing.cpp:103
const char ** list
Definition: routing.cpp:86
vec3_t mins
Definition: aabb.h:257
#define PATHFINDING_NO_STEPUP
Definition: defines.h:319
RT_data_s contains the essential data that is passed to most of the RT_* functions.
Definition: routing.cpp:80
#define RT_STEPUP_NX_PY(r, actorSize, x, y, z)
Definition: routing.h:69
static int RT_PlaceIsShifted(const place_t *p, const place_t *other)
This function detects a special stairway situation, where one place is right in front of a stairway a...
Definition: routing.cpp:149
static const AABB actor1x1Box(-half1x1Width,-half1x1Width, 0, half1x1Width, half1x1Width, 0)
static int RT_FindOpeningCeilingFrac(const RoutingData *rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
Performs a trace to find the ceiling of a passage a fraction of the way from start to end...
Definition: routing.cpp:655
#define ACTOR_SIZE_NORMAL
Definition: defines.h:302
#define RT_NO_OPENING
Definition: routing.cpp:46
bool RT_CanActorStandHere(const Routing &routing, const int actorSize, const pos3_t pos)
Check if an actor can stand(up) in the cell given by pos.
Definition: routing.cpp:237
#define ACTOR_SIZE_INVALID
Definition: defines.h:301
#define TRACE_VISIBLE_LEVELS
Definition: tracing.h:50
#define PLAYER_STANDING_HEIGHT
Definition: q_sizes.h:12
bool usable
Definition: routing.cpp:118
#define halfMicrostepSize
Definition: routing.cpp:49
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
char baseFilename[MAX_OSPATH]
Definition: ufo2map.cpp:55
Tracing functions.
static mapTiles_t mapTiles
bool isUsable(void) const
Definition: routing.cpp:120
static int RT_FindOpeningFloor(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int floorLimit)
Performs traces to find the approximate floor of a passage.
Definition: routing.cpp:683
#define QUANT
Definition: defines.h:126