Index: client/cl_le.c
===================================================================
--- client/cl_le.c (revision 15541)
+++ client/cl_le.c (working copy)
@@ -190,7 +190,11 @@
le->inuse = qfalse;
+ /* Recalc the client routing table!!! */
+ CL_CompleteRecalcRouting();
+ /* Now look for paths! */
CL_ConditionalMoveCalc(&clMap, selActor, MAX_ROUTE);
+
}
/**
Index: common/cmodel.c
===================================================================
--- common/cmodel.c (revision 15541)
+++ common/cmodel.c (working copy)
@@ -122,7 +122,7 @@
} mapTile_t;
/**
- * @brief Pathfinding routing structure
+ * @brief Pathfinding routing structure and tile layout
* @note Comments strongly WIP!
*
* ROUTE
@@ -159,6 +159,17 @@
* AREASTORED
* The stored mask (the cached move) of the routing data. (See Grid_MoveLength)
*
+ * TILE LAYOUT AND PATHING
+ * Maps are comprised of tiles. Each tile has a number of levels corresponding to entities in game.
+ * All static entities in the tile are located in levels 0-255, with the main world located in 0.
+ * Levels 256-258 are reserved, see LEVEL_* constants in src/shared/shared.h. Non-static entities
+ * (ET_BREAKABLE and ET_ROTATING, ET_DOOR, etc.) are contained in levels 259 and above. These entities'
+ * models are named *##, beginning from 1, and each corresponds to level LEVEL_STEPON + ##.
+ *
+ * The code that handles the pathing has separate checks for the static and non-static levels in a tile.
+ * The static levels have their bounds precalculated by CM_MakeTracingNodes and stored in tile->theads.
+ * The other levels are checked in the fly when Grid_CheckUnit is called.
+ *
*/
typedef struct routing_s {
byte route[HEIGHT][WIDTH][WIDTH];
@@ -803,11 +814,17 @@
/* get step height and trace vectors */
sh = R_STEP(map, x, y, z) ? sh_big : sh_low;
h = R_HEIGHT(map, x, y, z);
+
+ /* Setup the points used to check for walls and such */
VectorSet(pos, x, y, z);
PosToVec(pos, start);
- start[2] += h * 4;
+ /* Adjust height to actual floor plus stepping height. */
+ start[2] += h * 4 - UNIT_HEIGHT / 2 + sh * 4;
+ /*Set the end point to the edge of the field unit in the direction traveling, accounting for "step up" */
+ /** TODO: should this check into the center of the adjacent field unit insead of just to the edge? */
VectorSet(end, start[0] + (UNIT_HEIGHT / 2) * dvecs[dir][0], start[1] + (UNIT_HEIGHT / 2) * dvecs[dir][1], start[2]);
+
ax = x + dvecs[dir][0];
assert((ax >= 0) && (ax < WIDTH));
ay = y + dvecs[dir][1];
@@ -831,14 +848,15 @@
if (R_HEIGHT(map, ax, ay, az) > h)
return;
- /* center check */
- if (CM_EntTestLine(start, end))
- return;
+ /* Run multiple line tests to ensure that we can move between field units */
+ /** TODO: Do we need to use a bounding box to check for things in the way of this move? */
- /* lower check */
- start[2] = end[2] -= UNIT_HEIGHT / 2 - sh * 4 - 2;
- if (CM_EntTestLine(start, end))
- return;
+ for (h = sh * 4; h < PLAYER_HEIGHT; h += 4) {
+ if (CM_EntTestLine(start, end))
+ return;
+ /* raise check points */
+ start[2] = end[2] += 4;
+ }
/* allow connection to neighbor field */
map->route[z][y][x] |= (0x10 << dir) & UCHAR_MAX;
@@ -2433,6 +2451,45 @@
*/
/**
+ * @build Dumps contents of a map to console for inspection.
+ * @sa Grid_RecalcRouting
+ * @param[in] map The routing map (either server or client map)
+ * @param[in] lx The low end of the x range updated
+ * @param[in] ly The low end of the y range updated
+ * @param[in] lz The low end of the z range updated
+ * @param[in] hx The high end of the x range updated
+ * @param[in] hy The high end of the y range updated
+ * @param[in] hz The high end of the z range updated
+ */
+void Grid_DumpMap (struct routing_s *map, int lx, int ly, int lz, int hx, int hy, int hz)
+{
+ int x, y, z;
+
+ Com_Printf("\nGrid_DumpMap (%i %i %i) (%i %i %i)\n", lx, ly, lz, hx, hy, hz);
+ for (z = hz; z >= lz; --z) {
+ Com_Printf("\nLayer %i:\n ", z);
+ for (x = lx; x <= hx; ++x) {
+ Com_Printf("%9i", x);
+ }
+ Com_Printf("\n");
+ for (y = hy; y >= ly; --y) {
+ Com_Printf("%3i ", y);
+ for (x = lx; x <= hx; ++x) {
+ Com_Printf("%s%s%s%s%s%s%2i "
+ , R_CONN_NX(map, x, y, z) ? "w" : " "
+ , R_CONN_NY(map, x, y, z) ? "n" : " "
+ , R_CONN_PY(map, x, y, z) ? "s" : " "
+ , R_CONN_PX(map, x, y, z) ? "e" : " "
+ , R_STEP(map, x, y, z) ? "S" : " "
+ , R_FALL(map, x, y, z) ? "F" : " "
+ , R_HEIGHT(map, x, y, z));
+ }
+ Com_Printf("\n");
+ }
+ }
+}
+
+/**
* @brief Checks one field (square) on the grid of the given routing data (i.e. the map).
* @param[in] map Routing data/map.
* @param[in] x Field in x direction
@@ -3077,12 +3134,20 @@
(int)max[0], (int)max[1], (int)max[2]);
#endif
+#ifdef DEBUG
+ Grid_DumpMap(map, (int)min[0], (int)min[1], (int)min[2], (int)max[0], (int)max[1], (int)max[2]);
+#endif
+
/* check unit heights */
for (z = min[2]; z < max[2]; z++)
for (y = min[1]; y < max[1]; y++)
for (x = min[0]; x < max[0]; x++)
CM_CheckUnit(map, x, y, z);
+#ifdef DEBUG
+ Grid_DumpMap(map, (int)min[0], (int)min[1], (int)min[2], (int)max[0], (int)max[1], (int)max[2]);
+#endif
+
/* check connections */
for (z = min[2]; z < max[2]; z++)
for (y = min[1]; y < max[1]; y++)
@@ -3091,6 +3156,10 @@
for (i = 0; i < BASE_DIRECTIONS; i++)
CM_UpdateConnection(map, x, y, z, i, qtrue);
+#ifdef DEBUG
+ Grid_DumpMap(map, (int)min[0], (int)min[1], (int)min[2], (int)max[0], (int)max[1], (int)max[2]);
+#endif
+
inlineList = NULL;
}
Index: tools/ufo2map/routing.c
===================================================================
--- tools/ufo2map/routing.c (revision 15541)
+++ tools/ufo2map/routing.c (working copy)
@@ -169,6 +169,8 @@
{
int x, y, z, sz, ax, ay;
int i, h, sh;
+ int hc; /* height check */
+ qboolean vacant;
pos3_t pos;
vec3_t start, ts, te;
/* Falling deeper than one level or falling through the map is forbidden. This array */
@@ -194,7 +196,8 @@
/* prepare trace */
VectorSet(pos, x, y, z);
PosToVec(pos, start);
- start[2] += h * QUANT;
+ /* Adjust height to actual floor plus stepping height. */
+ start[2] += h * QUANT - UNIT_HEIGHT / 2 + sh * 4;
VectorCopy(start, ts);
sh = (step[y][x] & (1 << z)) ? SH_BIG : SH_LOW;
@@ -244,16 +247,16 @@
/* test for walls */
VectorAdd(start, move_vec[i], te);
- /* center check */
- if (TestLineMask(start, te, 2))
- continue;
-
- /* lower check */
- ts[2] = te[2] -= UNIT_HEIGHT / 2 - sh * QUANT - 2;
- if (TestLineMask(ts, te, 2))
- continue;
-
- route[z][y][x] |= 1 << (i + 4);
+ /* Check at multiple height levels */
+ for (hc = sh * QUANT, vacant = qtrue; hc < PLAYER_HEIGHT && vacant; hc+=4) {
+ /* perform check */
+ if (TestLineMask(ts, te, 2))
+ vacant=qfalse;
+ /* raise checking points */
+ ts[2] = te[2] += 4;
+ }
+ if (vacant)
+ route[z][y][x] |= 1 << (i + 4);
}
}