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); } }