diff --git a/main/source/detour/DetourNavMesh.cpp b/main/source/detour/DetourNavMesh.cpp index f6265057..0b3b2a93 100644 --- a/main/source/detour/DetourNavMesh.cpp +++ b/main/source/detour/DetourNavMesh.cpp @@ -284,7 +284,7 @@ dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flag if (dtStatusFailed(status)) return status; - return addTile(data, dataSize, flags, 0, 0); + return addTile(data, dataSize, flags, 0, 0, false); } /// @par @@ -979,7 +979,7 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co /// /// @see dtCreateNavMeshData, #removeTile dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, - dtTileRef lastRef, dtTileRef* result) + dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty) { // Make sure the data is in right format. dtMeshHeader* header = (dtMeshHeader*)data; @@ -1057,7 +1057,8 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); - + const int receivingOffMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->receivingOffMeshConCount); + unsigned char* d = data + headerSize; tile->verts = dtGetThenAdvanceBufferPointer(d, vertsSize); tile->polys = dtGetThenAdvanceBufferPointer(d, polysSize); @@ -1067,6 +1068,8 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, tile->detailTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); tile->bvTree = dtGetThenAdvanceBufferPointer(d, bvtreeSize); tile->offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshLinksSize); + tile->receivingOffMeshCons = dtGetThenAdvanceBufferPointer(d, receivingOffMeshLinksSize); + // If there are no items in the bvtree, reset the tree pointer. if (!bvtreeSize) @@ -1088,7 +1091,6 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, // Base off-mesh connections to their starting polygons and connect connections inside the tile. baseOffMeshLinks(tile); - connectExtOffMeshLinks(tile, tile, -1); // Create connections with neighbour tiles. static const int MAX_NEIS = 32; @@ -1104,8 +1106,6 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, connectExtLinks(tile, neis[j], -1); connectExtLinks(neis[j], tile, -1); - connectExtOffMeshLinks(tile, neis[j], -1); - connectExtOffMeshLinks(neis[j], tile, -1); } // Connect with neighbour tiles. @@ -1116,10 +1116,19 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, { connectExtLinks(tile, neis[j], i); connectExtLinks(neis[j], tile, dtOppositeTile(i)); - connectExtOffMeshLinks(tile, neis[j], i); - connectExtOffMeshLinks(neis[j], tile, dtOppositeTile(i)); } + } + GlobalOffMeshLinks(tile); + + if (bMarkOffMeshDirty) + { + for (int i = 0; i < tile->header->receivingOffMeshConCount; i++) + { + dtOffMeshConnection* targetCon = &tile->receivingOffMeshCons[i]; + + m_PendingOffMeshs[m_NumPendingConnections++] = targetCon; + } } if (result) @@ -1128,6 +1137,96 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, return DT_SUCCESS; } +void dtNavMesh::GlobalOffMeshLinks(dtMeshTile* target) +{ + if (!target) + return; + + for (int i = 0; i < target->header->offMeshConCount; ++i) + { + dtOffMeshConnection* targetCon = &target->offMeshCons[i]; + + dtPoly* targetPoly = &target->polys[targetCon->poly]; + // Skip off-mesh connections which start location could not be connected at all. + if (targetPoly->firstLink == DT_NULL_LINK) + continue; + + const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad }; + + + float bmin[3], bmax[3]; + dtVsub(bmin, &targetCon->pos[3], ext); + dtVadd(bmax, &targetCon->pos[3], ext); + + // Find tiles the query touches. + int minx, miny, maxx, maxy; + this->calcTileLoc(bmin, &minx, &miny); + this->calcTileLoc(bmax, &maxx, &maxy); + + static const int MAX_NEIS = 32; + dtMeshTile* neis[MAX_NEIS]; + + for (int y = miny; y <= maxy; ++y) + { + for (int x = minx; x <= maxx; ++x) + { + const int nneis = this->getTilesAt(x, y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + auto tile = neis[j]; + + + // Find polygon to connect to. + const float* p = &targetCon->pos[3]; + float nearestPt[3]; + dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt); + if (!ref) + continue; + // findNearestPoly may return too optimistic results, further check to make sure. + if (dtSqr(nearestPt[0] - p[0]) + dtSqr(nearestPt[2] - p[2]) > dtSqr(targetCon->rad)) + continue; + // Make sure the location is on current mesh. + float* v = &target->verts[targetPoly->verts[1] * 3]; + dtVcopy(v, nearestPt); + + // Link off-mesh connection to target poly. + unsigned int idx = allocLink(target); + if (idx != DT_NULL_LINK) + { + dtLink* link = &target->links[idx]; + link->ref = ref; + link->edge = (unsigned char)1; + link->side = (unsigned char)0xff; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = targetPoly->firstLink; + targetPoly->firstLink = idx; + } + + // Link target poly to off-mesh connection. + if (targetCon->flags & DT_OFFMESH_CON_BIDIR) + { + unsigned int tidx = allocLink(tile); + if (tidx != DT_NULL_LINK) + { + const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); + dtPoly* landPoly = &tile->polys[landPolyIdx]; + dtLink* link = &tile->links[tidx]; + link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly); + link->edge = (unsigned char)0xff; + link->side = (unsigned char)(0xff); + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = landPoly->firstLink; + landPoly->firstLink = tidx; + } + } + } + } + } + } +} + const dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const { // Find tile based on hash. diff --git a/main/source/detour/DetourNavMeshBuilder.cpp b/main/source/detour/DetourNavMeshBuilder.cpp index e93a9762..fd245c41 100644 --- a/main/source/detour/DetourNavMeshBuilder.cpp +++ b/main/source/detour/DetourNavMeshBuilder.cpp @@ -289,40 +289,41 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, return false; const int nvp = params->nvp; - + // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. unsigned char* offMeshConClass = 0; int storedOffMeshConCount = 0; + int receiveOffMeshConCount = 0; int offMeshConLinkCount = 0; - + if (params->offMeshConCount > 0) { - offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); + offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char) * params->offMeshConCount * 2, DT_ALLOC_TEMP); if (!offMeshConClass) return false; // Find tight heigh bounds, used for culling out off-mesh start locations. float hmin = FLT_MAX; float hmax = -FLT_MAX; - + if (params->detailVerts && params->detailVertsCount) { for (int i = 0; i < params->detailVertsCount; ++i) { - const float h = params->detailVerts[i*3+1]; - hmin = dtMin(hmin,h); - hmax = dtMax(hmax,h); + const float h = params->detailVerts[i * 3 + 1]; + hmin = dtMin(hmin, h); + hmax = dtMax(hmax, h); } } else { for (int i = 0; i < params->vertCount; ++i) { - const unsigned short* iv = ¶ms->verts[i*3]; + const unsigned short* iv = ¶ms->verts[i * 3]; const float h = params->bmin[1] + iv[1] * params->ch; - hmin = dtMin(hmin,h); - hmax = dtMax(hmax,h); + hmin = dtMin(hmin, h); + hmax = dtMax(hmax, h); } } hmin -= params->walkableClimb; @@ -335,55 +336,62 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, for (int i = 0; i < params->offMeshConCount; ++i) { - const float* p0 = ¶ms->offMeshConVerts[(i*2+0)*3]; - const float* p1 = ¶ms->offMeshConVerts[(i*2+1)*3]; - offMeshConClass[i*2+0] = classifyOffMeshPoint(p0, bmin, bmax); - offMeshConClass[i*2+1] = classifyOffMeshPoint(p1, bmin, bmax); + const float* p0 = ¶ms->offMeshConVerts[(i * 2 + 0) * 3]; + const float* p1 = ¶ms->offMeshConVerts[(i * 2 + 1) * 3]; + offMeshConClass[i * 2 + 0] = classifyOffMeshPoint(p0, bmin, bmax); + offMeshConClass[i * 2 + 1] = classifyOffMeshPoint(p1, bmin, bmax); // Zero out off-mesh start positions which are not even potentially touching the mesh. - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) { if (p0[1] < bmin[1] || p0[1] > bmax[1]) - offMeshConClass[i*2+0] = 0; + offMeshConClass[i * 2 + 0] = 0; } // Cound how many links should be allocated for off-mesh connections. - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) offMeshConLinkCount++; - if (offMeshConClass[i*2+1] == 0xff) + if (offMeshConClass[i * 2 + 1] == 0xff) offMeshConLinkCount++; - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) + { storedOffMeshConCount++; + } + else if (offMeshConClass[i * 2 + 1] == 0xff) + { + receiveOffMeshConCount++; + } + } } - + // Off-mesh connectionss are stored as polygons, adjust values. const int totPolyCount = params->polyCount + storedOffMeshConCount; - const int totVertCount = params->vertCount + storedOffMeshConCount*2; - + const int totVertCount = params->vertCount + storedOffMeshConCount * 2; + // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < params->polyCount; ++i) { - const unsigned short* p = ¶ms->polys[i*2*nvp]; + const unsigned short* p = ¶ms->polys[i * 2 * nvp]; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; edgeCount++; - - if (p[nvp+j] & 0x8000) + + if (p[nvp + j] & 0x8000) { - unsigned short dir = p[nvp+j] & 0xf; + unsigned short dir = p[nvp + j] & 0xf; if (dir != 0xf) portalCount++; } } } - const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2; - + const int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; + // Find unique detail vertices. int uniqueDetailVertCount = 0; int detailTriCount = 0; @@ -393,8 +401,8 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, detailTriCount = params->detailTriCount; for (int i = 0; i < params->polyCount; ++i) { - const unsigned short* p = ¶ms->polys[i*nvp*2]; - int ndv = params->detailMeshes[i*4+1]; + const unsigned short* p = ¶ms->polys[i * nvp * 2]; + int ndv = params->detailMeshes[i * 4 + 1]; int nv = 0; for (int j = 0; j < nvp; ++j) { @@ -412,40 +420,41 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, detailTriCount = 0; for (int i = 0; i < params->polyCount; ++i) { - const unsigned short* p = ¶ms->polys[i*nvp*2]; + const unsigned short* p = ¶ms->polys[i * nvp * 2]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } - detailTriCount += nv-2; + detailTriCount += nv - 2; } } - + // Calculate data size const int headerSize = dtAlign4(sizeof(dtMeshHeader)); - const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); - const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); - const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); - const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); - const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); - const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount); - const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0; - const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); - + const int vertsSize = dtAlign4(sizeof(float) * 3 * totVertCount); + const int polysSize = dtAlign4(sizeof(dtPoly) * totPolyCount); + const int linksSize = dtAlign4(sizeof(dtLink) * maxLinkCount); + const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * params->polyCount); + const int detailVertsSize = dtAlign4(sizeof(float) * 3 * uniqueDetailVertCount); + const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * detailTriCount); + const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode) * params->polyCount * 2) : 0; + const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection) * storedOffMeshConCount); + const int receiveOffMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection) * receiveOffMeshConCount); + const int dataSize = headerSize + vertsSize + polysSize + linksSize + - detailMeshesSize + detailVertsSize + detailTrisSize + - bvTreeSize + offMeshConsSize; - - unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); + detailMeshesSize + detailVertsSize + detailTrisSize + + bvTreeSize + offMeshConsSize + receiveOffMeshConsSize; + + unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char) * dataSize, DT_ALLOC_PERM); if (!data) { dtFree(offMeshConClass); return false; } memset(data, 0, dataSize); - + unsigned char* d = data; dtMeshHeader* header = dtGetThenAdvanceBufferPointer(d, headerSize); @@ -457,8 +466,9 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, unsigned char* navDTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer(d, bvTreeSize); dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshConsSize); - - + dtOffMeshConnection* receivingOffMeshCons = dtGetThenAdvanceBufferPointer(d, receiveOffMeshConsSize); + + // Store header header->magic = DT_NAVMESH_MAGIC; header->version = DT_NAVMESH_VERSION; @@ -480,17 +490,18 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, header->walkableRadius = params->walkableRadius; header->walkableClimb = params->walkableClimb; header->offMeshConCount = storedOffMeshConCount; - header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0; - + header->receivingOffMeshConCount = receiveOffMeshConCount; + header->bvNodeCount = params->buildBvTree ? params->polyCount * 2 : 0; + const int offMeshVertsBase = params->vertCount; const int offMeshPolyBase = params->polyCount; - + // Store vertices // Mesh vertices for (int i = 0; i < params->vertCount; ++i) { - const unsigned short* iv = ¶ms->verts[i*3]; - float* v = &navVerts[i*3]; + const unsigned short* iv = ¶ms->verts[i * 3]; + float* v = &navVerts[i * 3]; v[0] = params->bmin[0] + iv[0] * params->cs; v[1] = params->bmin[1] + iv[1] * params->ch; v[2] = params->bmin[2] + iv[2] * params->cs; @@ -500,16 +511,16 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) { - const float* linkv = ¶ms->offMeshConVerts[i*2*3]; - float* v = &navVerts[(offMeshVertsBase + n*2)*3]; + const float* linkv = ¶ms->offMeshConVerts[i * 2 * 3]; + float* v = &navVerts[(offMeshVertsBase + n * 2) * 3]; dtVcopy(&v[0], &linkv[0]); dtVcopy(&v[3], &linkv[3]); n++; } } - + // Store polygons // Mesh polys const unsigned short* src = params->polys; @@ -524,10 +535,10 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, { if (src[j] == MESH_NULL_IDX) break; p->verts[j] = src[j]; - if (src[nvp+j] & 0x8000) + if (src[nvp + j] & 0x8000) { // Border or portal edge. - unsigned short dir = src[nvp+j] & 0xf; + unsigned short dir = src[nvp + j] & 0xf; if (dir == 0xf) // Border p->neis[j] = 0; else if (dir == 0) // Portal x- @@ -542,24 +553,24 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, else { // Normal connection - p->neis[j] = src[nvp+j]+1; + p->neis[j] = src[nvp + j] + 1; } - + p->vertCount++; } - src += nvp*2; + src += nvp * 2; } // Off-mesh connection vertices. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) { - dtPoly* p = &navPolys[offMeshPolyBase+n]; + dtPoly* p = &navPolys[offMeshPolyBase + n]; p->vertCount = 2; - p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); - p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); + p->verts[0] = (unsigned short)(offMeshVertsBase + n * 2 + 0); + p->verts[1] = (unsigned short)(offMeshVertsBase + n * 2 + 1); p->flags = params->offMeshConFlags[i]; p->setArea(params->offMeshConAreas[i]); p->setType(DT_POLYTYPE_OFFMESH_CONNECTION); @@ -576,22 +587,22 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; - const int vb = (int)params->detailMeshes[i*4+0]; - const int ndv = (int)params->detailMeshes[i*4+1]; + const int vb = (int)params->detailMeshes[i * 4 + 0]; + const int ndv = (int)params->detailMeshes[i * 4 + 1]; const int nv = navPolys[i].vertCount; dtl.vertBase = (unsigned int)vbase; - dtl.vertCount = (unsigned char)(ndv-nv); - dtl.triBase = (unsigned int)params->detailMeshes[i*4+2]; - dtl.triCount = (unsigned char)params->detailMeshes[i*4+3]; + dtl.vertCount = (unsigned char)(ndv - nv); + dtl.triBase = (unsigned int)params->detailMeshes[i * 4 + 2]; + dtl.triCount = (unsigned char)params->detailMeshes[i * 4 + 3]; // Copy vertices except the first 'nv' verts which are equal to nav poly verts. - if (ndv-nv) + if (ndv - nv) { - memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); - vbase += (unsigned short)(ndv-nv); + memcpy(&navDVerts[vbase * 3], ¶ms->detailVerts[(vb + nv) * 3], sizeof(float) * 3 * (ndv - nv)); + vbase += (unsigned short)(ndv - nv); } } // Store triangles. - memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); + memcpy(navDTris, params->detailTris, sizeof(unsigned char) * 4 * params->detailTriCount); } else { @@ -604,18 +615,18 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, dtl.vertBase = 0; dtl.vertCount = 0; dtl.triBase = (unsigned int)tbase; - dtl.triCount = (unsigned char)(nv-2); + dtl.triCount = (unsigned char)(nv - 2); // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) { - unsigned char* t = &navDTris[tbase*4]; + unsigned char* t = &navDTris[tbase * 4]; t[0] = 0; - t[1] = (unsigned char)(j-1); + t[1] = (unsigned char)(j - 1); t[2] = (unsigned char)j; // Bit for each edge that belongs to poly boundary. - t[3] = (1<<2); - if (j == 2) t[3] |= (1<<0); - if (j == nv-1) t[3] |= (1<<4); + t[3] = (1 << 2); + if (j == 2) t[3] |= (1 << 0); + if (j == nv - 1) t[3] |= (1 << 4); tbase++; } } @@ -624,36 +635,63 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, // Store and create BVtree. if (params->buildBvTree) { - createBVTree(params, navBvtree, 2*params->polyCount); + createBVTree(params, navBvtree, 2 * params->polyCount); } - + // Store Off-Mesh connections. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. - if (offMeshConClass[i*2+0] == 0xff) + if (offMeshConClass[i * 2 + 0] == 0xff) { dtOffMeshConnection* con = &offMeshCons[n]; con->poly = (unsigned short)(offMeshPolyBase + n); // Copy connection end-points. - const float* endPts = ¶ms->offMeshConVerts[i*2*3]; + const float* endPts = ¶ms->offMeshConVerts[i * 2 * 3]; dtVcopy(&con->pos[0], &endPts[0]); dtVcopy(&con->pos[3], &endPts[3]); con->rad = params->offMeshConRad[i]; con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; - con->side = offMeshConClass[i*2+1]; + con->side = offMeshConClass[i * 2 + 1]; if (params->offMeshConUserID) con->userId = params->offMeshConUserID[i]; n++; } } - + + n = 0; + for (int i = 0; i < params->offMeshConCount; ++i) + { + // Only store connections which start from this tile. + if (offMeshConClass[i * 2 + 0] != 0xff && offMeshConClass[i * 2 + 1] == 0xff) + { + dtOffMeshConnection* con = &receivingOffMeshCons[n]; + con->poly = (unsigned short)(offMeshPolyBase + n); + // Copy connection end-points. + const float* endPts = ¶ms->offMeshConVerts[i * 2 * 3]; + dtVcopy(&con->pos[0], &endPts[0]); + dtVcopy(&con->pos[3], &endPts[3]); + con->rad = params->offMeshConRad[i]; + con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; + con->side = offMeshConClass[i * 2 + 0]; + if (params->offMeshConUserID) + con->userId = params->offMeshConUserID[i]; + n++; + } + } + + if (header->offMeshConCount > 0) + { + int thing = 0; + dtIgnoreUnused(thing); + } + dtFree(offMeshConClass); - + *outData = data; *outDataSize = dataSize; - + return true; } @@ -686,6 +724,7 @@ bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/) dtSwapEndian(&header->detailTriCount); dtSwapEndian(&header->bvNodeCount); dtSwapEndian(&header->offMeshConCount); + dtSwapEndian(&header->receivingOffMeshConCount); dtSwapEndian(&header->offMeshBase); dtSwapEndian(&header->walkableHeight); dtSwapEndian(&header->walkableRadius); @@ -728,6 +767,7 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); + const int receiveOffMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->receivingOffMeshConCount); unsigned char* d = data + headerSize; float* verts = dtGetThenAdvanceBufferPointer(d, vertsSize); @@ -740,6 +780,7 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) //unsigned char* detailTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); dtBVNode* bvTree = dtGetThenAdvanceBufferPointer(d, bvtreeSize); dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshLinksSize); + dtOffMeshConnection* receivingOffMeshCons = dtGetThenAdvanceBufferPointer(d, receiveOffMeshLinksSize); // Vertices for (int i = 0; i < header->vertCount*3; ++i) @@ -797,6 +838,16 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) dtSwapEndian(&con->rad); dtSwapEndian(&con->poly); } + + // Receiving Off-mesh Connections. + for (int i = 0; i < header->receivingOffMeshConCount; ++i) + { + dtOffMeshConnection* con = &receivingOffMeshCons[i]; + for (int j = 0; j < 6; ++j) + dtSwapEndian(&con->pos[j]); + dtSwapEndian(&con->rad); + dtSwapEndian(&con->poly); + } return true; } diff --git a/main/source/detour/DetourTileCache.cpp b/main/source/detour/DetourTileCache.cpp index 634bc4ac..0f1d5a5f 100644 --- a/main/source/detour/DetourTileCache.cpp +++ b/main/source/detour/DetourTileCache.cpp @@ -584,7 +584,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh, { // Build mesh const dtCompressedTileRef ref = m_update[0]; - status = buildNavMeshTile(ref, navmesh); + status = buildNavMeshTile(ref, navmesh, true); m_nupdate--; if (m_nupdate > 0) memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef)); @@ -628,6 +628,56 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh, } } } + + if (navmesh->GetNumPendingOffMeshConnections() > 0) + { + + for (int i = 0; i < navmesh->GetNumPendingOffMeshConnections(); i++) + { + int NumTiles = 0; + + float* connspos = &navmesh->GetPendingConnection(i)->pos[0]; + float* connepos = &navmesh->GetPendingConnection(i)->pos[3]; + + float ext[3] = { 10.0f, 10.0f, 10.0f }; + + float searchsposMin[3]; + float searchsposMax[3]; + + float searcheposMin[3]; + float searcheposMax[3]; + + dtVsub(searchsposMin, connspos, ext); + dtVadd(searchsposMax, connspos, ext); + + dtVsub(searcheposMin, connepos, ext); + dtVadd(searcheposMax, connepos, ext); + + dtCompressedTileRef AffectedTiles[DT_MAX_TOUCHED_TILES]; + + + queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]); + + buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false); + } + + queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]); + + buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false); + } + + } + + navmesh->ClearPendingOffMeshConnections(); + } if (upToDate) *upToDate = m_nupdate == 0 && m_nreqs == 0; @@ -636,7 +686,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh, } -dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh) +dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh, bool bMarkOffMeshDirty) { const int MAX_TILES = 32; dtCompressedTileRef tiles[MAX_TILES]; @@ -644,7 +694,7 @@ dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* for (int i = 0; i < ntiles; ++i) { - dtStatus status = buildNavMeshTile(tiles[i], navmesh); + dtStatus status = buildNavMeshTile(tiles[i], navmesh, bMarkOffMeshDirty); if (dtStatusFailed(status)) return status; } @@ -652,7 +702,7 @@ dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* return DT_SUCCESS; } -dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh) +dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh, bool bMarkOffMeshDirty) { dtAssert(m_talloc); dtAssert(m_tcomp); @@ -768,7 +818,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* if (navData) { // Let the navmesh own the data. - status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0); + status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0, bMarkOffMeshDirty); if (dtStatusFailed(status)) { dtFree(navData); diff --git a/main/source/detour/Include/DetourNavMesh.h b/main/source/detour/Include/DetourNavMesh.h index 7f5a2606..0aa133b6 100644 --- a/main/source/detour/Include/DetourNavMesh.h +++ b/main/source/detour/Include/DetourNavMesh.h @@ -265,6 +265,7 @@ struct dtMeshHeader int detailTriCount; ///< The number of triangles in the detail mesh. int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.) int offMeshConCount; ///< The number of off-mesh connections. + int receivingOffMeshConCount; int offMeshBase; ///< The index of the first polygon which is an off-mesh connection. float walkableHeight; ///< The height of the agents using the tile. float walkableRadius; ///< The radius of the agents using the tile. @@ -301,6 +302,7 @@ struct dtMeshTile dtBVNode* bvTree; dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] + dtOffMeshConnection* receivingOffMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.) int dataSize; ///< Size of the tile data. @@ -367,7 +369,7 @@ public: /// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0] /// @param[out] result The tile reference. (If the tile was succesfully added.) [opt] /// @return The status flags for the operation. - dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result); + dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty); /// Removes the specified tile from the navigation mesh. /// @param[in] ref The reference of the tile to remove. @@ -465,6 +467,10 @@ public: /// @param[in] ref The polygon reference of the off-mesh connection. /// @return The specified off-mesh connection, or null if the polygon reference is not valid. const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const; + + int GetNumPendingOffMeshConnections() { return m_NumPendingConnections; } + void ClearPendingOffMeshConnections() { m_NumPendingConnections = 0; } + dtOffMeshConnection* GetPendingConnection(int index) { return m_PendingOffMeshs[index]; } /// @} @@ -514,6 +520,8 @@ public: /// @param[in] maxDataSize The size of the state within the data buffer. /// @return The status flags for the operation. dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize); + + void GlobalOffMeshLinks(dtMeshTile* target); /// @} @@ -663,6 +671,9 @@ private: int m_tileLutSize; ///< Tile hash lookup size (must be pot). int m_tileLutMask; ///< Tile hash lookup mask. + dtOffMeshConnection* m_PendingOffMeshs[1024]; + int m_NumPendingConnections = 0; + dtMeshTile** m_posLookup; ///< Tile hash lookup. dtMeshTile* m_nextFree; ///< Freelist of tiles. dtMeshTile* m_tiles; ///< List of tiles. diff --git a/main/source/detour/Include/DetourTileCache.h b/main/source/detour/Include/DetourTileCache.h index 4bd72af8..2a218fcb 100644 --- a/main/source/detour/Include/DetourTileCache.h +++ b/main/source/detour/Include/DetourTileCache.h @@ -166,9 +166,9 @@ public: /// otherwise another call will continue processing obstacle requests and tile rebuilds. dtStatus update(const float dt, class dtNavMesh* navmesh, bool* upToDate = 0); - dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh); + dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh, bool bMarkOffMeshDirty); - dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh); + dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh, bool bMarkOffMeshDirty); void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const; diff --git a/main/source/detour/Include/DetourTileCacheBuilder.h b/main/source/detour/Include/DetourTileCacheBuilder.h index ba6e2205..da03a6df 100644 --- a/main/source/detour/Include/DetourTileCacheBuilder.h +++ b/main/source/detour/Include/DetourTileCacheBuilder.h @@ -27,12 +27,13 @@ static const int DT_TILECACHE_VERSION = 1; static const unsigned char DT_TILECACHE_NULL_AREA = 0; static const unsigned char DT_TILECACHE_CROUCH_AREA = 1; -static const unsigned char DT_TILECACHE_BLOCKED_AREA = 3; -static const unsigned char DT_TILECACHE_CLIMBABLE_AREA = 4; -static const unsigned char DT_TILECACHE_LADDER_AREA = 5; -static const unsigned char DT_TILECACHE_MSTRUCTURE_AREA = 12; -static const unsigned char DT_TILECACHE_ASTRUCTURE_AREA = 13; -static const unsigned char DT_TILECACHE_WELD_AREA = 15; +static const unsigned char DT_TILECACHE_BLOCKED_AREA = 2; +static const unsigned char DT_TILECACHE_WALLCLIMB_AREA = 3; +static const unsigned char DT_TILECACHE_LADDER_AREA = 4; +static const unsigned char DT_TILECACHE_TEAM1STRUCTURE_AREA = 5; +static const unsigned char DT_TILECACHE_TEAM2STRUCTURE_AREA = 6; +static const unsigned char DT_TILECACHE_WELD_AREA = 7; +static const unsigned char DT_TILECACHE_DOOR_AREA = 8; static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63; static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff; diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index f1a96659..64d9e08f 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -272,6 +272,9 @@ void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot) case SAMPLE_POLYFLAGS_BLOCKED: UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 128, 128, 128); break; + case SAMPLE_POLYFLAGS_PHASEGATE: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 255, 128, 128); + break; default: UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc); break; diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index 6d87e34d..3287bf5d 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -236,6 +236,62 @@ struct MeshProcess : public dtTileCacheMeshProcess } } + void GetOffMeshConnectionPoints(int Index, Vector& OutStartLoc, Vector& OutEndLoc) + { + OutStartLoc = ZERO_VECTOR; + OutEndLoc = ZERO_VECTOR; + + if (Index > -1 && Index < MAX_OFFMESH_CONNS) + { + float* src = &OffMeshVerts[Index * 3 * 2]; + + OutStartLoc.x = src[0]; + OutStartLoc.y = -src[2]; + OutStartLoc.z = src[1]; + + OutEndLoc.x = src[3]; + OutEndLoc.y = -src[5]; + OutEndLoc.z = src[4]; + } + } + + void DrawAllConnections(float DrawTime) + { + Vector StartLine = ZERO_VECTOR; + Vector EndLine = ZERO_VECTOR; + + for (int i = 0; i < NumOffMeshConns; i++) + { + Vector StartLine = Vector(OffMeshVerts[i * 6], -OffMeshVerts[(i * 6) + 2], OffMeshVerts[(i * 6) + 1]); + Vector EndLine = Vector(OffMeshVerts[(i * 6) + 3], -OffMeshVerts[(i * 6) + 5], OffMeshVerts[(i * 6) + 4]); + + switch (OffMeshFlags[i]) + { + case SAMPLE_POLYFLAGS_WALK: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 255); + break; + case SAMPLE_POLYFLAGS_JUMP: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 0); + break; + case SAMPLE_POLYFLAGS_WALLCLIMB: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 0); + break; + case SAMPLE_POLYFLAGS_FALL: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 0, 0); + break; + case SAMPLE_POLYFLAGS_LADDER: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 0, 255); + break; + case SAMPLE_POLYFLAGS_PHASEGATE: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 128, 128); + break; + default: + UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 255); + break; + } + } + } + virtual void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags) { @@ -300,6 +356,19 @@ struct MeshProcess : public dtTileCacheMeshProcess } }; +void AIDEBUG_DrawOffMeshConnections(float DrawTime) +{ + if (NavMeshes[0].tileCache) + { + MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[0].tileCache->getMeshProcess(); + + if (m_tmproc) + { + m_tmproc->DrawAllConnections(DrawTime); + } + } +} + void UTIL_UpdateTileCache() { for (int i = 0; i < MAX_NAV_MESHES; i++) @@ -799,7 +868,7 @@ bool LoadNavMesh(const char* mapname) } if (tile) - NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh); + NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh, false); } for (int i = 0; i < header.numOnosTiles; ++i) @@ -838,7 +907,7 @@ bool LoadNavMesh(const char* mapname) } if (tile) - NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh); + NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh, false); } for (int i = 0; i < header.numBuildingTiles; ++i) @@ -877,7 +946,7 @@ bool LoadNavMesh(const char* mapname) } if (tile) - NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh); + NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh, false); } fclose(savedFile); @@ -949,7 +1018,6 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DUCKJUMP); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD); - BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_LADDER); BaseNavProfiles[GORGE_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH; BaseNavProfiles[GORGE_BASE_NAV_PROFILE].bFlyingProfile = false; @@ -2882,8 +2950,17 @@ void NewMove(AvHAIPlayer* pBot) } break; case SAMPLE_POLYFLAGS_LADDER: - LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea); - break; + { + if (IsPlayerSkulk(pBot->Edict)) + { + SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea); + } + else + { + LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea); + } + } + break; case SAMPLE_POLYFLAGS_PHASEGATE: PhaseGateMove(pBot, MoveFrom, MoveTo); break; @@ -3081,8 +3158,6 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin edict_t* pEdict = pBot->Edict; AvHPlayer* AIPlayer = pBot->Player; - - const Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint); bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z); @@ -3346,6 +3421,131 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin } } +void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea) +{ + edict_t* pEdict = pBot->Edict; + AvHPlayer* AIPlayer = pBot->Player; + + const Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint); + + bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z); + + Vector LadderNormal = UTIL_GetNearestLadderNormal(pBot->Edict); + + const Vector LadderRightNormal = UTIL_GetVectorNormal(UTIL_GetCrossProduct(LadderNormal, UP_VECTOR)); + + Vector ClimbRightNormal = LadderRightNormal; + + if (bIsGoingUpLadder) + { + pBot->Button &= ~IN_DUCK; + + ClimbRightNormal = -LadderRightNormal; + + Vector HullTraceTo = EndPoint; + HullTraceTo.z = pBot->CollisionHullBottomLocation.z; + + // We have reached our desired climb height and want to get off the ladder + if ((pBot->Edict->v.origin.z >= RequiredClimbHeight) && UTIL_QuickHullTrace(pEdict, pEdict->v.origin, Vector(EndPoint.x, EndPoint.y, pEdict->v.origin.z), head_hull)) + { + // Move directly towards the desired get-off point, looking slightly up still + pBot->desiredMovementDir = vForward; + + Vector LookLocation = EndPoint; + LookLocation.z = pBot->CurrentEyePosition.z + 64.0f; + + BotMoveLookAt(pBot, LookLocation); + + // If the get-off point is opposite the ladder, then jump to get to it + if (UTIL_GetDotProduct(pBot->CurrentLadderNormal, vForward) > 0.75f) + { + BotJump(pBot); + } + + return; + } + else + { + // This is for cases where the ladder physically doesn't reach the desired get-off point and the bot kind of has to "jump" up off the ladder. + if (pBot->CollisionHullTopLocation.z >= UTIL_GetNearestLadderTopPoint(pEdict).z) + { + pBot->desiredMovementDir = vForward; + // We look up really far to get maximum launch + BotMoveLookAt(pBot, EndPoint + Vector(0.0f, 0.0f, 100.0f)); + return; + } + + // Still climbing the ladder. Look up, and move left/right on the ladder to avoid any blockages + + Vector StartLeftTrace = pBot->CollisionHullTopLocation - (ClimbRightNormal * GetPlayerRadius(pBot->Player)); + Vector StartRightTrace = pBot->CollisionHullTopLocation + (ClimbRightNormal * GetPlayerRadius(pBot->Player)); + + bool bBlockedLeft = !UTIL_QuickTrace(pEdict, StartLeftTrace, StartLeftTrace + Vector(0.0f, 0.0f, 32.0f)); + bool bBlockedRight = !UTIL_QuickTrace(pEdict, StartRightTrace, StartRightTrace + Vector(0.0f, 0.0f, 32.0f)); + + // Look up at the top of the ladder + + // If we are blocked going up the ladder, face the ladder and slide left/right to avoid blockage + if (bBlockedLeft && !bBlockedRight) + { + Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f); + LookLocation.z = RequiredClimbHeight + 100.0f; + BotMoveLookAt(pBot, LookLocation); + + pBot->desiredMovementDir = ClimbRightNormal; + return; + } + + if (bBlockedRight && !bBlockedLeft) + { + Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f); + LookLocation.z = RequiredClimbHeight + 100.0f; + BotMoveLookAt(pBot, LookLocation); + + pBot->desiredMovementDir = -ClimbRightNormal; + return; + } + + Vector LookLocation = UTIL_GetNearestLadderTopPoint(pBot->Edict); + + LookLocation.z = RequiredClimbHeight + 100.0f; + BotMoveLookAt(pBot, LookLocation); + + pBot->desiredMovementDir = -UTIL_GetNearestLadderNormal(pBot->Edict); + } + + + } + else + { + + // We're going down the ladder + + Vector StartLeftTrace = pBot->CollisionHullBottomLocation - (LadderRightNormal * (GetPlayerRadius(pBot->Player) + 2.0f)); + Vector StartRightTrace = pBot->CollisionHullBottomLocation + (LadderRightNormal * (GetPlayerRadius(pBot->Player) + 2.0f)); + + bool bBlockedLeft = !UTIL_QuickTrace(pEdict, StartLeftTrace, StartLeftTrace - Vector(0.0f, 0.0f, 32.0f)); + bool bBlockedRight = !UTIL_QuickTrace(pEdict, StartRightTrace, StartRightTrace - Vector(0.0f, 0.0f, 32.0f)); + + if (bBlockedLeft) + { + pBot->desiredMovementDir = LadderRightNormal; + return; + } + + if (bBlockedRight) + { + pBot->desiredMovementDir = -LadderRightNormal; + return; + } + + pBot->desiredMovementDir = pBot->CurrentLadderNormal; + + BotMoveLookAt(pBot, EndPoint); + } + +} + void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint) { DeployableSearchFilter PGFilter; @@ -4465,27 +4665,41 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) { if (bReverseCourse) { - LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); - - // We're going DOWN the ladder - if (MoveTo.z > MoveFrom.z) + if (IsPlayerSkulk(pBot->Edict)) { - if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f) + SkulkLadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + } + else + { + LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + + // We're going DOWN the ladder + if (MoveTo.z > MoveFrom.z) { - BotJump(pBot); + if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f) + { + BotJump(pBot); + } } } } else { - LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); - - // We're going DOWN the ladder - if (MoveFrom.z > MoveTo.z) + if (IsPlayerSkulk(pBot->Edict)) { - if (pBot->Edict->v.origin.z - MoveTo.z < 150.0f) + SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + } + else + { + LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + + // We're going DOWN the ladder + if (MoveFrom.z > MoveTo.z) { - BotJump(pBot); + if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f) + { + BotJump(pBot); + } } } } @@ -6959,4 +7173,130 @@ unsigned char UTIL_GetNextBotCurrentPathArea(AvHAIPlayer* pBot) if (pBot->BotNavInfo.PathSize == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.PathSize - 1) { return SAMPLE_POLYAREA_GROUND; } return pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area; +} + +int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional) +{ + if (NavMeshes[REGULAR_NAV_MESH].tileCache) + { + MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess(); + + if (m_tmproc) + { + return m_tmproc->AddOffMeshConnectionDef(StartLoc, EndLoc, area, flags, bBiDirectional); + UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc); + } + } +} + +void UTIL_RemoveOffMeshConnection(int ConnectionIndex) +{ + Vector StartLoc, EndLoc; + + if (NavMeshes[REGULAR_NAV_MESH].tileCache) + { + MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess(); + + if (m_tmproc) + { + m_tmproc->GetOffMeshConnectionPoints(ConnectionIndex, StartLoc, EndLoc); + m_tmproc->RemoveOffMeshConnectionDef(ConnectionIndex); + } + } + + UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc); +} + +void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc) +{ + float ext[3] = { 10.0f, 10.0f, 10.0f }; + + float spos[3] = { StartLoc.x, StartLoc.z, -StartLoc.y }; + float epos[3] = { EndLoc.x, EndLoc.z, -EndLoc.y }; + + float searchsposMin[3]; + float searchsposMax[3]; + + float searcheposMin[3]; + float searcheposMax[3]; + + dtVsub(searchsposMin, spos, ext); + dtVadd(searchsposMax, spos, ext); + + dtVsub(searcheposMin, epos, ext); + dtVadd(searcheposMax, epos, ext); + + int NumTiles = 0; + dtCompressedTileRef AffectedTiles[DT_MAX_TOUCHED_TILES]; + + if (NavMeshes[REGULAR_NAV_MESH].tileCache && NavMeshes[REGULAR_NAV_MESH].navMesh) + { + NumTiles = 0; + + NavMeshes[REGULAR_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[REGULAR_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[REGULAR_NAV_MESH].navMesh, false); + } + + NavMeshes[REGULAR_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[REGULAR_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[REGULAR_NAV_MESH].navMesh, false); + } + + + } + + if (NavMeshes[ONOS_NAV_MESH].tileCache && NavMeshes[ONOS_NAV_MESH].navMesh) + { + NumTiles = 0; + + NavMeshes[ONOS_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[ONOS_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[ONOS_NAV_MESH].navMesh, false); + } + + NavMeshes[ONOS_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[ONOS_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[ONOS_NAV_MESH].navMesh, false); + } + } + + if (NavMeshes[BUILDING_NAV_MESH].tileCache && NavMeshes[BUILDING_NAV_MESH].navMesh) + { + NumTiles = 0; + + NavMeshes[BUILDING_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[BUILDING_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[BUILDING_NAV_MESH].navMesh, false); + } + + NavMeshes[BUILDING_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES); + + for (int i = 0; i < NumTiles; i++) + { + const dtCompressedTile* Tile = NavMeshes[BUILDING_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]); + + NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[BUILDING_NAV_MESH].navMesh, false); + } + } } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index ec668975..3f256869 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -236,6 +236,8 @@ void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoi void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint); // Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea); +// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint as skulk, which requires different movement +void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea); // Called by NewMove, determines the movement direction and inputs required to climb a wall to reach endpoint void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight); void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight); @@ -269,6 +271,8 @@ bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination); // Called every bot frame (default is 60fps). Ensures the tile cache is updated after obstacles are placed void UTIL_UpdateTileCache(); +void AIDEBUG_DrawOffMeshConnections(float DrawTime); + Vector UTIL_GetNearestPointOnNavWall(AvHAIPlayer* pBot, const float MaxRadius); Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector Location, const float MaxRadius); @@ -289,6 +293,11 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef); void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs); +int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional); +void UTIL_RemoveOffMeshConnection(int ConnectionIndex); +void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc); + + /* Safely aborts the current movement the bot is performing. Returns true if the bot has successfully aborted, and is ready to calculate a new path.