// ----------------------------------------------------------------------------------------------------------- // Insert the point at the center of the CSide connecting two segments between the two points. // This is messy because we must insert into the list. The simplest (and not too slow) way to do this is to start // at the end of the list and go backwards. int InsertCenterPoints (tPointSeg *pointSegP, int numPoints) { int i, j; for (i = 0; i < numPoints; i++) { j = i + 2; InsertTransitPoint (pointSegP + i + 1, pointSegP + i, pointSegP + j, pointSegP [j].nConnSide); } return OptimizePath (pointSegP, numPoints); }
/* ============ FindOptimalPath Returns true if there is a path all the way to the goal. ============ */ bool FindOptimalPath( const pathNode_t *root, const obstacle_t *obstacles, int numObstacles, const float height, const idVec3 &curDir, idVec3 &seekPos ) { int i, numPathPoints, bestNumPathPoints; const pathNode_t *node, *lastNode, *bestNode; idVec2 optimizedPath[MAX_OBSTACLE_PATH]; float pathLength, bestPathLength; bool pathToGoalExists, optimizedPathCalculated; seekPos.Zero(); seekPos.z = height; pathToGoalExists = false; optimizedPathCalculated = false; bestNode = root; bestNumPathPoints = 0; bestPathLength = idMath::INFINITY; node = root; while( node ) { pathToGoalExists |= ( node->dist < 0.1f ); if ( node->dist <= bestNode->dist ) { if ( idMath::Fabs( node->dist - bestNode->dist ) < 0.1f ) { if ( !optimizedPathCalculated ) { bestNumPathPoints = OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath ); bestPathLength = PathLength( optimizedPath, bestNumPathPoints, curDir.ToVec2() ); seekPos.ToVec2() = optimizedPath[1]; } numPathPoints = OptimizePath( root, node, obstacles, numObstacles, optimizedPath ); pathLength = PathLength( optimizedPath, numPathPoints, curDir.ToVec2() ); if ( pathLength < bestPathLength ) { bestNode = node; bestNumPathPoints = numPathPoints; bestPathLength = pathLength; seekPos.ToVec2() = optimizedPath[1]; } optimizedPathCalculated = true; } else { bestNode = node; optimizedPathCalculated = false; } } if ( node->children[0] ) { node = node->children[0]; } else if ( node->children[1] ) { node = node->children[1]; } else { for ( lastNode = node, node = node->parent; node; lastNode = node, node = node->parent ) { if ( node->children[1] && node->children[1] != lastNode ) { node = node->children[1]; break; } } } } if ( !pathToGoalExists ) { seekPos.ToVec2() = root->children[0]->pos; } else if ( !optimizedPathCalculated ) { OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath ); seekPos.ToVec2() = optimizedPath[1]; } if ( ai_showObstacleAvoidance.GetBool() ) { idVec3 start, end; start.z = end.z = height + 4.0f; numPathPoints = OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath ); for ( i = 0; i < numPathPoints-1; i++ ) { start.ToVec2() = optimizedPath[i]; end.ToVec2() = optimizedPath[i+1]; gameRenderWorld->DebugArrow( colorCyan, start, end, 1 ); } } return pathToGoalExists; }
// ----------------------------------------------------------------------------------------------------------- // Create a path from objP->info.position.vPos to the center of nEndSeg. // Return a list of (segment_num, point_locations) at pointSegP // Return number of points in *numPoints. // if nMaxDepth == -1, then there is no maximum depth. // If unable to create path, return -1, else return 0. // If randomFlag !0, then introduce randomness into path by looking at sides in random order. This means // that a path between two segments won't always be the same, unless it is unique.p. // If bSafeMode is set, then additional points are added to "make sure" that points are reachable.p. I would // like to say that it ensures that the CObject can move between the points, but that would require knowing what // the CObject is (which isn't passed, right?) and making fvi calls (slow, right?). So, consider it the more_or_less_safeFlag. // If nEndSeg == -2, then end seg will never be found and this routine will drop out due to depth (xProbably called by CreateNSegmentPath). int CreatePathPoints (CObject *objP, int nStartSeg, int nEndSeg, tPointSeg *pointSegP, short *numPoints, int nMaxDepth, int bRandom, int bSafeMode, int nAvoidSeg) { short nCurSeg; short nSide, hSide; int qTail = 0, qHead = 0; int h, i, j; sbyte bVisited [MAX_SEGMENTS_D2X]; segQueueEntry segmentQ [MAX_SEGMENTS_D2X]; short depth [MAX_SEGMENTS_D2X]; int nCurDepth; sbyte randomXlate [MAX_SIDES_PER_SEGMENT]; tPointSeg *origPointSegs = pointSegP; int lNumPoints; CSegment *segP; CFixVector vCenter; int nParentSeg, nDestSeg; tFVIQuery fq; tFVIData hitData; int hitType; int bAvoidPlayer; #if PATH_VALIDATION ValidateAllPaths (); #endif if ((objP->info.nType == OBJ_ROBOT) && (objP->cType.aiInfo.behavior == AIB_RUN_FROM) && (nAvoidSeg != -32767)) { bRandom = 1; nAvoidSeg = gameData.objs.consoleP->info.nSegment; } bAvoidPlayer = gameData.objs.consoleP->info.nSegment == nAvoidSeg; if (nMaxDepth == -1) nMaxDepth = MAX_PATH_LENGTH; lNumPoints = 0; memset (bVisited, 0, sizeof (bVisited [0]) * gameData.segs.nSegments); memset (depth, 0, sizeof (depth [0]) * gameData.segs.nSegments); // If there is a CSegment we're not allowed to visit, mark it. if (nAvoidSeg != -1) { Assert (nAvoidSeg <= gameData.segs.nLastSegment); if ((nStartSeg != nAvoidSeg) && (nEndSeg != nAvoidSeg)) { bVisited [nAvoidSeg] = 1; depth [nAvoidSeg] = 0; } } nCurSeg = nStartSeg; bVisited [nCurSeg] = 1; nCurDepth = 0; #if DBG if (objP->Index () == nDbgObj) nDbgObj = nDbgObj; #endif if (bRandom) CreateRandomXlate (randomXlate); nCurSeg = nStartSeg; bVisited [nCurSeg] = 1; while (nCurSeg != nEndSeg) { segP = SEGMENTS + nCurSeg; if (bRandom && (d_rand () < 8192)) //create a different xlate at random time intervals CreateRandomXlate (randomXlate); for (nSide = 0; nSide < MAX_SIDES_PER_SEGMENT; nSide++) { hSide = bRandom ? randomXlate [nSide] : nSide; if (!IS_CHILD (segP->m_children [hSide])) continue; if (!((segP->IsDoorWay (hSide, NULL) & WID_FLY_FLAG) || (AIDoorIsOpenable (objP, segP, hSide)))) continue; nDestSeg = segP->m_children [hSide]; if (bVisited [nDestSeg]) continue; if (bAvoidPlayer && ((nCurSeg == nAvoidSeg) || (nDestSeg == nAvoidSeg))) { vCenter = segP->SideCenter (hSide); fq.p0 = &objP->info.position.vPos; fq.startSeg = objP->info.nSegment; fq.p1 = &vCenter; fq.radP0 = fq.radP1 = objP->info.xSize; fq.thisObjNum = objP->Index (); fq.ignoreObjList = NULL; fq.flags = 0; fq.bCheckVisibility = false; hitType = FindVectorIntersection (&fq, &hitData); if (hitType != HIT_NONE) continue; } if (nDestSeg < 0) continue; if (nCurSeg < 0) continue; segmentQ [qTail].start = nCurSeg; segmentQ [qTail].end = nDestSeg; segmentQ [qTail].nConnSide = (ubyte) hSide; bVisited [nDestSeg] = 1; depth [qTail++] = nCurDepth + 1; if (depth [qTail-1] == nMaxDepth) { nEndSeg = segmentQ [qTail-1].end; goto pathTooLong; } // end if (depth [... } // for (nSide.p... if (qHead >= qTail) { // Couldn't get to goal, return a path as far as we got, which is probably acceptable to the unparticular caller. nEndSeg = segmentQ [qTail-1].end; break; } nCurSeg = segmentQ [qHead].end; nCurDepth = depth [qHead]; qHead++; pathTooLong: ; } // while (nCurSeg ... // Set qTail to the CSegment which ends at the goal. while (segmentQ [--qTail].end != nEndSeg) if (qTail < 0) { *numPoints = lNumPoints; return -1; } for (i = qTail; i >= 0; ) { nParentSeg = segmentQ [i].start; lNumPoints++; if (nParentSeg == nStartSeg) break; while (segmentQ [--i].end != nParentSeg) Assert (i >= 0); } if (bSafeMode && ((pointSegP - gameData.ai.routeSegs) + 2 * lNumPoints + 1 >= LEVEL_POINT_SEGS)) { // Ouch! Cannot insert center points in path. So return unsafe path. #if TRACE console.printf (CON_DBG, "Resetting all paths because of bSafeMode.p.\n"); #endif AIResetAllPaths (); *numPoints = lNumPoints; return -1; } pointSegP->nSegment = nStartSeg; pointSegP->point = SEGMENTS [nStartSeg].Center (); if (bSafeMode) lNumPoints *= 2; j = lNumPoints++; h = bSafeMode + 1; for (i = qTail; i >= 0; j -= h) { nDestSeg = segmentQ [i].end; nParentSeg = segmentQ [i].start; pointSegP [j].nSegment = nDestSeg; pointSegP [j].point = SEGMENTS [nDestSeg].Center (); pointSegP [j].nConnSide = segmentQ [i].nConnSide; if (nParentSeg == nStartSeg) break; while (segmentQ [--i].end != nParentSeg) Assert (qTail >= 0); } if (bSafeMode) { for (i = 0; i < lNumPoints - 1; i = j) { j = i + 2; InsertTransitPoint (pointSegP + i + 1, pointSegP + i, pointSegP + j, pointSegP [j].nConnSide); } lNumPoints = OptimizePath (pointSegP, lNumPoints); } pointSegP += lNumPoints; #if PATH_VALIDATION ValidatePath (2, origPointSegs, lNumPoints); #endif #if PATH_VALIDATION ValidatePath (3, origPointSegs, lNumPoints); #endif // -- MK, 10/30/95 -- This code causes apparent discontinuities in the path, moving a point // into a new CSegment. It is not necessarily bad, but it makes it hard to track down actual // discontinuity xProblems. if ((objP->info.nType == OBJ_ROBOT) && ROBOTINFO (objP->info.nId).companion) MoveTowardsOutside (origPointSegs, &lNumPoints, objP, 0); #if PATH_VALIDATION ValidatePath (4, origPointSegs, lNumPoints); #endif *numPoints = lNumPoints; return 0; }