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