1 /**
2 * @file
3 * @brief Grid oriented movement and scanning
4 */
5
6 /*
7 Copyright (C) 1997-2001 Id Software, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "common.h"
27 #include "grid.h"
28 #include "tracing.h"
29 #include "routing.h"
30 #include "pqueue.h"
31
32 #define RT_AREA_POS(path, p, state) ((path)->area[(state)][(p)[2]][(p)[1]][(p)[0]])
33 #define RT_AREA_FROM_POS(path, p, state) ((path)->areaFrom[(state)][(p)[2]][(p)[1]][(p)[0]])
34 #define RT_SAREA(path, x, y, z, state) ((path)->areaStored[(state)][(z)][(y)][(x)])
35 #define RT_AREA_TEST_POS(path, p, state) assert((p)[2] < PATHFINDING_HEIGHT);\
36 assert((state) == 0 || (state) == 1);
37 /* assuming p is a pos3_t, we don't need to check for p[n] >= 0 here because it's unsigned.
38 * also we don't need to check against PATHFINDING_WIDTH because it's always greater than a 'byte' type. */
39
40 /** @note these are the TUs used to intentionally move in a given direction. Falling not included. */
41 static const int TUsUsed[] = {
42 TU_MOVE_STRAIGHT, /* E */
43 TU_MOVE_STRAIGHT, /* W */
44 TU_MOVE_STRAIGHT, /* N */
45 TU_MOVE_STRAIGHT, /* S */
46 TU_MOVE_DIAGONAL, /* NE */
47 TU_MOVE_DIAGONAL, /* SW */
48 TU_MOVE_DIAGONAL, /* NW */
49 TU_MOVE_DIAGONAL, /* SE */
50 TU_MOVE_CLIMB, /* UP */
51 TU_MOVE_CLIMB, /* DOWN */
52 TU_CROUCH, /* STAND */
53 TU_CROUCH, /* CROUCH */
54 0, /* ??? */
55 TU_MOVE_FALL, /* FALL */
56 0, /* ??? */
57 0, /* ??? */
58 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY UP & E */
59 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY UP & W */
60 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY UP & N */
61 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY UP & S */
62 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY UP & NE */
63 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY UP & SW */
64 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY UP & NW */
65 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY UP & SE */
66 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & E */
67 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & W */
68 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & N */
69 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & S */
70 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & NE */
71 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & SW */
72 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & NW */
73 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY LEVEL & SE */
74 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & E */
75 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & W */
76 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & N */
77 TU_MOVE_STRAIGHT * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & S */
78 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & NE */
79 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & SW */
80 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR, /* FLY DOWN & NW */
81 TU_MOVE_DIAGONAL * TU_FLYING_MOVING_FACTOR /* FLY DOWN & SE */
82 };
83 CASSERT(lengthof(TUsUsed) == PATHFINDING_DIRECTIONS);
84
85 /**
86 * @brief Checks one cell on the grid against the 'forbiddenList' (i.e. cells blocked by other entities).
87 * @param[in] exclude Exclude this position from the forbidden list check
88 * @param[in] actorSize width of the actor in cells
89 * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap)
90 * @param[in] x Field in x direction
91 * @param[in] y Field in y direction
92 * @param[in] z Field in z direction
93 * @sa G_BuildForbiddenList
94 * @sa CL_BuildForbiddenList
95 * @return true if one can't walk there (i.e. the field [and attached fields for e.g. 2x2 units] is/are blocked by entries in
96 * the forbidden list) otherwise false.
97 */
Grid_CheckForbidden(const pos3_t exclude,const actorSizeEnum_t actorSize,pathing_t * path,int x,int y,int z)98 static bool Grid_CheckForbidden (const pos3_t exclude, const actorSizeEnum_t actorSize, pathing_t* path, int x, int y, int z)
99 {
100 pos_t** p;
101 int i;
102 actorSizeEnum_t size;
103
104 for (i = 0, p = path->fblist; i < path->fblength / 2; i++, p += 2) {
105 /* Skip initial position. */
106 if (VectorCompare((*p), exclude)) {
107 continue;
108 }
109
110 /* extract the forbidden coordinates */
111 byte* forbiddenSize = *(p + 1);
112 memcpy(&size, forbiddenSize, sizeof(size));
113 const int fx = (*p)[0];
114 const int fy = (*p)[1];
115 const int fz = (*p)[2];
116
117 if (fx + size <= x || x + actorSize <= fx)
118 continue; /* x bounds do not intersect */
119 if (fy + size <= y || y + actorSize <= fy)
120 continue; /* y bounds do not intersect */
121 if (z == fz)
122 return true; /* confirmed intersection */
123 }
124 return false;
125 }
126
Grid_SetMoveData(pathing_t * path,const pos3_t toPos,const int crouch,const byte length,const int dir,const int oldZ)127 static void Grid_SetMoveData (pathing_t* path, const pos3_t toPos, const int crouch, const byte length, const int dir, const int oldZ)
128 {
129 RT_AREA_TEST_POS(path, toPos, crouch);
130 RT_AREA_POS(path, toPos, crouch) = length; /**< Store TUs for this square. */
131 RT_AREA_FROM_POS(path, toPos, crouch) = makeDV(dir, oldZ); /**< Store origination information for this square. */
132 }
133
134 /**
135 * @brief a struct holding the relevant data to check if we can move between two adjacent cells
136 */
137 class Step {
138 private:
139 /** @todo flier should return true if the actor can fly. */
140 bool flier; /**< This can be keyed into whether an actor can fly or not to allow flying */
141
142 /** @todo has_ladder_climb should return true if
143 * 1) There is a ladder in the new cell in the specified direction. */
144 bool hasLadderToClimb; /**< Indicates if there is a ladder present providing ability to climb. */
145
146 /** @todo has_ladder_support should return true if
147 * 1) There is a ladder in the new cell in the specified direction or
148 * 2) There is a ladder in any direction in the cell below the new cell and no ladder in the new cell itself. */
149 bool hasLadderSupport; /**< Indicates if there is a ladder present providing support. */
150
151 int actorHeight; /**< The actor's height in QUANT units. */
152 int actorCrouchedHeight;
153 const Routing &routing;
154 public:
155 const int dir;
156 pos3_t fromPos;
157 pos3_t toPos; /* The position we are moving to with this step. */
158 actorSizeEnum_t actorSize;
159 const byte crouchingState;
160 byte TUsAfter;
161
162 Step (const Routing &r, const pos3_t fromPos, const actorSizeEnum_t actorSize, const byte crouchingState, const int dir);
163 bool init ();
164 bool calcNewPos ();
165 void calcNewTUs (const pathing_t* path);
166 bool checkWalkingDirections (const pathing_t* path);
167 bool checkFlyingDirections () const;
168 bool checkVerticalDirections () const;
169 bool isPossible (const pathing_t* path);
170 };
171
172 /**
173 * @brief Constructor
174 * @param[in] r Reference to client or server side routing table (clMap, svMap)
175 * @param[in] _fromPos Position where we start this step
176 * @param[in] _actorSize Give the field size of the actor (e.g. for 2x2 units) to check linked fields as well.
177 * @param[in] _crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no.
178 * @param[in] _dir Direction vector index (see DIRECTIONS and dvecs)
179 */
Step(const Routing & r,const pos3_t _fromPos,const actorSizeEnum_t _actorSize,const byte _crouchingState,const int _dir)180 Step::Step (const Routing &r, const pos3_t _fromPos, const actorSizeEnum_t _actorSize, const byte _crouchingState, const int _dir) :
181 flier(false), hasLadderToClimb(false), hasLadderSupport(false), actorHeight(0), actorCrouchedHeight(0), routing(
182 r), dir(_dir), actorSize(_actorSize), crouchingState(_crouchingState), TUsAfter(0)
183 {
184 VectorCopy(_fromPos, fromPos);
185 }
186
187 /**
188 * @brief Initialize the other Step data
189 * @return false if dir is irrelevant or something went wrong
190 */
init()191 bool Step::init ()
192 {
193 /** @note This is the actor's height in QUANT units. */
194 /** @todo actor_height is currently the fixed height of a 1x1 actor. This needs to be adjusted
195 * to the actor's actual height. */
196 actorHeight = ModelCeilingToQuant((float)(crouchingState ? PLAYER_CROUCHING_HEIGHT : PLAYER_STANDING_HEIGHT)); /**< The actor's height */
197 actorCrouchedHeight = ModelCeilingToQuant((float)(PLAYER_CROUCHING_HEIGHT));
198
199 /* Ensure that dir is in bounds. */
200 assert(dir >= 0 && dir < PATHFINDING_DIRECTIONS);
201
202 /* IMPORTANT: only fliers can use directions higher than NON_FLYING_DIRECTIONS. */
203 if (!flier && dir >= FLYING_DIRECTIONS) {
204 return false;
205 }
206
207 /* We cannot fly and crouch at the same time. This will also cause an actor to stand to fly. */
208 if (crouchingState && dir >= FLYING_DIRECTIONS) {
209 return false;
210 }
211
212 return true;
213 }
214
215
216 /**
217 * @brief Calculate the cell the we end up in if moving in the give dir
218 * @return false if we can't move there
219 */
calcNewPos(void)220 bool Step::calcNewPos (void)
221 {
222 toPos[0] = fromPos[0] + dvecs[dir][0]; /**< "new" x value = starting x value + difference from chosen direction */
223 toPos[1] = fromPos[1] + dvecs[dir][1]; /**< "new" y value = starting y value + difference from chosen direction */
224 toPos[2] = fromPos[2] + dvecs[dir][2]; /**< "new" z value = starting z value + difference from chosen direction */
225
226 /* Connection checks. If we cannot move in the desired direction, then bail. */
227 /* Range check of new values (all sizes) */
228 /* "comparison is always false due to limited range of data type" */
229 /* Only activate this check if PATHFINDING_WIDTH or pos3_t changes */
230 /* if (toPos[0] < 0 || toPos[0] > PATHFINDING_WIDTH - actorSize
231 || toPos[1] < 0 || toPos[1] > PATHFINDING_WIDTH - actorSize
232 || toPos[2] < 0 {
233 return false;
234 } */
235 if (toPos[2] > PATHFINDING_HEIGHT) {
236 return false;
237 }
238 return true;
239 }
240
241 /**
242 * @brief Calculate the TUs after we in the given dir
243 * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap)
244 */
calcNewTUs(const pathing_t * path)245 void Step::calcNewTUs (const pathing_t* path)
246 {
247 const byte TUsSoFar = RT_AREA_POS(path, fromPos, crouchingState);
248 /* Find the number of TUs used (normally) to move in this direction. */
249 const byte TUsForMove = Grid_GetTUsForDirection(dir, crouchingState);
250
251 /* Now add the TUs needed to get to the originating cell. */
252 TUsAfter = TUsSoFar + TUsForMove;
253 }
254
255 /**
256 * @brief Checks if we can walk in the given direction
257 * First test for opening height availability. Then test for stepup compatibility. Last test for fall.
258 * @note Fliers use this code only when they are walking.
259 * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap)
260 * @return false if we can't fly there
261 */
checkWalkingDirections(const pathing_t * path)262 bool Step::checkWalkingDirections (const pathing_t* path)
263 {
264 int nx, ny, nz;
265 int passageHeight;
266 /** @todo falling_height should be replaced with an arbitrary max falling height based on the actor. */
267 const int fallingHeight = PATHFINDING_MAX_FALL;/**<This is the maximum height that an actor can fall. */
268 const int stepupHeight = routing.getStepupHeight(actorSize, fromPos[0], fromPos[1], fromPos[2], dir); /**< The actual stepup height without the level flags */
269 int heightChange;
270 /** @todo actor_stepup_height should be replaced with an arbitrary max stepup height based on the actor. */
271 int actorStepupHeight = PATHFINDING_MAX_STEPUP;
272
273 /* This is the standard passage height for all units trying to move horizontally. */
274 passageHeight = routing.getConn(actorSize, fromPos, dir);
275 if (passageHeight < actorHeight) {
276 #if 0
277 /** I know this code could be streamlined, but until I understand it myself, plz leave it like it is !*/
278 int dvFlagsNew = 0;
279 if (!crouchingState /* not in std crouch mode */
280 && passageHeight >= actorCrouchedHeight) { /* and passage is tall enough for crouching ? */
281 /* we should try autocrouching */
282 int dvFlagsOld = getDVflags(RT_AREA_POS(path, fromPos, crouchingState));
283 int toHeight = routing.getCeiling(actorSize, toPos) - routing.getFloor(actorSize, toPos);
284 int tuCr = Grid_GetTUsForDirection(dir, 1); /* 1 means crouched */
285 int newTUs = 0;
286
287 if (toHeight >= actorHeight) { /* can we stand in the new cell ? */
288 if ((dvFlagsOld & DV_FLAG_AUTOCROUCH) /* already in auto-crouch mode ? */
289 || (dvFlagsOld & DV_FLAG_AUTOCROUCHED)) {
290 dvFlagsNew |= DV_FLAG_AUTOCROUCHED; /* keep that ! */
291 newTUs = tuCr + TU_CROUCH; /* TUs for crouching plus getting up */
292 }
293 else {
294 dvFlagsNew |= DV_FLAG_AUTODIVE;
295 newTUs = tuCr + 2 * TU_CROUCH; /* TUs for crouching plus getting down and up */
296 }
297 }
298 else { /* we can't stand there */
299 if (dvFlagsOld & DV_FLAG_AUTOCROUCHED) {
300 dvFlagsNew |= DV_FLAG_AUTOCROUCHED; /* keep that ! */
301 newTUs = tuCr; /* TUs just for crouching */
302 }
303 else {
304 dvFlagsNew |= DV_FLAG_AUTOCROUCH; /* get down ! */
305 newTUs = tuCr + TU_CROUCH; /* TUs for crouching plus getting down */
306 }
307 }
308 }
309 else
310 #endif
311 return false; /* Passage is not tall enough. */
312 }
313
314 /* If we are moving horizontally, use the stepup requirement of the floors.
315 * The new z coordinate may need to be adjusted from stepup.
316 * Also, actor_stepup_height must be at least the cell's positive stepup value to move that direction. */
317 /* If the actor cannot reach stepup, then we can't go this way. */
318 if (actorStepupHeight < stepupHeight) {
319 return false; /* Actor cannot stepup high enough. */
320 }
321
322 nx = toPos[0];
323 ny = toPos[1];
324 nz = toPos[2];
325
326 if (routing.isStepUpLevel(actorSize, fromPos, dir) && toPos[2] < PATHFINDING_HEIGHT - 1) {
327 toPos[2]++;
328 /**
329 * @note If you need to know about how pathfinding works, you need to understand the
330 * following brief. It may cause nausea, but is an important concept.
331 *
332 * @brief OK, now some crazy tests:
333 * Because of the grid based nature of this game, each cell can have at most only ONE
334 * floor that can be stood upon. If an actor can walk down a slope that is in the
335 * same level, and actor should be able to walk on (and not fall into) the slope that
336 * decends a game level. BUT it is possible for an actor to be able to crawl under a
337 * floor that can be stood on, with this opening being in the same cell as the floor.
338 * SO to prevent any conflicts, we will move down a floor under the following conditions:
339 * - The STEPDOWN flag is set
340 * - The floor in the immediately adjacent cell is lower than the current floor, but not
341 * more than CELL_HEIGHT units (in QUANT units) below the current floor.
342 * - The actor's stepup value is at least the inverse stepup value. This is the stepup
343 * FROM the cell we are moving towards back into the cell we are starting in. This
344 * ensures that the actor can actually WALK BACK.
345 * If the actor does not have a high enough stepup but meets all the other requirements to
346 * descend the level, the actor will move into a fall state, provided that there is no
347 * floor in the adjacent cell.
348 *
349 * This will prevent actors from walking under a floor in the same cell in order to fall
350 * to the floor beneath. They will need to be able to step down into the cell or will
351 * not be able to use the opening.
352 */
353 } else if (routing.isStepDownLevel(actorSize, fromPos, dir) && toPos[2] > 0
354 && actorStepupHeight >= routing.getStepupHeight(actorSize, nx, ny, nz - 1, dir ^ 1)) {
355 toPos[2]--; /* Stepping down into lower cell. */
356 }
357
358 heightChange = routing.getFloor(actorSize, toPos) - routing.getFloor(actorSize, fromPos) + (toPos[2] - fromPos[2]) * CELL_HEIGHT;
359
360 /* If the actor tries to fall more than falling_height, then prohibit the move. */
361 if (heightChange < -fallingHeight && !hasLadderSupport) {
362 return false; /* Too far a drop without a ladder. */
363 }
364
365 /* If we are walking normally, we can fall if we move into a cell that does not
366 * have its STEPDOWN flag set and has a negative floor:
367 * Set heightChange to 0.
368 * The actor enters the cell.
369 * The actor will be forced to fall (dir 13) from the destination cell to the cell below. */
370 if (routing.getFloor(actorSize, toPos) < 0) {
371 /* We cannot fall if STEPDOWN is defined. */
372 if (routing.isStepDownLevel(actorSize, fromPos, dir)) {
373 return false; /* There is stepdown from here. */
374 }
375 heightChange = 0;
376 toPos[2]--;
377 }
378 return true;
379 }
380
381 /**
382 * @brief Checks if we can move in the given flying direction
383 * @return false if we can't fly there
384 */
checkFlyingDirections() const385 bool Step::checkFlyingDirections () const
386 {
387 const int coreDir = dir % CORE_DIRECTIONS; /**< The compass direction of this flying move */
388 int neededHeight;
389 int passageHeight;
390
391 if (toPos[2] > fromPos[2]) {
392 /* If the actor is moving up, check the passage at the current cell.
393 * The minimum height is the actor's height plus the distance from the current floor to the top of the cell. */
394 neededHeight = actorHeight + CELL_HEIGHT - std::max((const signed char)0, routing.getFloor(actorSize, fromPos));
395 passageHeight = routing.getConn(actorSize, fromPos, coreDir);
396 } else if (toPos[2] < fromPos[2]) {
397 /* If the actor is moving down, check from the destination cell back. *
398 * The minimum height is the actor's height plus the distance from the destination floor to the top of the cell. */
399 neededHeight = actorHeight + CELL_HEIGHT - std::max((const signed char)0, routing.getFloor(actorSize, toPos));
400 passageHeight = routing.getConn(actorSize, toPos, coreDir ^ 1);
401 } else {
402 neededHeight = actorHeight;
403 passageHeight = routing.getConn(actorSize, fromPos, coreDir);
404 }
405 if (passageHeight < neededHeight) {
406 return false;
407 }
408 return true;
409 }
410
411 /**
412 * @brief Checks if we can move in the given vertical direction
413 * @return false if we can't move there
414 */
checkVerticalDirections() const415 bool Step::checkVerticalDirections () const
416 {
417 if (dir == DIRECTION_FALL) {
418 if (flier) {
419 /* Fliers cannot fall intentionally. */
420 return false;
421 } else if (routing.getFloor(actorSize, fromPos) >= 0) {
422 /* We cannot fall if there is a floor in this cell. */
423 return false;
424 } else if (hasLadderSupport) {
425 /* The actor can't fall if there is ladder support. */
426 return false;
427 }
428 } else if (dir == DIRECTION_CLIMB_UP) {
429 if (flier && QuantToModel(routing.getCeiling(actorSize, fromPos)) < UNIT_HEIGHT * 2 - PLAYER_HEIGHT) { /* Not enough headroom to fly up. */
430 return false;
431 }
432 /* If the actor is not a flyer and tries to move up, there must be a ladder. */
433 if (dir == DIRECTION_CLIMB_UP && !hasLadderToClimb) {
434 return false;
435 }
436 } else if (dir == DIRECTION_CLIMB_DOWN) {
437 if (flier) {
438 if (routing.getFloor(actorSize, fromPos) >= 0 ) { /* Can't fly down through a floor. */
439 return false;
440 }
441 } else {
442 /* If the actor is not a flyer and tries to move down, there must be a ladder. */
443 if (!hasLadderToClimb) {
444 return false;
445 }
446 }
447 }
448 return true;
449 }
450
451 /**
452 * @param[in] path Pointer to client or server side pathing table (clMap, svMap)
453 */
isPossible(const pathing_t * path)454 bool Step::isPossible (const pathing_t* path)
455 {
456 /* calculate the position we would normally end up if moving in the given dir. */
457 if (!calcNewPos()) {
458 return false;
459 }
460 calcNewTUs(path);
461
462 /* If there is no passageway (or rather lack of a wall) to the desired cell, then return. */
463 /* If the flier is moving up or down diagonally, then passage height will also adjust */
464 if (dir >= FLYING_DIRECTIONS) {
465 if (!checkFlyingDirections()) {
466 return false;
467 }
468 } else if (dir < CORE_DIRECTIONS) {
469 /** note that this function may modify toPos ! */
470 if (!checkWalkingDirections(path)) {
471 return false;
472 }
473 } else {
474 /* else there is no movement that uses passages. */
475 /* If we are falling, the height difference is the floor value. */
476 if (!checkVerticalDirections()) {
477 return false;
478 }
479 }
480
481 /* OK, at this point we are certain of a few things:
482 * There is not a wall obstructing access to the destination cell.
483 * If the actor is not a flier, the actor will not rise more than actor_stepup_height or fall more than
484 * falling_height, unless climbing.
485 *
486 * If the actor is a flier, as long as there is a passage, it can be moved through.
487 * There are no floor difference restrictions for fliers, only obstructions. */
488
489 return true;
490 }
491
492
493 /**
494 * @brief Recalculate the pathing table for the given actor(-position)
495 *
496 * We calculate the table for ALL possible movement states (atm stand and crouch)
497 * to be able to propose smart things like autostand.
498 * @param[in] routing Reference to client or server side routing table (clMap, svMap)
499 * @param[in] actorSize The size of thing to calc the move for (e.g. size=2 means 2x2).
500 * The plan is to have the 'origin' in 2x2 units in the bottom-left (towards the lower coordinates) corner of the 2x2 square.
501 * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap)
502 * @param[in] from The position to start the calculation from.
503 * @param[in] maxTUs The maximum TUs away from 'from' to calculate move-information for
504 * @param[in] fb_list Forbidden list (entities are standing at those points)
505 * @param[in] fb_length Length of forbidden list
506 * @sa G_MoveCalc
507 * @sa CL_ConditionalMoveCalc
508 */
Grid_CalcPathing(const Routing & routing,const actorSizeEnum_t actorSize,pathing_t * path,const pos3_t from,int maxTUs,byte ** fb_list,int fb_length)509 void Grid_CalcPathing (const Routing &routing, const actorSizeEnum_t actorSize, pathing_t* path, const pos3_t from, int maxTUs, byte** fb_list, int fb_length)
510 {
511 priorityQueue_t pqueue;
512 pos4_t epos; /**< Extended position; includes crouching state */
513 pos3_t pos;
514 int amst; /* acronym for actor movement state */
515 /* this is the position of the current actor- so the actor can stand in the cell it is in when pathfinding */
516 pos3_t excludeFromForbiddenList;
517
518 /* Confirm bounds */
519 assert((from[2]) < PATHFINDING_HEIGHT);
520
521 /* reset move data */
522 OBJSET(path->area, ROUTING_NOT_REACHABLE);
523 OBJSET(path->areaFrom, ROUTING_NOT_REACHABLE);
524 path->fblist = fb_list;
525 path->fblength = fb_length;
526
527 maxTUs = std::min(maxTUs, MAX_ROUTE_TUS);
528
529 /* Prepare exclusion of starting-location (i.e. this should be ent-pos or le-pos) in Grid_CheckForbidden */
530 VectorCopy(from, excludeFromForbiddenList);
531
532 for (amst = 0; amst < ACTOR_MAX_STATES; amst++) {
533 /* set starting position to 0 TUs.*/
534 RT_AREA_POS(path, from, amst) = 0;
535
536 PQueueInitialise(&pqueue, 1024);
537 Vector4Set(epos, from[0], from[1], from[2], amst);
538 PQueuePush(&pqueue, epos, 0);
539
540 int count = 0;
541 while (!PQueueIsEmpty(&pqueue)) {
542 int dir;
543 PQueuePop(&pqueue, epos);
544 VectorCopy(epos, pos);
545 count++;
546
547 /* if reaching that square already took too many TUs,
548 * don't bother to reach new squares *from* there. */
549 const byte usedTUs = RT_AREA_POS(path, pos, amst);
550 if (usedTUs >= maxTUs)
551 continue;
552
553 for (dir = 0; dir < PATHFINDING_DIRECTIONS; ++dir) {
554 Step step(routing, pos, actorSize, amst, dir);
555 /* Directions 12, 14, and 15 are currently undefined. */
556 if (dir == 12 || dir == 14 || dir == 15)
557 continue;
558 /* If this is a crouching or crouching move, forget it. */
559 if (dir == DIRECTION_STAND_UP || dir == DIRECTION_CROUCH)
560 continue;
561
562 if (!step.init())
563 continue; /* either dir is irrelevant or something worse happened */
564
565 if (step.isPossible(path)) {
566 /* Is this a better move into this cell? */
567 RT_AREA_TEST_POS(path, step.toPos, step.crouchingState);
568 if (RT_AREA_POS(path, step.toPos, step.crouchingState) <= step.TUsAfter) {
569 continue; /* This move is not optimum. */
570 }
571
572 /* Test for forbidden (by other entities) areas. */
573 if (Grid_CheckForbidden(excludeFromForbiddenList, step.actorSize, path, step.toPos[0],
574 step.toPos[1], step.toPos[2])) {
575 continue; /* That spot is occupied. */
576 }
577
578 /* Store move in pathing table. */
579 Grid_SetMoveData(path, step.toPos, step.crouchingState, step.TUsAfter, step.dir, step.fromPos[2]);
580
581 pos4_t dummy;
582 Vector4Set(dummy, step.toPos[0], step.toPos[1], step.toPos[2], step.crouchingState);
583 PQueuePush(&pqueue, dummy, step.TUsAfter);
584 }
585 }
586 }
587 /* Com_Printf("Loop: %i", count); */
588 PQueueFree(&pqueue);
589 }
590 }
591
592 /**
593 * @brief Tries to find a path from the given actor(-position) to a given target position
594 *
595 * Unlike Grid_CalcPathing, this function does not neccessarily calculate the TU values for
596 * all positions reachable from 'from'. Instead it tries to find the shortest/fastest path to
597 * the target position. There is no limit to maxTUs.
598 *
599 * @param[in] routing Reference to client or server side routing table (clMap, svMap)
600 * @param[in] actorSize The size of thing to calc the move for (e.g. size=2 means 2x2).
601 * The plan is to have the 'origin' in 2x2 units in the bottom-left (towards the lower coordinates) corner of the 2x2 square.
602 * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap)
603 * @param[in] from The position to start the calculation from.
604 * @param[in] targetPos The position where we want to end up.
605 * @param[in] maxTUs The maximum TUs away from 'from' to calculate move-information for
606 * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no.
607 * @param[in] fb_list Forbidden list (entities are standing at those points)
608 * @param[in] fb_length Length of forbidden list
609 * @sa G_MoveCalc
610 * @sa CL_ConditionalMoveCalc
611 */
Grid_FindPath(const Routing & routing,const actorSizeEnum_t actorSize,pathing_t * path,const pos3_t from,const pos3_t targetPos,byte crouchingState,int maxTUs,byte ** fb_list,int fb_length)612 bool Grid_FindPath (const Routing &routing, const actorSizeEnum_t actorSize, pathing_t* path, const pos3_t from, const pos3_t targetPos, byte crouchingState, int maxTUs, byte** fb_list, int fb_length)
613 {
614 bool found = false;
615 int count;
616 priorityQueue_t pqueue;
617 pos4_t epos; /**< Extended position; includes crouching state */
618 pos3_t pos;
619 /* this is the position of the current actor- so the actor can stand in the cell it is in when pathfinding */
620 pos3_t excludeFromForbiddenList;
621
622 /* Confirm bounds */
623 assert((from[2]) < PATHFINDING_HEIGHT);
624 assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */
625
626 /* reset move data */
627 OBJSET(path->area, ROUTING_NOT_REACHABLE);
628 OBJSET(path->areaFrom, ROUTING_NOT_REACHABLE);
629 path->fblist = fb_list;
630 path->fblength = fb_length;
631
632 /* Prepare exclusion of starting-location (i.e. this should be ent-pos or le-pos) in Grid_CheckForbidden */
633 VectorCopy(from, excludeFromForbiddenList);
634 /* set starting position to 0 TUs.*/
635 RT_AREA_POS(path, from, crouchingState) = 0;
636
637 PQueueInitialise(&pqueue, 1024);
638 Vector4Set(epos, from[0], from[1], from[2], crouchingState);
639 PQueuePush(&pqueue, epos, 0);
640
641 count = 0;
642 while (!PQueueIsEmpty(&pqueue)) {
643 PQueuePop(&pqueue, epos);
644 VectorCopy(epos, pos);
645 count++;
646
647 /* if reaching that square already took too many TUs,
648 * don't bother to reach new squares *from* there. */
649 const byte usedTUs = RT_AREA_POS(path, pos, crouchingState);
650 if (usedTUs >= maxTUs)
651 continue;
652
653 for (int dir = 0; dir < PATHFINDING_DIRECTIONS; dir++) {
654 Step step(routing, pos, actorSize, crouchingState, dir);
655 /* Directions 12, 14, and 15 are currently undefined. */
656 if (dir == 12 || dir == 14 || dir == 15)
657 continue;
658 /* If this is a crouching or crouching move, forget it. */
659 if (dir == DIRECTION_STAND_UP || dir == DIRECTION_CROUCH)
660 continue;
661
662 if (!step.init())
663 continue; /* either dir is irrelevant or something worse happened */
664
665 if (!step.isPossible(path))
666 continue;
667
668 /* Is this a better move into this cell? */
669 RT_AREA_TEST_POS(path, step.toPos, step.crouchingState);
670 if (RT_AREA_POS(path, step.toPos, step.crouchingState) <= step.TUsAfter) {
671 continue; /* This move is not optimum. */
672 }
673
674 /* Test for forbidden (by other entities) areas. */
675 /* Do NOT check the forbiddenList. We might find a multi-turn path. */
676 #if 0
677 if (Grid_CheckForbidden(excludeFromForbiddenList, step.actorSize, path, step.toPos[0], step.toPos[1], step.toPos[2])) {
678 continue; /* That spot is occupied. */
679 }
680 #endif
681
682 /* Store move in pathing table. */
683 Grid_SetMoveData(path, step.toPos, step.crouchingState, step.TUsAfter, step.dir, step.fromPos[2]);
684
685 pos4_t dummy;
686 const int dist = step.TUsAfter + (int) (2 * VectorDist(step.toPos, targetPos));
687 Vector4Set(dummy, step.toPos[0], step.toPos[1], step.toPos[2], step.crouchingState);
688 PQueuePush(&pqueue, dummy, dist);
689
690 if (VectorEqual(step.toPos, targetPos)) {
691 found = true;
692 break;
693 }
694 }
695 if (found)
696 break;
697 }
698 /* Com_Printf("Loop: %i", count); */
699 PQueueFree(&pqueue);
700 return found;
701 }
702
703 /**
704 * @brief Caches the calculated move
705 * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap)
706 * @sa AI_ActorThink
707 */
Grid_MoveStore(pathing_t * path)708 void Grid_MoveStore (pathing_t* path)
709 {
710 memcpy(path->areaStored, path->area, sizeof(path->areaStored));
711 }
712
713
714 /**
715 * @brief Return the needed TUs to walk to a given position
716 * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap)
717 * @param[in] to Position to walk to
718 * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no.
719 * @param[in] stored Use the stored mask (the cached move) of the routing data
720 * @return ROUTING_NOT_REACHABLE if the move isn't possible
721 * @return length of move otherwise (TUs)
722 */
Grid_MoveLength(const pathing_t * path,const pos3_t to,byte crouchingState,bool stored)723 pos_t Grid_MoveLength (const pathing_t* path, const pos3_t to, byte crouchingState, bool stored)
724 {
725 /* Confirm bounds */
726 assert(to[2] < PATHFINDING_HEIGHT);
727 assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */
728
729 if (!stored)
730 return RT_AREA_POS(path, to, crouchingState);
731 else
732 return RT_SAREA(path, to[0], to[1], to[2], crouchingState);
733 }
734
735
736 /**
737 * @brief Get the direction to use to move to a position (used to reconstruct the path)
738 * @param[in] path Pointer to client or server side pathing table (le->PathMap, svPathMap)
739 * @param[in] toPos The desired location
740 * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no.
741 * @return a direction vector (see dvecs and DIRECTIONS)
742 * @sa Grid_MoveCheck
743 */
Grid_MoveNext(const pathing_t * path,const pos3_t toPos,byte crouchingState)744 int Grid_MoveNext (const pathing_t* path, const pos3_t toPos, byte crouchingState)
745 {
746 const pos_t l = RT_AREA_POS(path, toPos, crouchingState); /**< Get TUs for this square */
747
748 /* Check to see if the TUs needed to move here are greater than 0 and less then ROUTING_NOT_REACHABLE */
749 if (!l || l == ROUTING_NOT_REACHABLE) {
750 /* ROUTING_UNREACHABLE means, not possible/reachable */
751 return ROUTING_UNREACHABLE;
752 }
753
754 /* Return the information indicating how the actor got to this cell */
755 return RT_AREA_FROM_POS(path, toPos, crouchingState);
756 }
757
758
759 /**
760 * @brief Returns the height of the floor in a cell.
761 * @param[in] routing Reference to client or server side routing table (clMap, svMap)
762 * @param[in] actorSize width of the actor in cells
763 * @param[in] pos Position in the map to check the height
764 * @return The actual model height of the cell's ceiling.
765 */
Grid_Ceiling(const Routing & routing,const actorSizeEnum_t actorSize,const pos3_t pos)766 unsigned int Grid_Ceiling (const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos)
767 {
768 assert(pos[2] < PATHFINDING_HEIGHT);
769 return QuantToModel(routing.getCeiling(actorSize, pos[0], pos[1], pos[2] & 7));
770 }
771
772 /**
773 * @brief Returns the height of the floor in a cell.
774 * @param[in] routing Reference to client or server side routing table (clMap, svMap)
775 * @param[in] actorSize width of the actor in cells
776 * @param[in] pos Position in the map to check the height
777 * @return The actual model height of the cell's floor.
778 */
Grid_Floor(const Routing & routing,const actorSizeEnum_t actorSize,const pos3_t pos)779 int Grid_Floor (const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos)
780 {
781 assert(pos[2] < PATHFINDING_HEIGHT);
782 return QuantToModel(routing.getFloor(actorSize, pos[0], pos[1], pos[2] & (PATHFINDING_HEIGHT - 1)));
783 }
784
785 /**
786 * @brief Returns the amounts of TUs that are needed to perform one step into the given direction.
787 * @param[in] dir the direction in which we are moving
788 * @param[in] crouched The crouching state of the actor
789 * @return The TUs needed to move there.
790 */
Grid_GetTUsForDirection(const int dir,const int crouched)791 int Grid_GetTUsForDirection (const int dir, const int crouched)
792 {
793 assert(dir >= 0 && dir < PATHFINDING_DIRECTIONS);
794 if (crouched && dir < CORE_DIRECTIONS)
795 return TUsUsed[dir] * TU_CROUCH_MOVING_FACTOR;
796 else
797 return TUsUsed[dir];
798 }
799
800 /**
801 * @brief Calculated the new height level when something falls down from a certain position.
802 * @param[in] routing Reference to client or server side routing table (clMap, svMap)
803 * @param[in] pos Position in the map to start the fall (starting height is the z-value in this position)
804 * @param[in] actorSize Give the field size of the actor (e.g. for 2x2 units) to check linked fields as well.
805 * @return New z (height) value.
806 * @return 0xFF if an error occurred.
807 */
Grid_Fall(const Routing & routing,const actorSizeEnum_t actorSize,const pos3_t pos)808 pos_t Grid_Fall (const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos)
809 {
810 int z = pos[2], base, diff;
811 bool flier = false; /** @todo if an actor can fly, then set this to true. */
812
813 /* Is z off the map? */
814 if (z >= PATHFINDING_HEIGHT) {
815 return 0xFF;
816 }
817
818 /* If we can fly, then obviously we won't fall. */
819 if (flier)
820 return z;
821
822 /* Easy math- get the floor, integer divide by CELL_HEIGHT, add to z.
823 * If z < 0, we are going down.
824 * If z >= CELL_HEIGHT, we are going up.
825 * If 0 <= z <= CELL_HEIGHT, then z / 16 = 0, no change. */
826 base = routing.getFloor(actorSize, pos[0], pos[1], z);
827 /* Hack to deal with negative numbers- otherwise rounds toward 0 instead of down. */
828 diff = base < 0 ? (base - (CELL_HEIGHT - 1)) / CELL_HEIGHT : base / CELL_HEIGHT;
829 z += diff;
830 /* The tracing code will set locations without a floor to -1. Compensate for that. */
831 if (z < 0)
832 z = 0;
833 else if (z >= PATHFINDING_HEIGHT)
834 z = PATHFINDING_HEIGHT - 1;
835 return z;
836 }
837
838 /**
839 * @brief Checks if a crouched actor could save TUs by standing up, walking and crouching again.
840 * @param[in] path Pointer to client or server side pathing table
841 * @param[in] toPos Desired position
842 */
Grid_ShouldUseAutostand(const pathing_t * path,const pos3_t toPos)843 bool Grid_ShouldUseAutostand (const pathing_t* path, const pos3_t toPos)
844 {
845 const int tusCrouched = RT_AREA_POS(path, toPos, 1);
846 const int tusUpright = RT_AREA_POS(path, toPos, 0);
847 return tusUpright + 2 * TU_CROUCH < tusCrouched;
848 }
849
850 /**
851 * @brief Converts a grid position to world coordinates
852 * @param[in] routing The routing map
853 * @param[in] actorSize width of the actor in cells
854 * @param[in] pos The grid position
855 * @param[out] vec The world vector
856 */
Grid_PosToVec(const Routing & routing,const actorSizeEnum_t actorSize,const pos3_t pos,vec3_t vec)857 void Grid_PosToVec (const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos, vec3_t vec)
858 {
859 SizedPosToVec(pos, actorSize, vec);
860 #ifdef PARANOID
861 if (pos[2] >= PATHFINDING_HEIGHT)
862 Com_Printf("Grid_PosToVec: Warning - z level bigger than 7 (%i - source: %.02f)\n", pos[2], vec[2]);
863 #endif
864 /* Clamp the floor value between 0 and UNIT_HEIGHT */
865 const int gridFloor = Grid_Floor(routing, actorSize, pos);
866 vec[2] += std::max(0, std::min(UNIT_HEIGHT, gridFloor));
867 }
868
869
870 /**
871 * @brief This function recalculates the routing in and around the box bounded by min and max.
872 * @sa CMod_LoadRouting
873 * @sa Grid_RecalcRouting
874 * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
875 * @param[in] routing The routing map (either server or client map)
876 * @param[in] box The box to recalc routing for
877 * @param[in] list The local models list (a local model has a name starting with * followed by the model number)
878 */
Grid_RecalcBoxRouting(mapTiles_t * mapTiles,Routing & routing,const GridBox & box,const char ** list)879 void Grid_RecalcBoxRouting (mapTiles_t* mapTiles, Routing &routing, const GridBox &box, const char** list)
880 {
881 int x, y, z, actorSize, dir;
882
883 /* check unit heights */
884 for (actorSize = 1; actorSize <= ACTOR_MAX_SIZE; actorSize++) {
885 GridBox rBox(box); /* the box we will actually reroute */
886 /* Offset the initial X and Y to compensate for larger actors when needed. */
887 rBox.expandXY(actorSize - 1);
888 /* also start one level above the box to measure high floors correctly */
889 rBox.addOneZ();
890 for (y = rBox.mins[1]; y <= rBox.maxs[1]; y++) {
891 for (x = rBox.mins[0]; x <= rBox.maxs[0]; x++) {
892 /** @note RT_CheckCell goes from top (7) to bottom (0) */
893 for (z = rBox.maxs[2]; z >= 0; z--) {
894 const int newZ = RT_CheckCell(mapTiles, routing, actorSize, x, y, z, list);
895 assert(newZ <= z);
896 z = newZ;
897 }
898 }
899 }
900 }
901
902 /* check connections */
903 for (actorSize = 1; actorSize <= ACTOR_MAX_SIZE; actorSize++) {
904 GridBox rBox(box); /* the box we will actually reroute */
905 rBox.expandXY(actorSize); /* for connections, expand by the full size of the actor */
906 for (y = rBox.mins[1]; y <= rBox.maxs[1]; y++) {
907 for (x = rBox.mins[0]; x <= rBox.maxs[0]; x++) {
908 for (dir = 0; dir < CORE_DIRECTIONS; dir++) {
909 /** @note The new version of RT_UpdateConnectionColumn can work bidirectional, so we can
910 * trace every other dir, unless we are on the edge. */
911 #if RT_IS_BIDIRECTIONAL == 1
912 if ((dir & 1) && x != minX && x != maxX && y != minY && y != maxY)
913 continue;
914 #endif
915 /* for places outside the model box, skip dirs that can not be affected by the model */
916 if (x > box.maxs[0] && dir != 1 && dir != 5 && dir != 6)
917 continue;
918 if (y > box.maxs[1] && dir != 3 && dir != 5 && dir != 7)
919 continue;
920 if (actorSize == ACTOR_SIZE_NORMAL) {
921 if (x < box.mins[0] && dir != 0 && dir != 4 && dir != 7)
922 continue;
923 if (y < box.mins[1] && dir != 2 && dir != 4 && dir != 6)
924 continue;
925 } else {
926 /* the position of 2x2 actors is their lower left cell */
927 if (x < box.mins[0] - 1 && dir != 0 && dir != 4 && dir != 7)
928 continue;
929 if (y < box.mins[1] - 1 && dir != 2 && dir != 4 && dir != 6)
930 continue;
931 }
932 RT_UpdateConnectionColumn(mapTiles, routing, actorSize, x, y, dir, list);
933 }
934 }
935 }
936 }
937 }
938
939
940 /**
941 * @brief This function recalculates the routing surrounding the entity name.
942 * @sa CM_InlineModel
943 * @sa CM_CheckUnit
944 * @sa CM_UpdateConnection
945 * @sa CMod_LoadSubmodels
946 * @sa Grid_RecalcBoxRouting
947 * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
948 * @param[in] routing The routing map (either server or client map)
949 * @param[in] name Name of the inline model to compute the mins/maxs for
950 * @param[in] box The box around the inline model (alternative to name)
951 * @param[in] list The local models list (a local model has a name starting with * followed by the model number)
952 */
Grid_RecalcRouting(mapTiles_t * mapTiles,Routing & routing,const char * name,const GridBox & box,const char ** list)953 void Grid_RecalcRouting (mapTiles_t* mapTiles, Routing &routing, const char* name, const GridBox &box, const char** list)
954 {
955 if (box.isZero()) {
956 pos3_t min, max;
957 vec3_t absmin, absmax;
958 const cBspModel_t* model;
959 unsigned int i;
960 /* get inline model, if it is one */
961 if (*name != '*') {
962 Com_Printf("Called Grid_RecalcRouting with no inline model\n");
963 return;
964 }
965 model = CM_InlineModel(mapTiles, name);
966 if (!model) {
967 Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name);
968 return;
969 }
970
971 #if 1
972 /* An attempt to fix the 'doors starting opened' bug (# 3456).
973 * The main difference is the (missing) rotation of the halfVec.
974 * The results are better, but do not fix the problem. */
975 CalculateMinsMaxs(model->angles, model->mins, model->maxs, model->origin, absmin, absmax);
976 #else
977 /* get the target model's dimensions */
978 if (VectorNotEmpty(model->angles)) {
979 vec3_t minVec, maxVec;
980 vec3_t centerVec, halfVec, newCenterVec;
981 vec3_t m[3];
982
983 /* Find the center of the extents. */
984 VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec);
985
986 /* Find the half height and half width of the extents. */
987 VectorSubtract(model->maxs, centerVec, halfVec);
988
989 /* Rotate the center about the origin. */
990 VectorCreateRotationMatrix(model->angles, m);
991 VectorRotate(m, centerVec, newCenterVec);
992
993 /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */
994 VectorSubtract(newCenterVec, halfVec, minVec);
995 VectorAdd(newCenterVec, halfVec, maxVec);
996
997 /* Now offset by origin then convert to position (Doors do not have 0 origins) */
998 VectorAdd(minVec, model->origin, absmin);
999 VectorAdd(maxVec, model->origin, absmax);
1000 } else { /* normal */
1001 /* Now offset by origin then convert to position (Doors do not have 0 origins) */
1002 VectorAdd(model->mins, model->origin, absmin);
1003 VectorAdd(model->maxs, model->origin, absmax);
1004 }
1005 #endif
1006 VecToPos(absmin, min);
1007 VecToPos(absmax, max);
1008
1009 /* fit min/max into the world size */
1010 max[0] = std::min(max[0], (pos_t)(PATHFINDING_WIDTH - 1));
1011 max[1] = std::min(max[1], (pos_t)(PATHFINDING_WIDTH - 1));
1012 max[2] = std::min(max[2], (pos_t)(PATHFINDING_HEIGHT - 1));
1013 for (i = 0; i < 3; i++)
1014 min[i] = std::max(min[i], (pos_t)0);
1015
1016 /* We now have the dimensions, call the generic rerouting function. */
1017 GridBox rerouteBox(min, max);
1018 Grid_RecalcBoxRouting(mapTiles, routing, rerouteBox, list);
1019 } else {
1020 /* use the passed box */
1021 Grid_RecalcBoxRouting(mapTiles, routing, box, list);
1022 }
1023 }
1024