#include "common.h"
#include "General.h"
#include "FileMgr.h" // only needed for empty function
#include "Camera.h"
#include "Vehicle.h"
#include "World.h"
#include "Lines.h" // for debug
#include "PathFind.h"
bool gbShowPedPaths;
bool gbShowCarPaths;
bool gbShowCarPathsLinks;
CPathFind ThePaths;
#define MAX_DIST INT16_MAX-1
#define MIN_PED_ROUTE_DISTANCE 23.8f
#define NUMTEMPNODES 5000
#define NUMDETACHED_CARS 1024
#define NUMDETACHED_PEDS 1214
#define NUMTEMPEXTERNALNODES 4600
CPathInfoForObject *InfoForTileCars;
CPathInfoForObject *InfoForTilePeds;
CPathInfoForObject *DetachedInfoForTileCars;
CPathInfoForObject *DetachedInfoForTilePeds;
CTempNodeExternal *TempExternalNodes;
int32 NumTempExternalNodes;
int32 NumDetachedPedNodeGroups;
int32 NumDetachedCarNodeGroups;
bool
CPedPath::CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints)
{
*pointsFound = 0;
CVector vecDistance = destination - position;
if (Abs(vecDistance.x) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.y) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.z) > MIN_PED_ROUTE_DISTANCE)
return false;
CVector vecPos = (position + destination) * 0.5f;
CVector vecSectorStartPos (vecPos.x - 14.0f, vecPos.y - 14.0f, vecPos.z);
CVector2D vecSectorEndPos (vecPos.x + 28.0f, vecPos.x + 28.0f);
const int16 nodeStartX = (position.x - vecSectorStartPos.x) / 0.7f;
const int16 nodeStartY = (position.y - vecSectorStartPos.y) / 0.7f;
const int16 nodeEndX = (destination.x - vecSectorStartPos.x) / 0.7f;
const int16 nodeEndY = (destination.y - vecSectorStartPos.y) / 0.7f;
if (nodeStartX == nodeEndX && nodeStartY == nodeEndY)
return false;
CPedPathNode pathNodes[40][40];
CPedPathNode pathNodesList[416];
for (int32 x = 0; x < 40; x++) {
for (int32 y = 0; y < 40; y++) {
pathNodes[x][y].bBlockade = false;
pathNodes[x][y].id = INT16_MAX;
pathNodes[x][y].nodeIdX = x;
pathNodes[x][y].nodeIdY = y;
}
}
CWorld::AdvanceCurrentScanCode();
if (pathType != ROUTE_NO_BLOCKADE) {
const int32 nStartX = Max(CWorld::GetSectorIndexX(vecSectorStartPos.x), 0);
const int32 nStartY = Max(CWorld::GetSectorIndexY(vecSectorStartPos.y), 0);
const int32 nEndX = Min(CWorld::GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1);
const int32 nEndY = Min(CWorld::GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1);
for (int32 y = nStartY; y <= nEndY; y++) {
for (int32 x = nStartX; x <= nEndX; x++) {
CSector *pSector = CWorld::GetSector(x, y);
AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], pathNodes, &vecSectorStartPos);
AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pathNodes, &vecSectorStartPos);
AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], pathNodes, &vecSectorStartPos);
AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], pathNodes, &vecSectorStartPos);
}
}
}
for (int32 i = 0; i < 416; i++) {
pathNodesList[i].prev = nil;
pathNodesList[i].next = nil;
}
CPedPathNode *pStartPathNode = &pathNodes[nodeStartX][nodeStartY];
CPedPathNode *pEndPathNode = &pathNodes[nodeEndX][nodeEndY];
pEndPathNode->bBlockade = false;
pEndPathNode->id = 0;
pEndPathNode->prev = nil;
pEndPathNode->next = pathNodesList;
pathNodesList[0].prev = pEndPathNode;
int32 pathNodeIndex = 0;
CPedPathNode *pPreviousNode = nil;
for (; pathNodeIndex < 414; pathNodeIndex++)
{
pPreviousNode = pathNodesList[pathNodeIndex].prev;
while (pPreviousNode && pPreviousNode != pStartPathNode) {
const uint8 nodeIdX = pPreviousNode->nodeIdX;
const uint8 nodeIdY = pPreviousNode->nodeIdY;
if (nodeIdX > 0) {
AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY], pathNodeIndex + 5, pathNodesList);
if (nodeIdY > 0)
AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList);
if (nodeIdY < 39)
AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList);
}
if (nodeIdX < 39) {
AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY], pathNodeIndex + 5, pathNodesList);
if (nodeIdY > 0)
AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList);
if (nodeIdY < 39)
AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList);
}
if (nodeIdY > 0)
AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY - 1], pathNodeIndex + 5, pathNodesList);
if (nodeIdY < 39)
AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY + 1], pathNodeIndex + 5, pathNodesList);
pPreviousNode = pPreviousNode->prev;
if (!pPreviousNode)
break;
}
if (pPreviousNode && pPreviousNode == pStartPathNode)
break;
}
if (pathNodeIndex == 414)
return false;
CPedPathNode *pPathNode = pStartPathNode;
for (*pointsFound = 0; pPathNode != pEndPathNode && *pointsFound < maxPoints; ++ *pointsFound) {
const uint8 nodeIdX = pPathNode->nodeIdX;
const uint8 nodeIdY = pPathNode->nodeIdY;
if (nodeIdX > 0 && pathNodes[nodeIdX - 1][nodeIdY].id + 5 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX - 1][nodeIdY];
else if (nodeIdX > 39 && pathNodes[nodeIdX + 1][nodeIdY].id + 5 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX + 1][nodeIdY];
else if (nodeIdY > 0 && pathNodes[nodeIdX][nodeIdY - 1].id + 5 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX][nodeIdY - 1];
else if (nodeIdY > 39 && pathNodes[nodeIdX][nodeIdY + 1].id + 5 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX][nodeIdY + 1];
else if (nodeIdX > 0 && nodeIdY > 0 && pathNodes[nodeIdX - 1][nodeIdY - 1].id + 7 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX - 1][nodeIdY - 1];
else if (nodeIdX > 0 && nodeIdY < 39 && pathNodes[nodeIdX - 1][nodeIdY + 1].id + 7 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX - 1][nodeIdY + 1];
else if (nodeIdX < 39 && nodeIdY > 0 && pathNodes[nodeIdX + 1][nodeIdY - 1].id + 7 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX + 1][nodeIdY - 1];
else if (nodeIdX < 39 && nodeIdY < 39 && pathNodes[nodeIdX + 1][nodeIdY + 1].id + 7 == pPathNode->id)
pPathNode = &pathNodes[nodeIdX + 1][nodeIdY + 1];
pointPoses[*pointsFound] = vecSectorStartPos;
pointPoses[*pointsFound].x += pPathNode->nodeIdX * 0.7f;
pointPoses[*pointsFound].y += pPathNode->nodeIdY * 0.7f;
}
return true;
}
void
CPedPath::AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList)
{
if (!pNodeToAdd->bBlockade && id < pNodeToAdd->id) {
if (pNodeToAdd->id != INT16_MAX)
RemoveNodeFromList(pNodeToAdd);
AddNodeToList(pNodeToAdd, id, pNodeList);
}
}
void
CPedPath::RemoveNodeFromList(CPedPathNode *pNode)
{
pNode->next->prev = pNode->prev;
if (pNode->prev)
pNode->prev->next = pNode->next;
}
void
CPedPath::AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList)
{
pNode->prev = pList[index].prev;
pNode->next = &pList[index];
if (pList[index].prev)
pList[index].prev->next = pNode;
pList[index].prev = pNode;
pNode->id = index;
}
void
CPedPath::AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition)
{
CPtrNode* listNode = list.first;
while (listNode) {
CEntity* pEntity = (CEntity*)listNode->item;
if (pEntity->m_scanCode != CWorld::GetCurrentScanCode() && pEntity->bUsesCollision) {
pEntity->m_scanCode = CWorld::GetCurrentScanCode();
AddBlockade(pEntity, pathNodes, pPosition);
}
listNode = listNode->next;
}
}
void
CPedPath::AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition)
{
const CBox& boundingBox = pEntity->GetColModel()->boundingBox;
const float fBoundMaxY = boundingBox.max.y + 0.3f;
const float fBoundMinY = boundingBox.min.y - 0.3f;
const float fBoundMaxX = boundingBox.max.x + 0.3f;
const float fDistanceX = pPosition->x - pEntity->m_matrix.GetPosition().x;
const float fDistanceY = pPosition->y - pEntity->m_matrix.GetPosition().y;
const float fBoundRadius = pEntity->GetBoundRadius();
CVector vecBoundCentre;
pEntity->GetBoundCentre(vecBoundCentre);
if (vecBoundCentre.x + fBoundRadius >= pPosition->x &&
vecBoundCentre.y + fBoundRadius >= pPosition->y &&
vecBoundCentre.x - fBoundRadius <= pPosition->x + 28.0f &&
vecBoundCentre.y - fBoundRadius <= pPosition->y + 28.0f) {
for (int16 x = 0; x < 40; x++) {
const float pointX = x * 0.7f + fDistanceX;
for (int16 y = 0; y < 40; y++) {
if (!pathNodes[x][y].bBlockade) {
const float pointY = y * 0.7f + fDistanceY;
CVector2D point(pointX, pointY);
if (fBoundMaxX > Abs(DotProduct2D(point, pEntity->m_matrix.GetRight()))) {
float fDotProduct = DotProduct2D(point, pEntity->m_matrix.GetForward());
if (fBoundMaxY > fDotProduct && fBoundMinY < fDotProduct)
pathNodes[x][y].bBlockade = true;
}
}
}
}
}
}
// Make sure all externals link TO an internal
void
CPathInfoForObject::SwapConnectionsToBeRightWayRound(void)
{
int e, i;
CPathInfoForObject *tile = this;
for(e = 0; e < 12; e++)
if(tile[e].type == NodeTypeExtern && tile[e].next < 0)
for(i = 0; i < 12; i++)
if(tile[i].type == NodeTypeIntern && tile[i].next == e){
tile[e].next = i;
tile[i].next = -1;
bool tmp = !!tile[e].crossing;
tile[e].crossing = tile[i].crossing;
tile[i].crossing = tmp;
}
}
void
CPathFind::Init(void)
{
int i;
m_numPathNodes = 0;
m_numMapObjects = 0;
m_numConnections = 0;
m_numCarPathLinks = 0;
unk = 0;
NumTempExternalNodes = 0;
for(i = 0; i < NUM_PATHNODES; i++)
m_pathNodes[i].distance = MAX_DIST;
}
void
CPathFind::AllocatePathFindInfoMem(int16 numPathGroups)
{
delete[] InfoForTileCars;
InfoForTileCars = nil;
delete[] InfoForTilePeds;
InfoForTilePeds = nil;
// NB: MIAMI doesn't use numPathGroups here but hardcodes PATHNODESIZE
InfoForTileCars = new CPathInfoForObject[12*PATHNODESIZE];
memset(InfoForTileCars, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject));
InfoForTilePeds = new CPathInfoForObject[12*PATHNODESIZE];
memset(InfoForTilePeds, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject));
delete[] DetachedInfoForTileCars;
DetachedInfoForTileCars = nil;
delete[] DetachedInfoForTilePeds;
DetachedInfoForTilePeds = nil;
DetachedInfoForTileCars = new CPathInfoForObject[12*NUMDETACHED_CARS];
memset(DetachedInfoForTileCars, 0, 12*NUMDETACHED_CARS*sizeof(CPathInfoForObject));
DetachedInfoForTilePeds = new CPathInfoForObject[12*NUMDETACHED_PEDS];
memset(DetachedInfoForTilePeds, 0, 12*NUMDETACHED_PEDS*sizeof(CPathInfoForObject));
delete[] TempExternalNodes;
TempExternalNodes = nil;
TempExternalNodes = new CTempNodeExternal[NUMTEMPEXTERNALNODES];
memset(TempExternalNodes, 0, NUMTEMPEXTERNALNODES*sizeof(CTempNodeExternal));
NumTempExternalNodes = 0;
NumDetachedPedNodeGroups = 0;
NumDetachedCarNodeGroups = 0;
}
void
CPathFind::RegisterMapObject(CTreadable *mapObject)
{
m_mapObjects[m_numMapObjects++] = mapObject;
}
void
CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, bool crossing, uint8 spawnRate)
{
int i;
i = id*12 + node;
InfoForTilePeds[i].type = type;
InfoForTilePeds[i].next = next;
InfoForTilePeds[i].x = x/16.0f;
InfoForTilePeds[i].y = y/16.0f;
InfoForTilePeds[i].z = z/16.0f;
InfoForTilePeds[i].width = 8.0f*Min(width, 15.0f);
InfoForTilePeds[i].numLeftLanes = 0;
InfoForTilePeds[i].numRightLanes = 0;
InfoForTilePeds[i].crossing = crossing;
InfoForTilePeds[i].speedLimit = 0;
InfoForTilePeds[i].roadBlock = false;
InfoForTilePeds[i].disabled = false;
InfoForTilePeds[i].waterPath = false;
InfoForTilePeds[i].onlySmallBoats = false;
InfoForTilePeds[i].betweenLevels = false;
InfoForTilePeds[i].spawnRate = Min(spawnRate, 15);
if(node == 11)
InfoForTilePeds[id*12].SwapConnectionsToBeRightWayRound();
}
void
CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, int8 numLeft, int8 numRight,
bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate)
{
int i;
i = id*12 + node;
InfoForTileCars[i].type = type;
InfoForTileCars[i].next = next;
InfoForTileCars[i].x = x/16.0f;
InfoForTileCars[i].y = y/16.0f;
InfoForTileCars[i].z = z/16.0f;
InfoForTileCars[i].width = 8.0f*Min(width, 15.0f);
InfoForTileCars[i].numLeftLanes = numLeft;
InfoForTileCars[i].numRightLanes = numRight;
InfoForTileCars[i].crossing = false;
InfoForTileCars[i].speedLimit = 0;
InfoForTileCars[i].roadBlock = false;
InfoForTileCars[i].disabled = false;
InfoForTileCars[i].waterPath = false;
InfoForTileCars[i].onlySmallBoats = false;
InfoForTileCars[i].betweenLevels = false;
InfoForTileCars[i].spawnRate = Min(spawnRate, 15);
if(node == 11)
InfoForTileCars[id*12].SwapConnectionsToBeRightWayRound();
}
void
CPathFind::StoreDetachedNodeInfoPed(int32 node, int8 type, int32 next, float x, float y, float z, float width, bool crossing,
bool disabled, bool betweenLevels, uint8 spawnRate)
{
int i;
if(NumDetachedPedNodeGroups >= NUMDETACHED_PEDS)
return;
i = NumDetachedPedNodeGroups*12 + node;
DetachedInfoForTilePeds[i].type = type;
DetachedInfoForTilePeds[i].next = next;
DetachedInfoForTilePeds[i].x = x/16.0f;
DetachedInfoForTilePeds[i].y = y/16.0f;
DetachedInfoForTilePeds[i].z = z/16.0f;
DetachedInfoForTilePeds[i].width = 8.0f*Min(width, 31.0f);
DetachedInfoForTilePeds[i].numLeftLanes = 0;
DetachedInfoForTilePeds[i].numRightLanes = 0;
DetachedInfoForTilePeds[i].crossing = crossing;
DetachedInfoForTilePeds[i].speedLimit = 0;
DetachedInfoForTilePeds[i].roadBlock = false;
DetachedInfoForTilePeds[i].disabled = disabled;
DetachedInfoForTilePeds[i].waterPath = false;
DetachedInfoForTilePeds[i].onlySmallBoats = false;
DetachedInfoForTilePeds[i].betweenLevels = betweenLevels;
DetachedInfoForTilePeds[i].spawnRate = Min(spawnRate, 15);
if(node == 11){
DetachedInfoForTilePeds[NumDetachedPedNodeGroups*12].SwapConnectionsToBeRightWayRound();
NumDetachedPedNodeGroups++;
}
}
void
CPathFind::StoreDetachedNodeInfoCar(int32 node, int8 type, int32 next, float x, float y, float z, float width, int8 numLeft, int8 numRight,
bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate, bool onlySmallBoats)
{
int i;
if(NumDetachedCarNodeGroups >= NUMDETACHED_CARS)
return;
i = NumDetachedCarNodeGroups*12 + node;
DetachedInfoForTileCars[i].type = type;
DetachedInfoForTileCars[i].next = next;
DetachedInfoForTileCars[i].x = x/16.0f;
DetachedInfoForTileCars[i].y = y/16.0f;
DetachedInfoForTileCars[i].z = z/16.0f;
DetachedInfoForTileCars[i].width = 8.0f*Min(width, 15.0f);
DetachedInfoForTileCars[i].numLeftLanes = numLeft;
DetachedInfoForTileCars[i].numRightLanes = numRight;
DetachedInfoForTileCars[i].crossing = false;
DetachedInfoForTileCars[i].speedLimit = speedLimit;
DetachedInfoForTileCars[i].roadBlock = roadBlock;
DetachedInfoForTileCars[i].disabled = disabled;
DetachedInfoForTileCars[i].waterPath = waterPath;
DetachedInfoForTileCars[i].onlySmallBoats = onlySmallBoats;
DetachedInfoForTileCars[i].betweenLevels = betweenLevels;
DetachedInfoForTileCars[i].spawnRate = Min(spawnRate, 15);
if(node == 11){
DetachedInfoForTileCars[NumDetachedCarNodeGroups*12].SwapConnectionsToBeRightWayRound();
NumDetachedCarNodeGroups++;
}
}
void
CPathFind::CalcNodeCoors(float x, float y, float z, int id, CVector *out)
{
CVector pos;
pos.x = x;
pos.y = y;
pos.z = z;
*out = m_mapObjects[id]->GetMatrix() * pos;
}
bool
CPathFind::LoadPathFindData(void)
{
CFileMgr::SetDir("");
return false;
}
void
CPathFind::PreparePathData(void)
{
int i, j;
int numExtern, numIntern;
CTempNode *tempNodes;
printf("PreparePathData\n");
if(!CPathFind::LoadPathFindData() && // empty
InfoForTileCars && InfoForTilePeds &&
DetachedInfoForTileCars && DetachedInfoForTilePeds && TempExternalNodes){
tempNodes = new CTempNode[NUMTEMPNODES];
m_numConnections = 0;
for(i = 0; i < PATHNODESIZE; i++){
numExtern = 0;
numIntern = 0;
for(j = 0; j < 12; j++){
if(InfoForTileCars[i*12 + j].type == NodeTypeExtern)
numExtern++;
if(InfoForTileCars[i*12 + j].type == NodeTypeIntern)
numIntern++;
}
if(numIntern > 1 && numExtern != 2)
printf("ILLEGAL BLOCK. MORE THAN 1 INTERNALS AND NOT 2 EXTERNALS (Modelindex:%d)\n", i);
}
int numExternDetached, numInternDetached;
for(i = 0; i < NUMDETACHED_CARS; i++){
numExternDetached = 0;
numInternDetached = 0;
for(j = 0; j < 12; j++){
if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern)
numExternDetached++;
if(DetachedInfoForTilePeds[i*12 + j].type == NodeTypeIntern)
numInternDetached++;
}
// no diagnostic here
}
for(i = 0; i < PATHNODESIZE; i++)
for(j = 0; j < 12; j++)
if(InfoForTileCars[i*12 + j].type == NodeTypeExtern){
// MIAMI has MI:%d here but no argument for it
if(InfoForTileCars[i*12 + j].numLeftLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(InfoForTileCars[i*12 + j].numRightLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(InfoForTileCars[i*12 + j].numLeftLanes + InfoForTileCars[i*12 + j].numRightLanes <= 0)
printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i);
}
for(i = 0; i < NUMDETACHED_CARS; i++)
for(j = 0; j < 12; j++)
if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern){
// MI:%d here but no argument for it
if(DetachedInfoForTileCars[i*12 + j].numLeftLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(DetachedInfoForTileCars[i*12 + j].numRightLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(DetachedInfoForTileCars[i*12 + j].numLeftLanes + DetachedInfoForTileCars[i*12 + j].numRightLanes <= 0)
printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i);
}
m_numPathNodes = 0;
PreparePathDataForType(PATH_CAR, tempNodes, InfoForTileCars, 1.0f, DetachedInfoForTileCars, NumDetachedCarNodeGroups);
m_numCarPathNodes = m_numPathNodes;
PreparePathDataForType(PATH_PED, tempNodes, InfoForTilePeds, 1.0f, DetachedInfoForTilePeds, NumDetachedPedNodeGroups);
m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes;
delete[] tempNodes;
CountFloodFillGroups(PATH_CAR);
CountFloodFillGroups(PATH_PED);
delete[] InfoForTileCars;
InfoForTileCars = nil;
delete[] InfoForTilePeds;
InfoForTilePeds = nil;
delete[] DetachedInfoForTileCars;
DetachedInfoForTileCars = nil;
delete[] DetachedInfoForTilePeds;
DetachedInfoForTilePeds = nil;
delete[] TempExternalNodes;
TempExternalNodes = nil;
}
printf("Done with PreparePathData\n");
}
/* String together connected nodes in a list by a flood fill algorithm */
void
CPathFind::CountFloodFillGroups(uint8 type)
{
int start, end;
int i, l;
uint16 n;
CPathNode *node, *prev;
switch(type){
case PATH_CAR:
start = 0;
end = m_numCarPathNodes;
break;
case PATH_PED:
start = m_numCarPathNodes;
end = start + m_numPedPathNodes;
break;
}
for(i = start; i < end; i++)
m_pathNodes[i].group = 0;
n = 0;
for(;;){
n++;
if(n > 1500){
for(i = start; m_pathNodes[i].group && i < end; i++);
printf("NumNodes:%d Accounted for:%d\n", end - start, i - start);
}
// Look for unvisited node
for(i = start; m_pathNodes[i].group && i < end; i++);
if(i == end)
break;
node = &m_pathNodes[i];
node->SetNext(nil);
node->group = n;
if(node->numLinks == 0){
if(type == PATH_CAR)
printf("Single car node: %f %f %f\n",
node->GetX(), node->GetY(), node->GetZ());
else
printf("Single ped node: %f %f %f\n",
node->GetX(), node->GetY(), node->GetZ());
}
while(node){
prev = node;
node = node->GetNext();
for(i = 0; i < prev->numLinks; i++){
l = ConnectedNode(prev->firstLink + i);
if(m_pathNodes[l].group == 0){
m_pathNodes[l].group = n;
if(m_pathNodes[l].group == 0)
m_pathNodes[l].group = INT8_MIN;
m_pathNodes[l].SetNext(node);
node = &m_pathNodes[l];
}
}
}
}
m_numGroups[type] = n-1;
printf("GraphType:%d. FloodFill groups:%d\n", type, n);
}
int32 TempListLength;
void
CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float maxdist, CPathInfoForObject *detachednodes, int numDetached)
{
static CVector CoorsXFormed;
int i, j, k;
int l1, l2;
int start;
float posx, posy;
float dx, dy, mag;
float nearestDist;
int nearestId;
int oldNumPathNodes, oldNumLinks;
float dist;
int iseg, jseg;
int done, cont;
int tileStart;
oldNumPathNodes = m_numPathNodes;
oldNumLinks = m_numConnections;
#define OBJECTINDEX(n) (mapObjIndices[(n)])
int16 *mapObjIndices = new int16[NUM_PATHNODES];
NumTempExternalNodes = 0;
// Calculate internal nodes, store them and connect them to defining object
for(i = 0; i < m_numMapObjects; i++){
tileStart = m_numPathNodes;
start = 12 * m_mapObjects[i]->GetModelIndex();
for(j = 0; j < 12; j++){
if(objectpathinfo[start + j].type == NodeTypeIntern){
CalcNodeCoors(
objectpathinfo[start + j].x,
objectpathinfo[start + j].y,
objectpathinfo[start + j].z,
i,
&CoorsXFormed);
m_pathNodes[m_numPathNodes].SetPosition(CoorsXFormed);
OBJECTINDEX(m_numPathNodes) = i;
m_pathNodes[m_numPathNodes].width = objectpathinfo[start + j].width;
m_pathNodes[m_numPathNodes].speedLimit = objectpathinfo[start + j].speedLimit;
m_pathNodes[m_numPathNodes].spawnRate = objectpathinfo[start + j].spawnRate;
m_pathNodes[m_numPathNodes].bUseInRoadBlock = objectpathinfo[start + j].roadBlock;
m_pathNodes[m_numPathNodes].bDisabled = objectpathinfo[start + j].disabled;
m_pathNodes[m_numPathNodes].bWaterPath = objectpathinfo[start + j].waterPath;
m_pathNodes[m_numPathNodes].bOnlySmallBoats = objectpathinfo[start + j].onlySmallBoats;
m_pathNodes[m_numPathNodes].bBetweenLevels = objectpathinfo[start + j].betweenLevels;
m_numPathNodes++;
}
else if(objectpathinfo[start + j].type == NodeTypeExtern){
CalcNodeCoors(
objectpathinfo[start + j].x,
objectpathinfo[start + j].y,
objectpathinfo[start + j].z,
i,
&CoorsXFormed);
TempExternalNodes[NumTempExternalNodes].pos = CoorsXFormed;
assert(objectpathinfo[start + j].next >= 0);
TempExternalNodes[NumTempExternalNodes].next = tileStart + objectpathinfo[start + j].next;
TempExternalNodes[NumTempExternalNodes].numLeftLanes = objectpathinfo[start + j].numLeftLanes;
TempExternalNodes[NumTempExternalNodes].numRightLanes = objectpathinfo[start + j].numRightLanes;
TempExternalNodes[NumTempExternalNodes].width = objectpathinfo[start + j].width;
TempExternalNodes[NumTempExternalNodes].isCross = !!objectpathinfo[start + j].crossing;
NumTempExternalNodes++;
}
}
}
// Same thing for detached nodes
for(i = 0; i < numDetached; i++){
tileStart = m_numPathNodes;
start = 12*i;
for(j = 0; j < 12; j++){
if(detachednodes[start + j].type == NodeTypeIntern){
CVector pos;
pos.x = detachednodes[start + j].x;
pos.y = detachednodes[start + j].y;
pos.z = detachednodes[start + j].z;
m_pathNodes[m_numPathNodes].SetPosition(pos);
mapObjIndices[m_numPathNodes] = -(i+1);
m_pathNodes[m_numPathNodes].width = detachednodes[start + j].width;
m_pathNodes[m_numPathNodes].speedLimit = detachednodes[start + j].speedLimit;
m_pathNodes[m_numPathNodes].spawnRate = detachednodes[start + j].spawnRate;
m_pathNodes[m_numPathNodes].bUseInRoadBlock = detachednodes[start + j].roadBlock;
m_pathNodes[m_numPathNodes].bDisabled = detachednodes[start + j].disabled;
m_pathNodes[m_numPathNodes].bWaterPath = detachednodes[start + j].waterPath;
m_pathNodes[m_numPathNodes].bOnlySmallBoats = detachednodes[start + j].onlySmallBoats;
m_pathNodes[m_numPathNodes].bBetweenLevels = detachednodes[start + j].betweenLevels;
m_numPathNodes++;
}else if(detachednodes[start + j].type == NodeTypeExtern){
TempExternalNodes[NumTempExternalNodes].pos.x = detachednodes[start + j].x;
TempExternalNodes[NumTempExternalNodes].pos.y = detachednodes[start + j].y;
TempExternalNodes[NumTempExternalNodes].pos.z = detachednodes[start + j].z;
assert(detachednodes[start + j].next >= 0);
TempExternalNodes[NumTempExternalNodes].next = tileStart + detachednodes[start + j].next;
TempExternalNodes[NumTempExternalNodes].numLeftLanes = detachednodes[start + j].numLeftLanes;
TempExternalNodes[NumTempExternalNodes].numRightLanes = detachednodes[start + j].numRightLanes;
TempExternalNodes[NumTempExternalNodes].width = detachednodes[start + j].width;
TempExternalNodes[NumTempExternalNodes].isCross = !!detachednodes[start + j].crossing;
NumTempExternalNodes++;
}
}
}
// Insert external nodes into TempList
TempListLength = 0;
for(i = 0; i < NumTempExternalNodes; i++){
// find closest unconnected node
nearestId = -1;
nearestDist = maxdist;
for(k = 0; k < TempListLength; k++){
if(tempnodes[k].linkState != 1)
continue;
dx = tempnodes[k].pos.x - TempExternalNodes[i].pos.x;
if(Abs(dx) < nearestDist){
dy = tempnodes[k].pos.y - TempExternalNodes[i].pos.y;
if(Abs(dy) < nearestDist){
nearestDist = Max(Abs(dx), Abs(dy));
nearestId = k;
}
}
}
if(nearestId < 0){
// None found, add this one to temp list
tempnodes[TempListLength].pos = TempExternalNodes[i].pos;
// link to connecting internal node
tempnodes[TempListLength].link1 = TempExternalNodes[i].next;
if(type == PATH_CAR){
tempnodes[TempListLength].numLeftLanes = TempExternalNodes[i].numLeftLanes;
tempnodes[TempListLength].numRightLanes = TempExternalNodes[i].numRightLanes;
}
tempnodes[TempListLength].width = TempExternalNodes[i].width;
tempnodes[TempListLength].isCross = TempExternalNodes[i].isCross;
tempnodes[TempListLength++].linkState = 1;
}else{
// Found nearest, connect it to our neighbour
tempnodes[nearestId].link2 = TempExternalNodes[i].next;
tempnodes[nearestId].linkState = 2;
// collapse this node with nearest we found
dx = m_pathNodes[tempnodes[nearestId].link1].GetX() - m_pathNodes[tempnodes[nearestId].link2].GetX();
dy = m_pathNodes[tempnodes[nearestId].link1].GetY() - m_pathNodes[tempnodes[nearestId].link2].GetY();
tempnodes[nearestId].pos = (tempnodes[nearestId].pos + TempExternalNodes[i].pos)*0.5f;
mag = Sqrt(dx*dx + dy*dy);
tempnodes[nearestId].dirX = dx/mag * 100;
tempnodes[nearestId].dirY = dy/mag * 100;
tempnodes[nearestId].width = Max(tempnodes[nearestId].width, TempExternalNodes[i].width);
if(TempExternalNodes[i].isCross)
tempnodes[nearestId].isCross = true; // TODO: is this guaranteed to be false otherwise?
// do something when number of lanes doesn't agree
if(type == PATH_CAR)
if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 &&
(TempExternalNodes[i].numLeftLanes == 0 || TempExternalNodes[i].numRightLanes == 0)){
// why switch left and right here?
tempnodes[nearestId].numLeftLanes = TempExternalNodes[i].numRightLanes;
tempnodes[nearestId].numRightLanes = TempExternalNodes[i].numLeftLanes;
}
}
}
// Loop through previously added internal nodes and link them
for(i = oldNumPathNodes; i < m_numPathNodes; i++){
// Init link
m_pathNodes[i].numLinks = 0;
m_pathNodes[i].firstLink = m_numConnections;
// See if node connects to external nodes
for(j = 0; j < TempListLength; j++){
if(tempnodes[j].linkState != 2)
continue;
// Add link to other side of the external
// NB this clears the flags in MIAMI
if(tempnodes[j].link1 == i)
m_connections[m_numConnections] = tempnodes[j].link2;
else if(tempnodes[j].link2 == i)
m_connections[m_numConnections] = tempnodes[j].link1;
else
continue;
dist = (m_pathNodes[i].GetPosition() - m_pathNodes[ConnectedNode(m_numConnections)].GetPosition()).Magnitude();
m_distances[m_numConnections] = Min(dist, 255);
if(tempnodes[j].isCross)
m_connections[j] |= 0x8000; // crosses road flag
if(type == PATH_CAR){
// IMPROVE: use a goto here
// Find existing car path link
for(k = 0; k < m_numCarPathLinks; k++){
if(m_carPathLinks[k].dirX == tempnodes[j].dirX &&
m_carPathLinks[k].dirY == tempnodes[j].dirY &&
m_carPathLinks[k].x == (int)(tempnodes[j].pos.x*8.0f) &&
m_carPathLinks[k].y == (int)(tempnodes[j].pos.y*8.0f)){
m_carPathConnections[m_numConnections] = k;
k = m_numCarPathLinks;
}
}
// k is m_numCarPathLinks+1 if we found one
if(k == m_numCarPathLinks){
m_carPathLinks[m_numCarPathLinks].dirX = tempnodes[j].dirX;
m_carPathLinks[m_numCarPathLinks].dirY = tempnodes[j].dirY;
m_carPathLinks[m_numCarPathLinks].x = tempnodes[j].pos.x*8.0f;
m_carPathLinks[m_numCarPathLinks].y = tempnodes[j].pos.y*8.0f;
m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false;
m_carPathLinks[m_numCarPathLinks].width = tempnodes[j].width;
m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i;
m_carPathLinks[m_numCarPathLinks].numLeftLanes = tempnodes[j].numLeftLanes;
m_carPathLinks[m_numCarPathLinks].numRightLanes = tempnodes[j].numRightLanes;
m_carPathLinks[m_numCarPathLinks].trafficLightType = 0;
assert(m_numCarPathLinks <= NUM_CARPATHLINKS);
m_carPathConnections[m_numConnections] = m_numCarPathLinks++;
}
}
m_pathNodes[i].numLinks++;
m_numConnections++;
}
CPathInfoForObject *tile;
if(mapObjIndices[i] < 0){
if(type == PATH_CAR)
tile = &DetachedInfoForTileCars[12 * (-1 - mapObjIndices[i])];
else
tile = &DetachedInfoForTilePeds[12 * (-1 - mapObjIndices[i])];
}else{
if(type == PATH_CAR)
tile = &InfoForTileCars[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()];
else
tile = &InfoForTilePeds[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()];
}
// Find i inside path segment
iseg = 0;
for(j = Max(oldNumPathNodes, i-12); j < i; j++)
if(OBJECTINDEX(j) == OBJECTINDEX(i))
iseg++;
// Add links to other internal nodes
for(j = Max(oldNumPathNodes, i-12); j < Min(m_numPathNodes, i+12); j++){
if(OBJECTINDEX(i) != OBJECTINDEX(j) || i == j)
continue;
// N.B.: in every path segment, the externals have to be at the end
jseg = j-i + iseg;
if(tile[iseg].next == jseg ||
tile[jseg].next == iseg){
// Found a link between i and jConnectionSetCrossesRoad
// NB this clears the flags in MIAMI
m_connections[m_numConnections] = j;
dist = (m_pathNodes[i].GetPosition() - m_pathNodes[j].GetPosition()).Magnitude();
m_distances[m_numConnections] = Min(dist, 255);
if(type == PATH_CAR){
posx = (m_pathNodes[i].GetX() + m_pathNodes[j].GetX())*0.5f;
posy = (m_pathNodes[i].GetY() + m_pathNodes[j].GetY())*0.5f;
dx = m_pathNodes[j].GetX() - m_pathNodes[i].GetX();
dy = m_pathNodes[j].GetY() - m_pathNodes[i].GetY();
mag = Sqrt(dx*dx + dy*dy);
dx /= mag;
dy /= mag;
uint8 width = Max(m_pathNodes[i].width, m_pathNodes[j].width);
if(i < j){
dx = -dx;
dy = -dy;
}
// IMPROVE: use a goto here
// Find existing car path link
for(k = 0; k < m_numCarPathLinks; k++){
if(m_carPathLinks[k].dirX == (int)(dx*100.0f) &&
m_carPathLinks[k].dirY == (int)(dy*100.0f) &&
m_carPathLinks[k].x == (int)(posx*8.0f) &&
m_carPathLinks[k].y == (int)(posy*8.0f)){
m_carPathConnections[m_numConnections] = k;
k = m_numCarPathLinks;
}
}
// k is m_numCarPathLinks+1 if we found one
if(k == m_numCarPathLinks){
m_carPathLinks[m_numCarPathLinks].dirX = dx*100.0f;
m_carPathLinks[m_numCarPathLinks].dirY = dy*100.0f;
m_carPathLinks[m_numCarPathLinks].x = posx*8.0f;
m_carPathLinks[m_numCarPathLinks].y = posy*8.0f;
m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false;
m_carPathLinks[m_numCarPathLinks].width = width;
m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i;
m_carPathLinks[m_numCarPathLinks].numLeftLanes = -1;
m_carPathLinks[m_numCarPathLinks].numRightLanes = -1;
m_carPathLinks[m_numCarPathLinks].trafficLightType = 0;
assert(m_numCarPathLinks <= NUM_CARPATHLINKS);
m_carPathConnections[m_numConnections] = m_numCarPathLinks++;
}
}else{
// Crosses road
if(tile[iseg].next == jseg && tile[iseg].crossing ||
tile[jseg].next == iseg && tile[jseg].crossing)
m_connections[m_numConnections] |= 0x8000; // crosses road flag
}
m_pathNodes[i].numLinks++;
m_numConnections++;
}
}
}
if(type == PATH_CAR){
done = 0;
// Set number of lanes for all nodes somehow
// very strange code
for(k = 0; !done && k < 12; k++){
done = 1;
for(i = 0; i < m_numPathNodes; i++){
if(m_pathNodes[i].numLinks != 2)
continue;
l1 = m_carPathConnections[m_pathNodes[i].firstLink];
l2 = m_carPathConnections[m_pathNodes[i].firstLink+1];
int8 l1Left = m_carPathLinks[l1].numLeftLanes;
int8 l1Right = m_carPathLinks[l1].numRightLanes;
int8 l2Left = m_carPathLinks[l2].numLeftLanes;
int8 l2Right = m_carPathLinks[l2].numRightLanes;
int8 *l1Leftp, *l1Rightp;
int8 *l2Leftp, *l2Rightp;
if(m_carPathLinks[l1].pathNodeIndex == i){
l1Leftp = &l1Left;
l1Rightp = &l1Right;
}else{
l1Leftp = &l1Right;
l1Rightp = &l1Left;
}
if(m_carPathLinks[l2].pathNodeIndex == i){
l2Leftp = &l2Left;
l2Rightp = &l2Right;
}else{
l2Leftp = &l2Right;
l2Rightp = &l2Left;
}
if(*l1Leftp == -1 && *l2Rightp != -1){
*l1Leftp = *l2Rightp;
done = 0;
}
if(*l1Rightp == -1 && *l2Leftp != -1){
*l1Rightp = *l2Leftp;
done = 0;
}
if(*l2Leftp == -1 && *l1Rightp != -1){
*l2Leftp = *l1Rightp;
done = 0;
}
if(*l2Rightp == -1 && *l1Leftp != -1){
*l2Rightp = *l1Leftp;
done = 0;
}
if(*l1Leftp == -1 && *l2Rightp == -1)
done = 0;
if(*l2Leftp == -1 && *l1Rightp == -1)
done = 0;
m_carPathLinks[l1].numLeftLanes = l1Left;
m_carPathLinks[l1].numRightLanes = l1Right;
m_carPathLinks[l2].numLeftLanes = l2Left;
m_carPathLinks[l2].numRightLanes = l2Right;
}
}
// Fall back to default values for number of lanes
for(i = 0; i < m_numPathNodes; i++)
for(j = 0; j < m_pathNodes[i].numLinks; j++){
k = m_carPathConnections[m_pathNodes[i].firstLink + j];
if(m_carPathLinks[k].numLeftLanes == -1)
m_carPathLinks[k].numLeftLanes = 0;
if(m_carPathLinks[k].numRightLanes == -1)
m_carPathLinks[k].numRightLanes = 0;
}
}
// Set flags for car nodes
if(type == PATH_CAR){
do{
cont = 0;
for(i = 0; i < m_numPathNodes; i++){
// See if node is a dead end, if so, we're not done yet
if(!m_pathNodes[i].bDeadEnd){
k = 0;
for(j = 0; j < m_pathNodes[i].numLinks; j++)
if(!m_pathNodes[ConnectedNode(m_pathNodes[i].firstLink + j)].bDeadEnd)
k++;
if(k < 2){
m_pathNodes[i].bDeadEnd = true;
cont = 1;
}
}
}
}while(cont);
}
// Remove isolated ped nodes
if(type == PATH_PED)
for(i = oldNumPathNodes; i < m_numPathNodes; i++){
if(m_pathNodes[i].numLinks != 0)
continue;
// Remove node
for(j = i; j < m_numPathNodes-1; j++)
m_pathNodes[j] = m_pathNodes[j+1];
// Fix links
for(j = oldNumLinks; j < m_numConnections; j++){
int node = ConnectedNode(j);
if(node >= i)
m_connections[j] = node-1;
}
i--;
m_numPathNodes--;
}
delete[] mapObjIndices;
}
float
CPathFind::CalcRoadDensity(float x, float y)
{
int i, j;
float density = 0.0f;
for(i = 0; i < m_numCarPathNodes; i++){
if(Abs(m_pathNodes[i].GetX() - x) < 80.0f &&
Abs(m_pathNodes[i].GetY() - y) < 80.0f &&
m_pathNodes[i].numLinks > 0){
for(j = 0; j < m_pathNodes[i].numLinks; j++){
int next = ConnectedNode(m_pathNodes[i].firstLink + j);
float dist = (m_pathNodes[i].GetPosition() - m_pathNodes[next].GetPosition()).Magnitude2D();
next = m_carPathConnections[m_pathNodes[i].firstLink + j];
density += m_carPathLinks[next].numLeftLanes * dist;
density += m_carPathLinks[next].numRightLanes * dist;
}
}
}
return density/2500.0f;
}
bool
CPathFind::TestForPedTrafficLight(CPathNode *n1, CPathNode *n2)
{
int i;
for(i = 0; i < n1->numLinks; i++)
if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2)
return ConnectionHasTrafficLight(n1->firstLink + i);
return false;
}
bool
CPathFind::TestCrossesRoad(CPathNode *n1, CPathNode *n2)
{
int i;
for(i = 0; i < n1->numLinks; i++)
if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2)
return ConnectionCrossesRoad(n1->firstLink + i);
return false;
}
void
CPathFind::AddNodeToList(CPathNode *node, int32 listId)
{
int i = listId & 0x1FF;
node->SetNext(m_searchNodes[i].GetNext());
node->SetPrev(&m_searchNodes[i]);
if(m_searchNodes[i].GetNext())
m_searchNodes[i].GetNext()->SetPrev(node);
m_searchNodes[i].SetNext(node);
node->distance = listId;
}
void
CPathFind::RemoveNodeFromList(CPathNode *node)
{
node->GetPrev()->SetNext(node->GetNext());
if(node->GetNext())
node->GetNext()->SetPrev(node->GetPrev());
}
void
CPathFind::RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n)
{
int i;
if(*n < 2)
return;
if(DotProduct2D(nodes[1]->GetPosition() - pos, nodes[0]->GetPosition() - pos) < 0.0f){
(*n)--;
for(i = 0; i < *n; i++)
nodes[i] = nodes[i+1];
}
}
#ifdef GTA_BRIDGE
void
CPathFind::SetLinksBridgeLights(float x1, float x2, float y1, float y2, bool enable)
{
int i;
for(i = 0; i < m_numCarPathLinks; i++){
CVector2D pos = m_carPathLinks[i].GetPosition();
if(x1 < pos.x && pos.x < x2 &&
y1 < pos.y && pos.y < y2)
m_carPathLinks[i].bBridgeLights = enable;
}
}
#endif
void
CPathFind::SwitchOffNodeAndNeighbours(int32 nodeId, bool disable)
{
int i, next;
m_pathNodes[nodeId].bDisabled = disable;
if(m_pathNodes[nodeId].numLinks < 3)
for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
next = ConnectedNode(m_pathNodes[nodeId].firstLink + i);
if(m_pathNodes[next].bDisabled != disable &&
m_pathNodes[next].numLinks < 3)
SwitchOffNodeAndNeighbours(next, disable);
}
}
void
CPathFind::SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable)
{
int i;
for(i = 0; i < m_numCarPathNodes; i++){
CVector pos = m_pathNodes[i].GetPosition();
if(x1 <= pos.x && pos.x <= x2 &&
y1 <= pos.y && pos.y <= y2 &&
z1 <= pos.z && pos.z <= z2 &&
disable != m_pathNodes[i].bDisabled)
SwitchOffNodeAndNeighbours(i, disable);
}
}
void
CPathFind::SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable)
{
int i;
for(i = m_numCarPathNodes; i < m_numPathNodes; i++){
CVector pos = m_pathNodes[i].GetPosition();
if(x1 <= pos.x && pos.x <= x2 &&
y1 <= pos.y && pos.y <= y2 &&
z1 <= pos.z && pos.z <= z2 &&
disable != m_pathNodes[i].bDisabled)
SwitchOffNodeAndNeighbours(i, disable);
}
}
void
CPathFind::SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 mode)
{
int i;
int firstNode, lastNode;
// this is NOT PATH_CAR
if(type != 0){
firstNode = 0;
lastNode = m_numCarPathNodes;
}else{
firstNode = m_numCarPathNodes;
lastNode = m_numPathNodes;
}
if(z1 > z2){
float tmp = z2;
z2 = z1;
z1 = tmp;
}
// angle of vector from p2 to p1
float angle = CGeneral::GetRadianAngleBetweenPoints(x1, y1, x2, y2) + HALFPI;
while(angle < 0.0f) angle += TWOPI;
while(angle > TWOPI) angle -= TWOPI;
// vector from p1 to p2
CVector2D v12(x2 - x1, y2 - y1);
float len12 = v12.Magnitude();
v12 /= len12;
// vector from p2 to new point p3
CVector2D v23(Sin(angle)*length, -(Cos(angle)*length));
v23 /= v23.Magnitude(); // obivously just 'length' but whatever
bool disable = mode == SWITCH_OFF;
for(i = firstNode; i < lastNode; i++){
CVector pos = m_pathNodes[i].GetPosition();
if(pos.z < z1 || pos.z > z2)
continue;
CVector2D d(pos.x - x1, pos.y - y1);
float dot = DotProduct2D(d, v12);
if(dot < 0.0f || dot > len12)
continue;
dot = DotProduct2D(d, v23);
if(dot < 0.0f || dot > length)
continue;
if(m_pathNodes[i].bDisabled != disable)
SwitchOffNodeAndNeighbours(i, disable);
}
}
void
CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId)
{
int i, next;
m_pathNodes[nodeId].bBetweenLevels = true;
if(m_pathNodes[nodeId].numLinks < 3)
for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
next = ConnectedNode(m_pathNodes[nodeId].firstLink + i);
if(!m_pathNodes[next].bBetweenLevels &&
m_pathNodes[next].numLinks < 3)
MarkRoadsBetweenLevelsNodeAndNeighbours(next);
}
}
void
CPathFind::MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2)
{
int i;
for(i = 0; i < m_numPathNodes; i++){
CVector pos = m_pathNodes[i].GetPosition();
if(x1 < pos.x && pos.x < x2 &&
y1 < pos.y && pos.y < y2 &&
z1 < pos.z && pos.z < z2)
MarkRoadsBetweenLevelsNodeAndNeighbours(i);
}
}
void
CPathFind::PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2)
{
int i;
for(i = m_numCarPathNodes; i < m_numPathNodes; i++){
CVector pos = m_pathNodes[i].GetPosition();
if(x1 < pos.x && pos.x < x2 &&
y1 < pos.y && pos.y < y2 &&
z1 < pos.z && pos.z < z2)
MarkRoadsBetweenLevelsNodeAndNeighbours(i);
}
}
int32
CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, bool ignoreSelected, bool bWaterPath)
{
int i;
int firstNode, lastNode;
float dist;
float closestDist = 10000.0f;
int closestNode = 0;
switch(type){
case PATH_CAR:
firstNode = 0;
lastNode = m_numCarPathNodes;
break;
case PATH_PED:
firstNode = m_numCarPathNodes;
lastNode = m_numPathNodes;
break;
}
for(i = firstNode; i < lastNode; i++){
if(ignoreDisabled && m_pathNodes[i].bDisabled) continue;
if(ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue;
if(ignoreSelected && m_pathNodes[i].bSelected) continue;
if(bWaterPath != m_pathNodes[i].bWaterPath) continue;
dist = Abs(m_pathNodes[i].GetX() - coors.x) +
Abs(m_pathNodes[i].GetY() - coors.y) +
3.0f*Abs(m_pathNodes[i].GetZ() - coors.z);
if(dist < closestDist){
closestDist = dist;
closestNode = i;
}
}
return closestDist < distLimit ? closestNode : -1;
}
int32
CPathFind::FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY)
{
int i;
int firstNode, lastNode;
float dist, dX, dY;
NormalizeXY(dirX, dirY);
float closestDist = 10000.0f;
int closestNode = 0;
switch(type){
case PATH_CAR:
firstNode = 0;
lastNode = m_numCarPathNodes;
break;
case PATH_PED:
firstNode = m_numCarPathNodes;
lastNode = m_numPathNodes;
break;
}
for(i = firstNode; i < lastNode; i++){
dX = m_pathNodes[i].GetX() - coors.x;
dY = m_pathNodes[i].GetY() - coors.y;
dist = Abs(dX) + Abs(dY) +
3.0f*Abs(m_pathNodes[i].GetZ() - coors.z);
if(dist < closestDist){
NormalizeXY(dX, dY);
dist -= (dX*dirX + dY*dirY - 1.0f)*20.0f;
if(dist < closestDist){
closestDist = dist;
closestNode = i;
}
}
}
return closestNode;
}
void
CPathFind::FindNodePairClosestToCoors(CVector coors, uint8 type, int* node1, int* node2, float* angle, float minDist, float maxDist, bool ignoreDisabled, bool ignoreBetweenLevels, bool bWaterPath)
{
int i, j;
int firstNode, lastNode, connectedNode;
float dist;
float closestDist = 10000.0f;
int closestNode = 0, closestConnectedNode = 0;
switch (type) {
case PATH_CAR:
firstNode = 0;
lastNode = m_numCarPathNodes;
break;
case PATH_PED:
firstNode = m_numCarPathNodes;
lastNode = m_numPathNodes;
break;
}
for (i = firstNode; i < lastNode; i++) {
if (ignoreDisabled && m_pathNodes[i].bDisabled) continue;
if (ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue;
if (bWaterPath != m_pathNodes[i].bWaterPath) continue;
dist = Abs(m_pathNodes[i].GetX() - coors.x) +
Abs(m_pathNodes[i].GetY() - coors.y) +
3.0f * Abs(m_pathNodes[i].GetZ() - coors.z);
if (dist < closestDist) {
for (j = 0; j < m_pathNodes[i].numLinks; j++) {
connectedNode = ConnectedNode(m_pathNodes[i].firstLink + j);
if (ignoreDisabled && m_pathNodes[connectedNode].bDisabled) continue;
if (ignoreBetweenLevels && m_pathNodes[connectedNode].bBetweenLevels) continue;
if (bWaterPath != m_pathNodes[connectedNode].bWaterPath) continue;
if ((m_pathNodes[connectedNode].GetPosition() - m_pathNodes[i].GetPosition()).Magnitude() > minDist) {
closestDist = dist;
closestNode = i;
closestConnectedNode = connectedNode;
}
}
}
}
if (closestDist < maxDist) {
*node1 = closestNode;
*node2 = closestConnectedNode;
CVector dir(m_pathNodes[*node2].GetX() - m_pathNodes[*node1].GetX(), m_pathNodes[*node2].GetY() - m_pathNodes[*node1].GetY(), 0.0f);
dir.Normalise();
*angle = RADTODEG(Atan2(-dir.x, dir.y));
}
else {
*node1 = -1;
*node2 = -1;
*angle = 0.0f;
}
}
int32
CPathFind::FindNthNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, int N, bool bWaterPath)
{
int i;
int firstNode, lastNode;
switch (type) {
case PATH_CAR:
firstNode = 0;
lastNode = m_numCarPathNodes;
break;
case PATH_PED:
firstNode = m_numCarPathNodes;
lastNode = m_numPathNodes;
break;
}
for (i = firstNode; i < lastNode; i++)
m_pathNodes[i].bSelected = false;
for (; N > 0; N--) {
i = FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath);
if (i < 0)
return -1;
m_pathNodes[i].bSelected = true;
}
return FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath);
}
CVector
CPathFind::FindNodeCoorsForScript(int32 id)
{
// the point is to return valid position in case there is a divider in the middle of the road
if (!m_pathNodes[id].HasDivider() || m_pathNodes[id].numLinks == 0)
return m_pathNodes[id].GetPosition();
CVector2D dir(m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetX() - m_pathNodes[id].GetX(),
m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetY() - m_pathNodes[id].GetY());
dir.Normalise();
if (dir.x < 0)
dir = -dir;
return m_pathNodes[id].GetPosition() + CVector(-dir.y, dir.x, 0.0f) * (LANE_WIDTH / 2 + m_pathNodes[id].GetDividerWidth());
}
float
CPathFind::FindNodeOrientationForCarPlacement(int32 nodeId)
{
if(m_pathNodes[nodeId].numLinks == 0)
return 0.0f;
CVector dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink)].GetPosition() - m_pathNodes[nodeId].GetPosition();
dir.z = 0.0f;
dir.Normalise();
return RADTODEG(dir.Heading());
}
float
CPathFind::FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards)
{
int i;
CVector targetDir(x - m_pathNodes[nodeId].GetX(), y - m_pathNodes[nodeId].GetY(), 0.0f);
targetDir.Normalise();
CVector dir;
if(m_pathNodes[nodeId].numLinks == 0)
return 0.0f;
int bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink);
#ifdef FIX_BUGS
float bestDot = towards ? -2.0f : 2.0f;
#else
int bestDot = towards ? -2 : 2; // why int?
#endif
for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink + i)].GetPosition() - m_pathNodes[nodeId].GetPosition();
dir.z = 0.0f;
dir.Normalise();
float angle = DotProduct2D(dir, targetDir);
if(towards){
if(angle > bestDot){
bestDot = angle;
bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i);
}
}else{
if(angle < bestDot){
bestDot = angle;
bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i);
}
}
}
dir = m_pathNodes[bestNode].GetPosition() - m_pathNodes[nodeId].GetPosition();
dir.z = 0.0f;
dir.Normalise();
return RADTODEG(dir.Heading());
}
bool
CPathFind::GenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled)
{
int i, j;
int node1, node2;
float dist1, dist2, d1, d2;
if(m_numCarPathNodes == 0)
return false;
for(i = 0; i < 500; i++){
node1 = (CGeneral::GetRandomNumber()>>3) % m_numCarPathNodes;
if(m_pathNodes[node1].bDisabled && !ignoreDisabled)
continue;
dist1 = Distance2D(m_pathNodes[node1].GetPosition(), x, y);
if(dist1 < Max(spawnDist + 70.0f, spawnDist * 1.7f)){
d1 = m_pathNodes[node1].bWaterPath ? (dist1 - spawnDist * 1.5f) : (dist1 - spawnDist);
for(j = 0; j < m_pathNodes[node1].numLinks; j++){
node2 = ConnectedNode(m_pathNodes[node1].firstLink + j);
if(m_pathNodes[node2].bDisabled && !ignoreDisabled)
continue;
dist2 = Distance2D(m_pathNodes[node2].GetPosition(), x, y);
d2 = m_pathNodes[node2].bWaterPath ? (dist2 - spawnDist * 1.5f) : (dist2 - spawnDist);
if(d1*d2 < 0.0f){
// nodes are on different sides of spawn distance
float f2 = Abs(d1)/(Abs(d1) + Abs(d2));
float f1 = 1.0f - f2;
*pPositionBetweenNodes = f2;
CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2;
CVector2D dist2d(pos.x - x, pos.y - y);
dist2d.Normalise(); // done manually in the game
float dot = DotProduct2D(dist2d, CVector2D(dirX, dirY));
if(forward){
if(dot > angleLimit){
*pNode1 = node1;
*pNode2 = node2;
*pPosition = pos;
return true;
}
}else{
if(dot <= angleLimit){
*pNode1 = node1;
*pNode2 = node2;
*pPosition = pos;
return true;
}
}
}
}
}
}
return false;
}
bool
CPathFind::GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix)
{
int i;
int node1, node2;
float node1_dist, node2_dist;
static int32 node_cnt;
if(m_numPedPathNodes == 0)
return false;
for(i = 0; i < 230; i++){
if (node_cnt++ >= m_numPedPathNodes)
node_cnt = 0;
node1 = node_cnt + m_numCarPathNodes;
node1_dist = Distance2D(m_pathNodes[node1].GetPosition(), x, y);
if(node1_dist < maxDist+30.0f){
if(m_pathNodes[node1].numLinks != 0)
break;
}
}
if (i >= 230)
return false;
for(i = 0; i < m_pathNodes[node1].numLinks; i++){
int link = m_pathNodes[node1].firstLink + i;
if(ConnectionCrossesRoad(link))
continue;
node2 = ConnectedNode(link);
if(m_pathNodes[node1].bDisabled || m_pathNodes[node2].bDisabled)
continue;
node2_dist = Distance2D(m_pathNodes[node2].GetPosition(), x, y);
if ((node1_dist < maxDist || node2_dist < maxDist) && (node1_dist > minDistOffScreen || node2_dist > minDistOffScreen))
break;
}
if(i >= m_pathNodes[node1].numLinks)
return false;
for(i = 0; i < 5; i++){
float f2 = (CGeneral::GetRandomNumber()&0xFF)/256.0f;
float f1 = 1.0f - f2;
*pPositionBetweenNodes = f2;
CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2;
if(Distance2D(pos, x, y) < maxDist+20.0f){
pos.x += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f;
pos.y += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f;
float dist = Distance2D(pos, x, y);
bool visible;
if(camMatrix)
visible = TheCamera.IsSphereVisible(pos, 2.0f, camMatrix);
else
visible = TheCamera.IsSphereVisible(pos, 2.0f);
if(!visible){
minDist = minDistOffScreen;
maxDist = maxDistOffScreen;
}
if(visible && (minDist < dist && dist < maxDist) ||
!visible && (minDistOffScreen < dist && dist < maxDistOffScreen)){
*pNode1 = node1;
*pNode2 = node2;
*pPosition = pos;
bool found;
float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z+2.0f, &found);
if(!found)
return false;
if(Abs(groundZ - pos.z) > 3.0f)
return false;
pPosition->z = groundZ;
return true;
}
}
}
return false;
}
void
CPathFind::FindNextNodeWandering(uint8 type, CVector coors, CPathNode **lastNode, CPathNode **nextNode, uint8 curDir, uint8 *nextDir)
{
int i;
CPathNode *node;
if(lastNode == nil || (node = *lastNode) == nil || (coors - (*lastNode)->GetPosition()).MagnitudeSqr() > 7.0f){
int32 nodeIdx = FindNodeClosestToCoors(coors, type, 999999.88f);
node = &m_pathNodes[nodeIdx];
}
CVector2D vCurDir(Sin(curDir*PI/4.0f), Cos(curDir * PI / 4.0f));
*nextNode = 0;
float bestDot = -999999.0f;
for(i = 0; i < node->numLinks; i++){
int next = ConnectedNode(node->firstLink+i);
if(!node->bDisabled && m_pathNodes[next].bDisabled)
continue;
CVector pedCoors = coors;
pedCoors.z += 1.0f;
CVector nodeCoors = m_pathNodes[next].GetPosition();
nodeCoors.z += 1.0f;
if(!CWorld::GetIsLineOfSightClear(pedCoors, nodeCoors, true, false, false, false, false, false))
continue;
CVector2D nodeDir = m_pathNodes[next].GetPosition() - node->GetPosition();
nodeDir.Normalise();
float dot = DotProduct2D(nodeDir, vCurDir);
if(dot >= bestDot){
*nextNode = &m_pathNodes[next];
bestDot = dot;
// direction is 0, 2, 4, 6 for north, east, south, west
// this could be done simpler...
if(nodeDir.x < 0.0f){
if(2.0f*Abs(nodeDir.y) < -nodeDir.x)
*nextDir = 6; // west
else if(-2.0f*nodeDir.x < nodeDir.y)
*nextDir = 0; // north
else if(2.0f*nodeDir.x > nodeDir.y)
*nextDir = 4; // south
else if(nodeDir.y > 0.0f)
*nextDir = 7; // north west
else
*nextDir = 5; // south west`
}else{
if(2.0f*Abs(nodeDir.y) < nodeDir.x)
*nextDir = 2; // east
else if(2.0f*nodeDir.x < nodeDir.y)
*nextDir = 0; // north
else if(-2.0f*nodeDir.x > nodeDir.y)
*nextDir = 4; // south
else if(nodeDir.y > 0.0f)
*nextDir = 1; // north east
else
*nextDir = 3; // south east`
}
}
}
if(*nextNode == nil){
*nextDir = 0;
*nextNode = node;
}
}
static CPathNode *apNodesToBeCleared[6525];
void
CPathFind::DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *pNumNodes, int16 maxNumNodes, CVehicle *vehicle, float *pDist, float distLimit, int32 targetNodeId)
{
int i, j;
// Find target
if(targetNodeId < 0)
targetNodeId = FindNodeClosestToCoors(target, type, distLimit);
if(targetNodeId < 0) {
*pNumNodes = 0;
if(pDist) *pDist = 100000.0f;
return;
}
// Find start
if(startNodeId < 0)
startNodeId = FindNodeClosestToCoors(start, type, 999999.88f);
if(startNodeId < 0) {
*pNumNodes = 0;
if(pDist) *pDist = 100000.0f;
return;
}
if(startNodeId == targetNodeId){
*pNumNodes = 0;
if(pDist) *pDist = 0.0f;
return;
}
if(m_pathNodes[startNodeId].group != m_pathNodes[targetNodeId].group) {
*pNumNodes = 0;
if(pDist) *pDist = 100000.0f;
return;
}
for(i = 0; i < ARRAY_SIZE(m_searchNodes); i++)
m_searchNodes[i].SetNext(nil);
AddNodeToList(&m_pathNodes[targetNodeId], 0);
int numNodesToBeCleared = 0;
apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[targetNodeId];
// Dijkstra's algorithm
// Find distances
int numPathsFound = 0;
for(i = 0; numPathsFound == 0; i = (i+1) & 0x1FF){
CPathNode *node;
for(node = m_searchNodes[i].GetNext(); node; node = node->GetNext()){
if(node == &m_pathNodes[startNodeId])
numPathsFound = 1;
for(j = 0; j < node->numLinks; j++){
int next = ConnectedNode(node->firstLink + j);
int dist = node->distance + m_distances[node->firstLink + j];
if(dist < m_pathNodes[next].distance){
if(m_pathNodes[next].distance != MAX_DIST)
RemoveNodeFromList(&m_pathNodes[next]);
if(m_pathNodes[next].distance == MAX_DIST)
apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[next];
AddNodeToList(&m_pathNodes[next], dist);
}
}
RemoveNodeFromList(node);
}
}
// Find out whence to start tracing back
CPathNode *curNode;
curNode = &m_pathNodes[startNodeId];
*pNumNodes = 0;
if(pDist)
*pDist = m_pathNodes[startNodeId].distance;
nodes[(*pNumNodes)++] = curNode;
// Trace back to target and update list of nodes
while(*pNumNodes < maxNumNodes && curNode != &m_pathNodes[targetNodeId])
for(i = 0; i < curNode->numLinks; i++){
int next = ConnectedNode(curNode->firstLink + i);
if(curNode->distance - m_distances[curNode->firstLink + i] == m_pathNodes[next].distance){
curNode = &m_pathNodes[next];
nodes[(*pNumNodes)++] = curNode;
i = 29030; // could have used a break...
}
}
for(i = 0; i < numNodesToBeCleared; i++)
apNodesToBeCleared[i]->distance = MAX_DIST;
}
static CPathNode *pNodeList[32];
static int16 DummyResult;
static int16 DummyResult2;
bool
CPathFind::TestCoorsCloseness(CVector target, uint8 type, CVector start)
{
float dist;
if(type == PATH_CAR)
DoPathSearch(type, start, -1, target, pNodeList, &DummyResult, 32, nil, &dist, 170.0f, -1);
else
DoPathSearch(type, start, -1, target, nil, &DummyResult2, 0, nil, &dist, 50.0f, -1);
#ifdef FIX_BUGS
// dist has GenerationDistMultiplier as a factor, so our reference dist should have it too
if(type == PATH_CAR)
return dist < 180.0f*TheCamera.GenerationDistMultiplier;
else
return dist < 100.0f*TheCamera.GenerationDistMultiplier;
#else
if(type == PATH_CAR)
return dist < 180.0f;
else
return dist < 100.0f;
#endif
}
void
CPathFind::Save(uint8 *buf, uint32 *size)
{
int i;
int n = m_numPathNodes/8 + 1;
*size = 2*n;
for(i = 0; i < m_numPathNodes; i++)
if(m_pathNodes[i].bDisabled)
buf[i/8] |= 1 << i%8;
else
buf[i/8] &= ~(1 << i%8);
for(i = 0; i < m_numPathNodes; i++)
if(m_pathNodes[i].bBetweenLevels)
buf[i/8 + n] |= 1 << i%8;
else
buf[i/8 + n] &= ~(1 << i%8);
}
void
CPathFind::Load(uint8 *buf, uint32 size)
{
int i;
int n = m_numPathNodes/8 + 1;
for(i = 0; i < m_numPathNodes; i++)
if(buf[i/8] & (1 << i%8))
m_pathNodes[i].bDisabled = true;
else
m_pathNodes[i].bDisabled = false;
for(i = 0; i < m_numPathNodes; i++)
if(buf[i/8 + n] & (1 << i%8))
m_pathNodes[i].bBetweenLevels = true;
else
m_pathNodes[i].bBetweenLevels = false;
#ifdef SECUROM
// if pirated game
for(i = 0; i < m_numPathNodes; i++)
m_pathNodes[i].bDisabled = true;
#endif
}
void
CPathFind::DisplayPathData(void)
{
// Not the function from mobile but my own!
int i, j, k;
// Draw 50 units around camera
CVector pos = TheCamera.GetPosition();
const float maxDist = 50.0f;
// Render car path nodes
if(gbShowCarPaths)
for(i = 0; i < m_numCarPathNodes; i++){
if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist))
continue;
CVector n1 = m_pathNodes[i].GetPosition();
n1.z += 0.3f;
// Draw node itself
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n1.x, n1.y, n1.z + 1.0f,
0xFFFFFFFF, 0xFFFFFFFF);
for(j = 0; j < m_pathNodes[i].numLinks; j++){
k = ConnectedNode(m_pathNodes[i].firstLink + j);
CVector n2 = m_pathNodes[k].GetPosition();
n2.z += 0.3f;
// Draw links to neighbours
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n2.x, n2.y, n2.z,
0xFFFFFFFF, 0xFFFFFFFF);
}
}
// Render car path nodes
if(gbShowCarPathsLinks)
for(i = 0; i < m_numCarPathLinks; i++){
CVector2D n1_2d = m_carPathLinks[i].GetPosition();
if((n1_2d - pos).MagnitudeSqr() > SQR(maxDist))
continue;
int ni = m_carPathLinks[i].pathNodeIndex;
CVector pn1 = m_pathNodes[ni].GetPosition();
pn1.z += 0.3f;
CVector n1(n1_2d.x, n1_2d.y, pn1.z);
n1.z += 0.3f;
// Draw car node itself
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n1.x, n1.y, n1.z + 1.0f,
0xFFFFFFFF, 0xFFFFFFFF);
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z + 0.5f,
n1.x+m_carPathLinks[i].GetDirX(), n1.y+m_carPathLinks[i].GetDirY(), n1.z + 0.5f,
0xFFFFFFFF, 0xFFFFFFFF);
// Draw connection to car path node
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
pn1.x, pn1.y, pn1.z,
0xFF0000FF, 0xFFFFFFFF);
// traffic light type
uint32 col = 0xFF;
if((m_carPathLinks[i].trafficLightType&0x7F) == 1)
col += 0xFF000000;
if((m_carPathLinks[i].trafficLightType&0x7F) == 2)
col += 0x00FF0000;
if(m_carPathLinks[i].trafficLightType & 0x80)
col += 0x0000FF00;
CLines::RenderLineWithClipping(n1.x+0.2f, n1.y, n1.z,
n1.x+0.2f, n1.y, n1.z + 1.0f,
col, col);
for(j = 0; j < m_pathNodes[ni].numLinks; j++){
k = m_carPathConnections[m_pathNodes[ni].firstLink + j];
CVector2D n2_2d = m_carPathLinks[k].GetPosition();
int nk = m_carPathLinks[k].pathNodeIndex;
CVector pn2 = m_pathNodes[nk].GetPosition();
pn2.z += 0.3f;
CVector n2(n2_2d.x, n2_2d.y, pn2.z);
n2.z += 0.3f;
// Draw links to neighbours
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n2.x, n2.y, n2.z,
0xFF00FFFF, 0xFF00FFFF);
}
}
// Render ped path nodes
if(gbShowPedPaths)
for(i = m_numCarPathNodes; i < m_numPathNodes; i++){
if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist))
continue;
CVector n1 = m_pathNodes[i].GetPosition();
n1.z += 0.3f;
// Draw node itself
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n1.x, n1.y, n1.z + 1.0f,
0xFFFFFFFF, 0xFFFFFFFF);
for(j = 0; j < m_pathNodes[i].numLinks; j++){
k = ConnectedNode(m_pathNodes[i].firstLink + j);
CVector n2 = m_pathNodes[k].GetPosition();
n2.z += 0.3f;
// Draw links to neighbours
CLines::RenderLineWithClipping(n1.x, n1.y, n1.z,
n2.x, n2.y, n2.z,
0xFFFFFFFF, 0xFFFFFFFF);
// Draw connection flags
CVector mid = (n1+n2)/2.0f;
uint32 col = 0xFF;
if(ConnectionCrossesRoad(m_pathNodes[i].firstLink + j))
col += 0x00FF0000;
if(ConnectionHasTrafficLight(m_pathNodes[i].firstLink + j))
col += 0xFF000000;
CLines::RenderLineWithClipping(mid.x, mid.y, mid.z,
mid.x, mid.y, mid.z + 1.0f,
col, col);
}
}
}
CVector
CPathFind::TakeWidthIntoAccountForWandering(CPathNode* nextNode, uint16 random)
{
CVector pos = nextNode->GetPosition();
float newX = (nextNode->GetPedNodeWidth() * ((random % 16) - 7)) + pos.x;
float newY = (nextNode->GetPedNodeWidth() * (((random / 16) % 16) - 7)) + pos.y;
return CVector(newX, newY, pos.z);
}
void
CPathFind::TakeWidthIntoAccountForCoors(CPathNode* node1, CPathNode* node2, uint16 random, float* x, float* y)
{
*x += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * ((random % 16) - 7));
*y += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * (((random / 16) % 16) - 7));
}
CPathNode*
CPathFind::GetNode(int16 index)
{
if(index < 0)
return nil;
if(index < ARRAY_SIZE(ThePaths.m_searchNodes))
return &ThePaths.m_searchNodes[index];
return &ThePaths.m_pathNodes[index - ARRAY_SIZE(ThePaths.m_searchNodes)];
}
int16
CPathFind::GetIndex(CPathNode *node)
{
if(node == nil)
return -1;
if(node >= &ThePaths.m_searchNodes[0] && node < &ThePaths.m_searchNodes[ARRAY_SIZE(ThePaths.m_searchNodes)])
return node - ThePaths.m_searchNodes;
else
return (node - ThePaths.m_pathNodes) + ARRAY_SIZE(ThePaths.m_searchNodes);
}