diff options
-rw-r--r-- | src/control/PathFind.cpp | 1014 | ||||
-rw-r--r-- | src/control/PathFind.h | 103 | ||||
-rw-r--r-- | src/control/TrafficLights.cpp | 14 | ||||
-rw-r--r-- | src/control/TrafficLights.h | 7 | ||||
-rw-r--r-- | src/core/General.h | 1 | ||||
-rw-r--r-- | src/core/config.h | 6 | ||||
-rw-r--r-- | src/entities/Physical.cpp | 25 | ||||
-rw-r--r-- | src/entities/Physical.h | 3 | ||||
-rw-r--r-- | src/entities/Treadable.h | 3 | ||||
-rw-r--r-- | src/math/Vector2D.h | 28 | ||||
-rw-r--r-- | src/peds/Ped.cpp | 526 | ||||
-rw-r--r-- | src/peds/Ped.h | 7 | ||||
-rw-r--r-- | src/render/Renderer.cpp | 4 |
13 files changed, 1598 insertions, 143 deletions
diff --git a/src/control/PathFind.cpp b/src/control/PathFind.cpp index 1e128457..e9b33395 100644 --- a/src/control/PathFind.cpp +++ b/src/control/PathFind.cpp @@ -1,44 +1,25 @@ #include "common.h" #include "patcher.h" +#include "General.h" +#include "FileMgr.h" // only needed for empty function +#include "Camera.h" +#include "Vehicle.h" +#include "World.h" #include "PathFind.h" CPathFind &ThePaths = *(CPathFind*)0x8F6754; -WRAPPER int32 CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool disabled, bool betweenLevels) { EAXJMP(0x42CC30); } -WRAPPER CPathNode** CPathFind::FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*) { EAXJMP(0x42B9F0); } -WRAPPER bool CPathFind::NewGenerateCarCreationCoors(float, float, float, float, float, float, bool, CVector*, int32*, int32*, float*, bool) { EAXJMP(0x42BF10); } -WRAPPER bool CPathFind::TestCoorsCloseness(CVector, bool, CVector) { EAXJMP(0x42C8C0); } -int TempListLength; - enum { NodeTypeExtern = 1, NodeTypeIntern = 2, - PathTypeCar = 0, - PathTypePed = 1, - - PathNodeFlag1 = 1, // used? - PathNodeFlag2 = 2, - PathNodeDeadEnd = 4, - PathNodeDisabled = 8, - PathNodeBetweenLevels = 0x10, + ObjectFlag1 = 1, + ObjectEastWest = 2, - ConnectionCrossRoad = 1, - ConnectionTrafficLight = 2, + MAX_DIST = INT16_MAX-1 }; -// link flags: -// 1: crosses road -// 2: ped traffic light -// pathnode flags: -// 1: -// 2: -// 4: dead end -// 8: switched off -// 10: road between levels?? -// navi node flags: -// 1: bridge light // object flags: // 1 // 2 east/west road(?) @@ -50,10 +31,55 @@ CTempDetachedNode *&DetachedNodesCars = *(CTempDetachedNode**)0x8E2824; CTempDetachedNode *&DetachedNodesPeds = *(CTempDetachedNode**)0x8E28A0; void -CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing) +CPathFind::Init(void) { int i; + m_numPathNodes = 0; + m_numMapObjects = 0; + m_numConnections = 0; + m_numCarPathLinks = 0; + unk = 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; + + InfoForTileCars = new CPathInfoForObject[12*numPathGroups]; + memset(InfoForTileCars, 0, 12*numPathGroups*sizeof(CPathInfoForObject)); + InfoForTilePeds = new CPathInfoForObject[12*numPathGroups]; + memset(InfoForTilePeds, 0, 12*numPathGroups*sizeof(CPathInfoForObject)); + + // unused + delete[] DetachedNodesCars; + DetachedNodesCars = nil; + delete[] DetachedNodesPeds; + DetachedNodesPeds = nil; + DetachedNodesCars = new CTempDetachedNode[100]; + memset(DetachedNodesCars, 0, 100*sizeof(CTempDetachedNode)); + DetachedNodesPeds = new CTempDetachedNode[50]; + memset(DetachedNodesPeds, 0, 50*sizeof(CTempDetachedNode)); +} + +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, int16 width, bool crossing) +{ + int i, j; + i = id*12 + node; InfoForTilePeds[i].type = type; InfoForTilePeds[i].next = next; @@ -63,12 +89,23 @@ CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, InfoForTilePeds[i].numLeftLanes = 0; InfoForTilePeds[i].numRightLanes = 0; InfoForTilePeds[i].crossing = crossing; + + if(type) + for(i = 0; i < node; i++){ + j = id*12 + i; + if(x == InfoForTilePeds[j].x && y == InfoForTilePeds[j].y){ + printf("^^^^^^^^^^^^^ AARON IS TOO CHICKEN TO EAT MEAT!\n"); + printf("Several ped nodes on one road segment have identical coordinates (%d==%d && %d==%d)\n", + x, InfoForTilePeds[j].x, y, InfoForTilePeds[j].y); + printf("Modelindex of cullprit: %d\n\n", id); + } + } } void CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight) { - int i; + int i, j; i = id*12 + node; InfoForTileCars[i].type = type; @@ -78,12 +115,34 @@ CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, InfoForTileCars[i].z = z; InfoForTileCars[i].numLeftLanes = numLeft; InfoForTileCars[i].numRightLanes = numRight; + + if(type) + for(i = 0; i < node; i++){ + j = id*12 + i; + if(x == InfoForTileCars[j].x && y == InfoForTileCars[j].y){ + printf("^^^^^^^^^^^^^ AARON IS TOO CHICKEN TO EAT MEAT!\n"); + printf("Several car nodes on one road segment have identical coordinates (%d==%d && %d==%d)\n", + x, InfoForTileCars[j].x, y, InfoForTileCars[j].y); + printf("Modelindex of cullprit: %d\n\n", id); + } + } } void -CPathFind::RegisterMapObject(CTreadable *mapObject) +CPathFind::CalcNodeCoors(int16 x, int16 y, int16 z, int id, CVector *out) { - m_mapObjects[m_numMapObjects++] = mapObject; + CVector pos; + pos.x = x / 16.0f; + pos.y = y / 16.0f; + pos.z = z / 16.0f; + *out = m_mapObjects[id]->GetMatrix() * pos; +} + +bool +CPathFind::LoadPathFindData(void) +{ + CFileMgr::SetDir(""); + return false; } void @@ -95,14 +154,14 @@ CPathFind::PreparePathData(void) CTempNode *tempNodes; printf("PreparePathData\n"); - // UNUSED: CPathFind::LoadPathFindData - if(InfoForTileCars && InfoForTilePeds && + if(!CPathFind::LoadPathFindData() && // empty + InfoForTileCars && InfoForTilePeds && DetachedNodesCars && DetachedNodesPeds){ tempNodes = new CTempNode[4000]; m_numConnections = 0; for(i = 0; i < PATHNODESIZE; i++) - m_pathNodes[i].flags &= ~(PathNodeFlag1 | PathNodeFlag2); + m_pathNodes[i].unkBits = 0; for(i = 0; i < PATHNODESIZE; i++){ numExtern = 0; @@ -129,9 +188,9 @@ CPathFind::PreparePathData(void) } m_numPathNodes = 0; - PreparePathDataForType(PathTypeCar, tempNodes, InfoForTileCars, 1.0f, DetachedNodesCars, 100); + PreparePathDataForType(PATH_CAR, tempNodes, InfoForTileCars, 1.0f, DetachedNodesCars, 100); m_numCarPathNodes = m_numPathNodes; - PreparePathDataForType(PathTypePed, tempNodes, InfoForTilePeds, 1.0f, DetachedNodesPeds, 50); + PreparePathDataForType(PATH_PED, tempNodes, InfoForTilePeds, 1.0f, DetachedNodesPeds, 50); m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes; // TODO: figure out what exactly is going on here @@ -157,26 +216,26 @@ CPathFind::PreparePathData(void) if(numIntern == 1 && numExtern == 2){ if(numLanes < 4){ if((i & 7) == 4){ // WHAT? - m_objectFlags[i] |= PathNodeFlag1; + m_objectFlags[i] |= ObjectFlag1; if(maxX > maxY) - m_objectFlags[i] |= PathNodeFlag2; + m_objectFlags[i] |= ObjectEastWest; else - m_objectFlags[i] &= ~PathNodeFlag2; + m_objectFlags[i] &= ~ObjectEastWest; } }else{ - m_objectFlags[i] |= PathNodeFlag1; + m_objectFlags[i] |= ObjectFlag1; if(maxX > maxY) - m_objectFlags[i] |= PathNodeFlag2; + m_objectFlags[i] |= ObjectEastWest; else - m_objectFlags[i] &= ~PathNodeFlag2; + m_objectFlags[i] &= ~ObjectEastWest; } } } delete[] tempNodes; - CountFloodFillGroups(PathTypeCar); - CountFloodFillGroups(PathTypePed); + CountFloodFillGroups(PATH_CAR); + CountFloodFillGroups(PATH_PED); delete[] InfoForTileCars; InfoForTileCars = nil; @@ -200,11 +259,11 @@ CPathFind::CountFloodFillGroups(uint8 type) CPathNode *node, *prev; switch(type){ - case PathTypeCar: + case PATH_CAR: start = 0; end = m_numCarPathNodes; break; - case PathTypePed: + case PATH_PED: start = m_numCarPathNodes; end = start + m_numPedPathNodes; break; @@ -231,7 +290,7 @@ CPathFind::CountFloodFillGroups(uint8 type) node->group = n; if(node->numLinks == 0){ - if(type == PathTypeCar) + if(type == PATH_CAR) printf("Single car node: %f %f %f (%d)\n", node->pos.x, node->pos.y, node->pos.z, m_mapObjects[node->objectIndex]->m_modelIndex); @@ -260,6 +319,8 @@ CPathFind::CountFloodFillGroups(uint8 type) printf("GraphType:%d. FloodFill groups:%d\n", type, n); } +int32 TempListLength; + void CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, float maxdist, CTempDetachedNode *detachednodes, int unused) @@ -267,7 +328,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor static CVector CoorsXFormed; int i, j, k, l; int l1, l2; - int start, typeoff; + int start; float posx, posy; float dx, dy, mag; float nearestDist; @@ -279,14 +340,13 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor int istart, jstart; int done, cont; - typeoff = 12*type; oldNumPathNodes = m_numPathNodes; oldNumLinks = m_numConnections; // Initialize map objects for(i = 0; i < m_numMapObjects; i++) for(j = 0; j < 12; j++) - m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = -1; + m_mapObjects[i]->m_nodeIndices[type][j] = -1; // Calculate internal nodes, store them and connect them to defining object for(i = 0; i < m_numMapObjects; i++){ @@ -302,8 +362,8 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor &CoorsXFormed); m_pathNodes[m_numPathNodes].pos = CoorsXFormed; m_pathNodes[m_numPathNodes].objectIndex = i; - m_pathNodes[m_numPathNodes].flags |= PathNodeFlag1; - m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = m_numPathNodes++; + m_pathNodes[m_numPathNodes].unkBits = 1; + m_mapObjects[i]->m_nodeIndices[type][j] = m_numPathNodes++; } } @@ -349,8 +409,8 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor next++; } // link to connecting internal node - tempnodes[TempListLength].link1 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next]; - if(type == PathTypeCar){ + tempnodes[TempListLength].link1 = m_mapObjects[i]->m_nodeIndices[type][next]; + if(type == PATH_CAR){ tempnodes[TempListLength].numLeftLanes = objectpathinfo[start + j].numLeftLanes; tempnodes[TempListLength].numRightLanes = objectpathinfo[start + j].numRightLanes; } @@ -364,7 +424,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor for(k = start; j != objectpathinfo[k].next; k++) next++; } - tempnodes[nearestId].link2 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next]; + tempnodes[nearestId].link2 = m_mapObjects[i]->m_nodeIndices[type][next]; tempnodes[nearestId].linkState = 2; // collapse this node with nearest we found @@ -375,7 +435,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor tempnodes[nearestId].dirX = dx/mag; tempnodes[nearestId].dirY = dy/mag; // do something when number of lanes doesn't agree - if(type == PathTypeCar) + if(type == PATH_CAR) if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 && (objectpathinfo[start + j].numLeftLanes == 0 || objectpathinfo[start + j].numRightLanes == 0)){ // why switch left and right here? @@ -407,9 +467,9 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor dist = m_pathNodes[i].pos - m_pathNodes[m_connections[m_numConnections]].pos; m_distances[m_numConnections] = dist.Magnitude(); - m_connectionFlags[m_numConnections] = 0; + m_connectionFlags[m_numConnections].flags = 0; - if(type == PathTypeCar){ + if(type == PATH_CAR){ // IMPROVE: use a goto here // Find existing car path link for(k = 0; k < m_numCarPathLinks; k++){ @@ -461,7 +521,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor dist = m_pathNodes[i].pos - m_pathNodes[j].pos; m_distances[m_numConnections] = dist.Magnitude(); - if(type == PathTypeCar){ + if(type == PATH_CAR){ posx = (m_pathNodes[i].pos.x + m_pathNodes[j].pos.x)*0.5f; posy = (m_pathNodes[i].pos.y + m_pathNodes[j].pos.y)*0.5f; dx = m_pathNodes[j].pos.x - m_pathNodes[i].pos.x; @@ -500,9 +560,9 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor // Crosses road if(objectpathinfo[istart + iseg].next == jseg && objectpathinfo[istart + iseg].crossing || objectpathinfo[jstart + jseg].next == iseg && objectpathinfo[jstart + jseg].crossing) - m_connectionFlags[m_numConnections] |= ConnectionCrossRoad; + m_connectionFlags[m_numConnections].bCrossesRoad = true; else - m_connectionFlags[m_numConnections] &= ~ConnectionCrossRoad; + m_connectionFlags[m_numConnections].bCrossesRoad = false; } m_pathNodes[i].numLinks++; @@ -511,7 +571,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor } } - if(type == PathTypeCar){ + if(type == PATH_CAR){ done = 0; // Set number of lanes for all nodes somehow // very strange code @@ -565,20 +625,20 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor } // Set flags for car nodes - if(type == PathTypeCar){ + if(type == PATH_CAR){ do{ cont = 0; for(i = 0; i < m_numPathNodes; i++){ - m_pathNodes[i].flags &= ~PathNodeDisabled; - m_pathNodes[i].flags &= ~PathNodeBetweenLevels; + m_pathNodes[i].bDisabled = false; + m_pathNodes[i].bBetweenLevels = false; // See if node is a dead end, if so, we're not done yet - if((m_pathNodes[i].flags & PathNodeDeadEnd) == 0){ + if(!m_pathNodes[i].bDeadEnd){ k = 0; for(j = 0; j < m_pathNodes[i].numLinks; j++) - if((m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].flags & PathNodeDeadEnd) == 0) + if(!m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].bDeadEnd) k++; if(k < 2){ - m_pathNodes[i].flags |= PathNodeDeadEnd; + m_pathNodes[i].bDeadEnd = true; cont = 1; } } @@ -587,7 +647,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor } // Remove isolated ped nodes - if(type == PathTypePed) + if(type == PATH_PED) for(i = oldNumPathNodes; i < m_numPathNodes; i++){ if(m_pathNodes[i].numLinks != 0) continue; @@ -604,13 +664,13 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor // Also in treadables for(j = 0; j < m_numMapObjects; j++) for(k = 0; k < 12; k++){ - if(m_mapObjects[j]->m_nodeIndicesPeds[k] == i){ + if(m_mapObjects[j]->m_nodeIndices[PATH_PED][k] == i){ // remove this one for(l = k; l < 12-1; l++) - m_mapObjects[j]->m_nodeIndicesPeds[l] = m_mapObjects[j]->m_nodeIndicesPeds[l+1]; - m_mapObjects[j]->m_nodeIndicesPeds[11] = -1; - }else if(m_mapObjects[j]->m_nodeIndicesPeds[k] > i) - m_mapObjects[j]->m_nodeIndicesPeds[k]--; + m_mapObjects[j]->m_nodeIndices[PATH_PED][l] = m_mapObjects[j]->m_nodeIndices[PATH_PED][l+1]; + m_mapObjects[j]->m_nodeIndices[PATH_PED][11] = -1; + }else if(m_mapObjects[j]->m_nodeIndices[PATH_PED][k] > i) + m_mapObjects[j]->m_nodeIndices[PATH_PED][k]--; } i--; @@ -618,20 +678,810 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor } } +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].pos.x - x) < 80.0f && + Abs(m_pathNodes[i].pos.y - y) < 80.0f && + m_pathNodes[i].numLinks > 0){ + for(j = 0; j < m_pathNodes[i].numLinks; j++){ + int next = m_connections[m_pathNodes[i].firstLink + j]; + float dist = (m_pathNodes[i].pos - m_pathNodes[next].pos).Magnitude2D(); + next = m_carPathConnections[m_pathNodes[i].firstLink + j]; + density += m_carPathLinks[next].numLeftLanes * dist; + density += m_carPathLinks[next].numRightLanes * dist; + + if(m_carPathLinks[next].numLeftLanes < 0) + printf("Link from object %d to %d (MIs)\n", + m_mapObjects[m_pathNodes[i].objectIndex]->GetModelIndex(), + m_mapObjects[m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].objectIndex]->GetModelIndex()); + if(m_carPathLinks[next].numRightLanes < 0) + printf("Link from object %d to %d (MIs)\n", + m_mapObjects[m_pathNodes[i].objectIndex]->GetModelIndex(), + m_mapObjects[m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].objectIndex]->GetModelIndex()); + } + } + } + return density/2500.0f; +} + +bool +CPathFind::TestForPedTrafficLight(CPathNode *n1, CPathNode *n2) +{ + int i; + for(i = 0; i < n1->numLinks; i++) + if(&m_pathNodes[m_connections[n1->firstLink + i]] == n2) + return m_connectionFlags[n1->firstLink + i].bTrafficLight; + return false; +} + +bool +CPathFind::TestCrossesRoad(CPathNode *n1, CPathNode *n2) +{ + int i; + for(i = 0; i < n1->numLinks; i++) + if(&m_pathNodes[m_connections[n1->firstLink + i]] == n2) + return m_connectionFlags[n1->firstLink + i].bCrossesRoad; + return false; +} + void -CPathFind::CalcNodeCoors(int16 x, int16 y, int16 z, int id, CVector *out) +CPathFind::AddNodeToList(CPathNode *node, int32 listId) { - CVector pos; - pos.x = x / 16.0f; - pos.y = y / 16.0f; - pos.z = z / 16.0f; - *out = m_mapObjects[id]->GetMatrix() * pos; + int i = listId & 0x1FF; + node->next = m_searchNodes[i].next; + node->prev = &m_searchNodes[i]; + if(m_searchNodes[i].next) + m_searchNodes[i].next->prev = node; + m_searchNodes[i].next = node; + node->distance = listId; +} + +void +CPathFind::RemoveNodeFromList(CPathNode *node) +{ + node->prev->next = node->next; + if(node->next) + node->next->prev = node->prev; +} + +void +CPathFind::RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n) +{ + int i; + if(*n < 2) + return; + if(DotProduct2D(nodes[1]->pos - pos, nodes[0]->pos - pos) < 0.0f){ + (*n)--; + for(i = 0; i < *n; i++) + nodes[i] = nodes[i+1]; + } +} + +void +CPathFind::SetLinksBridgeLights(float x1, float x2, float y1, float y2, bool enable) +{ + int i; + for(i = 0; i < m_numCarPathLinks; i++) + if(x1 < m_carPathLinks[i].posX && m_carPathLinks[i].posX < x2 && + y1 < m_carPathLinks[i].posY && m_carPathLinks[i].posY < y2) + m_carPathLinks[i].bBridgeLights = enable; +} + +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 = m_connections[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_numPathNodes; i++) + if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 && + y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 && + z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2) + 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++) + if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 && + y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 && + z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2) + SwitchOffNodeAndNeighbours(i, disable); +} + +void +CPathFind::SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable) +{ + int i; + int firstNode, lastNode; + + if(type == PATH_CAR){ + firstNode = 0; + lastNode = m_numCarPathNodes; + }else{ + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + } + + if(z1 > z2){ + float tmp = z1; + z1 = z2; + z2 = tmp; + } + + // angle of vector from p2 to p1 + float angle = CGeneral::GetRadianAngleBetweenPoints(x1, y1, x2, y2) + HALFPI; + while(angle < TWOPI) angle += TWOPI; + while(angle > TWOPI) angle -= TWOPI; + // vector from p1 to p2 + CVector2D v12(x2 - x1, y2 - y1); + float len12 = v12.Magnitude(); + CVector2D vn12 = v12/len12; + // vector from p2 to new point p3 + CVector2D v23(-Sin(angle)*length, Cos(angle)*length); + float len23 = v23.Magnitude(); // obivously just 'length' but whatever + CVector2D vn23 = v23/len23; + + bool disable = !enable; + for(i = firstNode; i < lastNode; i++){ + if(m_pathNodes[i].pos.z < z1 || m_pathNodes[i].pos.z > z2) + continue; + CVector2D d(m_pathNodes[i].pos.x - x1, m_pathNodes[i].pos.y - y1); + float dot = DotProduct2D(d, v12); + if(dot < 0.0f || dot > len12) + continue; + dot = DotProduct2D(d, v23); + if(dot < 0.0f || dot > len23) + 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 = m_connections[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++) + if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 && + y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 && + z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2) + MarkRoadsBetweenLevelsNodeAndNeighbours(i); +} + +void +CPathFind::MarkPedRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2) +{ + int i; + + for(i = m_numCarPathNodes; i < m_numPathNodes; i++) + if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 && + y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 && + z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2) + MarkRoadsBetweenLevelsNodeAndNeighbours(i); +} + +int32 +CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels) +{ + 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; + switch(m_pathNodes[i].unkBits){ + case 1: + case 2: + dist = Abs(m_pathNodes[i].pos.x - coors.x) + + Abs(m_pathNodes[i].pos.y - coors.y) + + 3.0f*Abs(m_pathNodes[i].pos.z - coors.z); + if(dist < closestDist){ + closestDist = dist; + closestNode = i; + } + break; + } + } + 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++){ + switch(m_pathNodes[i].unkBits){ + case 1: + case 2: + dX = m_pathNodes[i].pos.x - coors.x; + dY = m_pathNodes[i].pos.y - coors.y; + dist = Abs(dX) + Abs(dY) + + 3.0f*Abs(m_pathNodes[i].pos.z - coors.z); + if(dist < closestDist){ + NormalizeXY(dX, dY); + dist -= (dX*dirX + dY*dirY - 1.0f)*20.0f; + if(dist < closestDist){ + closestDist = dist; + closestNode = i; + } + } + break; + } + } + return closestNode; +} + + +float +CPathFind::FindNodeOrientationForCarPlacement(int32 nodeId) +{ + if(m_pathNodes[nodeId].numLinks == 0) + return 0.0; + CVector dir = m_pathNodes[m_connections[m_pathNodes[nodeId].firstLink]].pos - m_pathNodes[nodeId].pos; + 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].pos.x, y - m_pathNodes[nodeId].pos.y, 0.0f); + targetDir.Normalise(); + CVector dir; + + if(m_pathNodes[nodeId].numLinks == 0) + return 0.0; + + int bestNode = m_connections[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[m_connections[m_pathNodes[nodeId].firstLink + i]].pos - m_pathNodes[nodeId].pos; + dir.z = 0.0f; + dir.Normalise(); + float angle = DotProduct2D(dir, targetDir); + if(towards){ + if(angle > bestDot){ + bestDot = angle; + bestNode = m_connections[m_pathNodes[nodeId].firstLink + i]; + } + }else{ + if(angle < bestDot){ + bestDot = angle; + bestNode = m_connections[m_pathNodes[nodeId].firstLink + i]; + } + } + } + + dir = m_pathNodes[bestNode].pos - m_pathNodes[nodeId].pos; + dir.z = 0.0f; + dir.Normalise(); + return RADTODEG(dir.Heading()); +} + +bool +CPathFind::NewGenerateCarCreationCoors(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].pos, x, y); + if(dist1 < spawnDist + 60.0f){ + d1 = dist1 - spawnDist; + for(j = 0; j < m_pathNodes[node1].numLinks; j++){ + node2 = m_connections[m_pathNodes[node1].firstLink + j]; + if(m_pathNodes[node2].bDisabled && !ignoreDisabled) + continue; + dist2 = Distance2D(m_pathNodes[node2].pos, x, y); + d2 = 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].pos*f1 + m_pathNodes[node2].pos*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; + + if(m_numPedPathNodes == 0) + return false; + + for(i = 0; i < 400; i++){ + node1 = m_numCarPathNodes + CGeneral::GetRandomNumber() % m_numPedPathNodes; + if(DistanceSqr2D(m_pathNodes[node1].pos, x, y) < sq(maxDist+30.0f)){ + if(m_pathNodes[node1].numLinks == 0) + continue; + int link = m_pathNodes[node1].firstLink + CGeneral::GetRandomNumber() % m_pathNodes[node1].numLinks; + if(m_connectionFlags[link].bCrossesRoad) + continue; + node2 = m_connections[link]; + if(m_pathNodes[node1].bDisabled || m_pathNodes[node2].bDisabled) + continue; + + float f2 = (CGeneral::GetRandomNumber()&0xFF)/256.0f; + float f1 = 1.0f - f2; + *pPositionBetweenNodes = f2; + CVector pos = m_pathNodes[node1].pos*f1 + m_pathNodes[node2].pos*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(minDist < dist && dist < maxDist){ + *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; +} + +CTreadable* +CPathFind::FindRoadObjectClosestToCoors(CVector coors, uint8 type) +{ + int i, j, k; + int node1, node2; + CTreadable *closestMapObj = nil; + float closestDist = 10000.0f; + + for(i = 0; i < m_numMapObjects; i++){ + CTreadable *mapObj = m_mapObjects[i]; + if(mapObj->m_nodeIndices[type][0] < 0) + continue; + CVector vDist = mapObj->GetPosition() - coors; + float fDist = Abs(vDist.x) + Abs(vDist.y) + Abs(vDist.z); + if(fDist < 200.0f || fDist < closestDist) + for(j = 0; j < 12; j++){ + node1 = mapObj->m_nodeIndices[type][j]; + if(node1 < 0) + break; + // FIX: game uses ThePaths here explicitly + for(k = 0; k < m_pathNodes[node1].numLinks; k++){ + node2 = m_connections[m_pathNodes[node1].firstLink + k]; + float lineDist = CCollision::DistToLine(&m_pathNodes[node1].pos, &m_pathNodes[node2].pos, &coors); + if(lineDist < closestDist){ + closestDist = lineDist; + if((coors - m_pathNodes[node1].pos).MagnitudeSqr() < (coors - m_pathNodes[node2].pos).MagnitudeSqr()) + closestMapObj = m_mapObjects[m_pathNodes[node1].objectIndex]; + else + closestMapObj = m_mapObjects[m_pathNodes[node2].objectIndex]; + } + } + } + } + return closestMapObj; } -WRAPPER void CPathFind::SetLinksBridgeLights(float, float, float, float, bool) { EAXJMP(0x42E3B0); } +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)->pos).MagnitudeSqr() > 7.0f){ + // need to find the node we're coming from + node = nil; + CTreadable *obj = FindRoadObjectClosestToCoors(coors, type); + float nodeDist = 1000000000.0f; + for(i = 0; i < 12; i++){ + if(obj->m_nodeIndices[i] < 0) + break; + float dist = (coors - m_pathNodes[obj->m_nodeIndices[type][i]].pos).Magnitude2D(); + if(dist < nodeDist){ + nodeDist = dist; + node = &m_pathNodes[obj->m_nodeIndices[type][i]]; + } + } + } + + CVector2D vCurDir(Cos(curDir*PI/4.0f), Sin(curDir*PI/4.0f)); + *nextNode = 0; + float bestDot = -999999.0f; + for(i = 0; i < node->numLinks; i++){ + int next = m_connections[node->firstLink+i]; + if(node->bDisabled || m_pathNodes[next].bDisabled) + continue; + CVector pedCoors = coors; + pedCoors.z += 1.0f; + CVector nodeCoors = m_pathNodes[next].pos; + nodeCoors.z += 1.0f; + if(!CWorld::GetIsLineOfSightClear(pedCoors, nodeCoors, true, false, false, false, false, false)) + continue; + CVector2D nodeDir = m_pathNodes[next].pos - node->pos; + nodeDir /= nodeDir.Magnitude(); + 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 sone 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[4995]; + +void +CPathFind::DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *pNumNodes, int16 maxNumNodes, CVehicle *vehicle, float *pDist, float distLimit, int32 forcedTargetNode) +{ + int i, j; + + // Find target + int targetNode; + if(forcedTargetNode < 0) + targetNode = FindNodeClosestToCoors(target, type, distLimit); + else + targetNode = forcedTargetNode; + if(targetNode < 0) + goto fail; + + // Find start + int numPathsToTry; + CTreadable *startObj; + if(startNodeId < 0){ + if(vehicle == nil || (startObj = vehicle->m_treadable[type]) == nil) + startObj = FindRoadObjectClosestToCoors(start, type); + numPathsToTry = 0; + for(i = 0; i < 12; i++){ + if(startObj->m_nodeIndices[type][i] < 0) + break; + if(m_pathNodes[startObj->m_nodeIndices[type][i]].group == m_pathNodes[targetNode].group) + numPathsToTry++; + } + }else{ + numPathsToTry = 1; + startObj = m_mapObjects[m_pathNodes[startNodeId].objectIndex]; + } + if(numPathsToTry == 0) + goto fail; + + if(startNodeId < 0){ + // why only check node 0? + if(m_pathNodes[startObj->m_nodeIndices[type][0]].group != m_pathNodes[targetNode].group) + goto fail; + }else{ + if(m_pathNodes[startNodeId].group != m_pathNodes[targetNode].group) + goto fail; + } + + + for(i = 0; i < 512; i++) + m_searchNodes[i].next = nil; + AddNodeToList(&m_pathNodes[targetNode], 0); + int numNodesToBeCleared = 0; + apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[targetNode]; + + // Dijkstra's algorithm + // Find distances + int numPathsFound = 0; + if(startNodeId < 0 && m_mapObjects[m_pathNodes[targetNode].objectIndex] == startObj) + numPathsFound++; + for(i = 0; numPathsFound < numPathsToTry; i = (i+1) & 0x1FF){ + CPathNode *node; + for(node = m_searchNodes[i].next; node; node = node->next){ + if(m_mapObjects[node->objectIndex] == startObj && + (startNodeId < 0 || node == &m_pathNodes[startNodeId])) + numPathsFound++; + + for(j = 0; j < node->numLinks; j++){ + int next = m_connections[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; + if(startNodeId < 0){ + int minDist = MAX_DIST; + *pNumNodes = 1; + for(i = 0; i < 12; i++){ + if(startObj->m_nodeIndices[type][i] < 0) + break; + int dist = (m_pathNodes[startObj->m_nodeIndices[type][i]].pos - start).Magnitude(); + if(m_pathNodes[startObj->m_nodeIndices[type][i]].distance + dist < minDist){ + minDist = m_pathNodes[startObj->m_nodeIndices[type][i]].distance + dist; + curNode = &m_pathNodes[startObj->m_nodeIndices[type][i]]; + } + } + if(maxNumNodes == 0){ + *pNumNodes = 0; + }else{ + nodes[0] = curNode; + *pNumNodes = 1; + } + if(pDist) + *pDist = minDist; + }else{ + curNode = &m_pathNodes[startNodeId]; + *pNumNodes = 0; + if(pDist) + *pDist = m_pathNodes[startNodeId].distance; + } + + // Trace back to target and update list of nodes + while(*pNumNodes < maxNumNodes && curNode != &m_pathNodes[targetNode]) + for(i = 0; i < curNode->numLinks; i++){ + int next = m_connections[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; + return; + +fail: + *pNumNodes = 0; + if(pDist) + *pDist = 100000.0f; +} + +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, 999999.88f, -1); + else + DoPathSearch(type, start, -1, target, nil, &DummyResult2, 0, nil, &dist, 50.0f, -1); + if(type == PATH_CAR) + return dist < 160.0f; + else + return dist < 100.0f; +} + +void +CPathFind::Save(uint8 *buffer, uint32 *length) +{ + int i; + int n = m_numPathNodes/8 + 1; + + *length = 2*n; + + for(i = 0; i < m_numPathNodes; i++) + if(m_pathNodes[i].bDisabled) + buffer[i/8] |= 1 << i%8; + else + buffer[i/8] &= ~(1 << i%8); + + for(i = 0; i < m_numPathNodes; i++) + if(m_pathNodes[i].bBetweenLevels) + buffer[i/8 + n] |= 1 << i%8; + else + buffer[i/8 + n] &= ~(1 << i%8); +} + +void +CPathFind::Load(uint8 *buffer, uint32 length) +{ + int i; + int n = m_numPathNodes/8 + 1; + + for(i = 0; i < m_numPathNodes; i++) + if(buffer[i/8] & (1 << i%8)) + m_pathNodes[i].bDisabled = true; + else + m_pathNodes[i].bDisabled = false; + + for(i = 0; i < m_numPathNodes; i++) + if(buffer[i/8 + n] & (1 << i%8)) + m_pathNodes[i].bBetweenLevels = true; + else + m_pathNodes[i].bBetweenLevels = false; +} STARTPATCHES + InjectHook(0x4294A0, &CPathFind::Init, PATCH_JUMP); + InjectHook(0x42D580, &CPathFind::AllocatePathFindInfoMem, PATCH_JUMP); + InjectHook(0x429540, &CPathFind::RegisterMapObject, PATCH_JUMP); + InjectHook(0x42D7E0, &CPathFind::StoreNodeInfoPed, PATCH_JUMP); + InjectHook(0x42D690, &CPathFind::StoreNodeInfoCar, PATCH_JUMP); InjectHook(0x429610, &CPathFind::PreparePathData, PATCH_JUMP); - InjectHook(0x429C20, &CPathFind::PreparePathDataForType, PATCH_JUMP); InjectHook(0x42B810, &CPathFind::CountFloodFillGroups, PATCH_JUMP); + InjectHook(0x429C20, &CPathFind::PreparePathDataForType, PATCH_JUMP); + + InjectHook(0x42C990, &CPathFind::CalcRoadDensity, PATCH_JUMP); + InjectHook(0x42E1B0, &CPathFind::TestForPedTrafficLight, PATCH_JUMP); + InjectHook(0x42E340, &CPathFind::TestCrossesRoad, PATCH_JUMP); + InjectHook(0x42CBE0, &CPathFind::AddNodeToList, PATCH_JUMP); + InjectHook(0x42CBB0, &CPathFind::RemoveNodeFromList, PATCH_JUMP); + InjectHook(0x42B790, &CPathFind::RemoveBadStartNode, PATCH_JUMP); + InjectHook(0x42E3B0, &CPathFind::SetLinksBridgeLights, PATCH_JUMP); + InjectHook(0x42DED0, &CPathFind::SwitchOffNodeAndNeighbours, PATCH_JUMP); + InjectHook(0x42D960, &CPathFind::SwitchRoadsOffInArea, PATCH_JUMP); + InjectHook(0x42DA50, &CPathFind::SwitchPedRoadsOffInArea, PATCH_JUMP); + InjectHook(0x42DB50, &CPathFind::SwitchRoadsInAngledArea, PATCH_JUMP); + InjectHook(0x42E140, &CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours, PATCH_JUMP); + InjectHook(0x42DF50, &CPathFind::MarkRoadsBetweenLevelsInArea, PATCH_JUMP); + InjectHook(0x42E040, &CPathFind::MarkPedRoadsBetweenLevelsInArea, PATCH_JUMP); + InjectHook(0x42CC30, &CPathFind::FindNodeClosestToCoors, PATCH_JUMP); + InjectHook(0x42CDC0, &CPathFind::FindNodeClosestToCoorsFavourDirection, PATCH_JUMP); + InjectHook(0x42CFC0, &CPathFind::FindNodeOrientationForCarPlacement, PATCH_JUMP); + InjectHook(0x42D060, &CPathFind::FindNodeOrientationForCarPlacementFacingDestination, PATCH_JUMP); + InjectHook(0x42BF10, &CPathFind::NewGenerateCarCreationCoors, PATCH_JUMP); + InjectHook(0x42C1E0, &CPathFind::GeneratePedCreationCoors, PATCH_JUMP); + InjectHook(0x42D2A0, &CPathFind::FindRoadObjectClosestToCoors, PATCH_JUMP); + InjectHook(0x42B9F0, &CPathFind::FindNextNodeWandering, PATCH_JUMP); + InjectHook(0x42B040, &CPathFind::DoPathSearch, PATCH_JUMP); + InjectHook(0x42C8C0, &CPathFind::TestCoorsCloseness, PATCH_JUMP); + InjectHook(0x42E450, &CPathFind::Save, PATCH_JUMP); + InjectHook(0x42E550, &CPathFind::Load, PATCH_JUMP); ENDPATCHES diff --git a/src/control/PathFind.h b/src/control/PathFind.h index b5255704..d23ea823 100644 --- a/src/control/PathFind.h +++ b/src/control/PathFind.h @@ -2,24 +2,35 @@ #include "Treadable.h" +enum +{ + PATH_CAR = 0, + PATH_PED = 1, +}; + struct CPathNode { CVector pos; - CPathNode *prev; //? + CPathNode *prev; CPathNode *next; - int16 unknown; + int16 distance; // in path search int16 objectIndex; int16 firstLink; uint8 numLinks; - uint8 flags; + + uint8 unkBits : 2; + uint8 bDeadEnd : 1; + uint8 bDisabled : 1; + uint8 bBetweenLevels : 1; + uint8 group; -/* VC: - int16 unk1; +/* For reference VC: + int16 prevIndex; int16 nextIndex; int16 x; int16 y; int16 z; - int16 unknown; + int16 distance; int16 firstLink; int8 width; int8 group; @@ -40,6 +51,15 @@ struct CPathNode */ }; +union CConnectionFlags +{ + uint8 flags; + struct { + uint8 bCrossesRoad : 1; + uint8 bTrafficLight : 1; + }; +}; + struct CCarPathLink { float posX; @@ -50,10 +70,9 @@ struct CCarPathLink int8 numLeftLanes; int8 numRightLanes; int8 trafficLightType; - int8 field15; - // probably only padding - int8 field16; - int8 field17; + + uint8 bBridgeLights : 1; + // more? }; struct CPathInfoForObject @@ -80,8 +99,6 @@ struct CTempNode int8 numLeftLanes; int8 numRightLanes; int8 linkState; - // probably padding - int8 field1B; }; struct CTempDetachedNode // unused @@ -102,41 +119,65 @@ public: uint8 m_distances[20400]; int16 m_carPathConnections[20400]; */ - CPathNode m_pathNodes[4930]; - CCarPathLink m_carPathLinks[2076]; - CTreadable *m_mapObjects[1250]; - uint8 m_objectFlags[1250]; - int16 m_connections[10260]; - int16 m_distances[10260]; - uint8 m_connectionFlags[10260]; - int16 m_carPathConnections[10260]; + CPathNode m_pathNodes[NUM_PATHNODES]; + CCarPathLink m_carPathLinks[NUM_CARPATHLINKS]; + CTreadable *m_mapObjects[NUM_MAPOBJECTS]; + uint8 m_objectFlags[NUM_MAPOBJECTS]; + int16 m_connections[NUM_PATHCONNECTIONS]; + int16 m_distances[NUM_PATHCONNECTIONS]; + CConnectionFlags m_connectionFlags[NUM_PATHCONNECTIONS]; + int16 m_carPathConnections[NUM_PATHCONNECTIONS]; int32 m_numPathNodes; int32 m_numCarPathNodes; int32 m_numPedPathNodes; int16 m_numMapObjects; int16 m_numConnections; int32 m_numCarPathLinks; - int32 h; + int32 unk; uint8 m_numGroups[2]; - CPathNode m_aExtraPaths[872]; + CPathNode m_searchNodes[512]; + void Init(void); + void AllocatePathFindInfoMem(int16 numPathGroups); + void RegisterMapObject(CTreadable *mapObject); + void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing); + void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight); + void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out); + bool LoadPathFindData(void); void PreparePathData(void); void CountFloodFillGroups(uint8 type); void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, float unk, CTempDetachedNode *detachednodes, int unused); - void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out); - void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing); - void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight); - void RegisterMapObject(CTreadable *mapObject); - int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool disabled, bool betweenLevels); - CPathNode** FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*); - bool NewGenerateCarCreationCoors(float spawnX, float spawnY, float frontX, float frontY, float preferredDistance, float angleLimit /* angle limit between camera direction and vector to spawn */, bool invertAngleLimitTest, CVector* pSpawnPosition, int32* pNode1, int32* pNode2, float* pPositionBetweenNodes, bool ignoreSwitchedOff); - bool TestCoorsCloseness(CVector pos1, bool, CVector pos2); bool IsPathObject(int id) { return id < PATHNODESIZE && (InfoForTileCars[id*12].type != 0 || InfoForTilePeds[id*12].type != 0); } + float CalcRoadDensity(float x, float y); + bool TestForPedTrafficLight(CPathNode *n1, CPathNode *n2); + bool TestCrossesRoad(CPathNode *n1, CPathNode *n2); + void AddNodeToList(CPathNode *node, int32 listId); + void RemoveNodeFromList(CPathNode *node); + void RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n); void SetLinksBridgeLights(float, float, float, float, bool); + void SwitchOffNodeAndNeighbours(int32 nodeId, bool disable); + void SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); + void SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); + void SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable); + void MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId); + void MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); + void MarkPedRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); + int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled = false, bool ignoreBetweenLevels = false); + int32 FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY); + float FindNodeOrientationForCarPlacement(int32 nodeId); + float FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards); + bool NewGenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled = false); + bool GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix); + CTreadable *FindRoadObjectClosestToCoors(CVector coors, uint8 type); + void FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*); + void DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *numNodes, int16 maxNumNodes, CVehicle *vehicle, float *dist, float distLimit, int32 forcedTargetNode); + bool TestCoorsCloseness(CVector target, uint8 type, CVector start); + void Save(uint8 *buffer, uint32 *length); + void Load(uint8 *buffer, uint32 length); }; -static_assert(sizeof(CPathFind) == 0x4c8f4, "CPathFind: error"); +static_assert(sizeof(CPathFind) == 0x49bf4, "CPathFind: error"); extern CPathFind &ThePaths; diff --git a/src/control/TrafficLights.cpp b/src/control/TrafficLights.cpp index 73ff6118..f1532ab5 100644 --- a/src/control/TrafficLights.cpp +++ b/src/control/TrafficLights.cpp @@ -1,5 +1,19 @@ #include "common.h" #include "patcher.h" #include "TrafficLights.h" +#include "Timer.h" WRAPPER void CTrafficLights::DisplayActualLight(CEntity *ent) { EAXJMP(0x455800); } + +uint8 +CTrafficLights::LightForPeds(void) +{ + uint32 period = CTimer::GetTimeInMilliseconds() & 0x3FFF; // Equals to % 16384 + + if (period >= 15384) + return PED_LIGHTS_WALK_BLINK; + else if (period >= 12000) + return PED_LIGHTS_WALK; + else + return PED_LIGHTS_DONT_WALK; +}
\ No newline at end of file diff --git a/src/control/TrafficLights.h b/src/control/TrafficLights.h index eec3e1e3..db240111 100644 --- a/src/control/TrafficLights.h +++ b/src/control/TrafficLights.h @@ -2,8 +2,15 @@ class CEntity; +enum { + PED_LIGHTS_WALK, + PED_LIGHTS_WALK_BLINK, + PED_LIGHTS_DONT_WALK, +}; + class CTrafficLights { public: static void DisplayActualLight(CEntity *ent); + static uint8 LightForPeds(void); }; diff --git a/src/core/General.h b/src/core/General.h index 351243be..3d191d84 100644 --- a/src/core/General.h +++ b/src/core/General.h @@ -74,6 +74,7 @@ public: return result; } + // Returns an angle such that x2/y2 looks at x1/y1 with its forward vector if rotated by that angle static float GetRadianAngleBetweenPoints(float x1, float y1, float x2, float y2) { float x = x2 - x1; diff --git a/src/core/config.h b/src/core/config.h index 52d1dab8..8eda6187 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -33,6 +33,12 @@ enum Config { NUMTEMPOBJECTS = 30, + // Path data + NUM_PATHNODES = 4930, + NUM_CARPATHLINKS = 2076, + NUM_MAPOBJECTS = 1250, + NUM_PATHCONNECTIONS = 10260, + // Link list lengths // TODO: alpha list NUMCOLCACHELINKS = 200, diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp index 391f1e33..fbd1322d 100644 --- a/src/entities/Physical.cpp +++ b/src/entities/Physical.cpp @@ -11,6 +11,7 @@ #include "ParticleObject.h" #include "Particle.h" #include "SurfaceTable.h" +#include "PathFind.h" #include "CarCtrl.h" #include "DMAudio.h" #include "Automobile.h" @@ -56,8 +57,8 @@ CPhysical::CPhysical(void) m_phy_flagA80 = false; m_fDistanceTravelled = 0.0f; - m_pedTreadable = nil; - m_carTreadable = nil; + m_treadable[PATH_CAR] = nil; + m_treadable[PATH_PED] = nil; m_phy_flagA10 = false; m_phy_flagA20 = false; @@ -267,16 +268,16 @@ CPhysical::AddCollisionRecord_Treadable(CEntity *ent) { if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){ CTreadable *t = (CTreadable*)ent; - if(t->m_nodeIndicesPeds[0] >= 0 || - t->m_nodeIndicesPeds[1] >= 0 || - t->m_nodeIndicesPeds[2] >= 0 || - t->m_nodeIndicesPeds[3] >= 0) - m_pedTreadable = t; - if(t->m_nodeIndicesCars[0] >= 0 || - t->m_nodeIndicesCars[1] >= 0 || - t->m_nodeIndicesCars[2] >= 0 || - t->m_nodeIndicesCars[3] >= 0) - m_carTreadable = t; + if(t->m_nodeIndices[PATH_PED][0] >= 0 || + t->m_nodeIndices[PATH_PED][1] >= 0 || + t->m_nodeIndices[PATH_PED][2] >= 0 || + t->m_nodeIndices[PATH_PED][3] >= 0) + m_treadable[PATH_PED] = t; + if(t->m_nodeIndices[PATH_CAR][0] >= 0 || + t->m_nodeIndices[PATH_CAR][1] >= 0 || + t->m_nodeIndices[PATH_CAR][2] >= 0 || + t->m_nodeIndices[PATH_CAR][3] >= 0) + m_treadable[PATH_CAR] = t; } } diff --git a/src/entities/Physical.h b/src/entities/Physical.h index ee75d059..2786a7de 100644 --- a/src/entities/Physical.h +++ b/src/entities/Physical.h @@ -19,8 +19,7 @@ public: int32 m_audioEntityId; float unk1; - CTreadable *m_carTreadable; - CTreadable *m_pedTreadable; + CTreadable *m_treadable[2]; // car and ped uint32 m_nLastTimeCollided; CVector m_vecMoveSpeed; // velocity CVector m_vecTurnSpeed; // angular velocity diff --git a/src/entities/Treadable.h b/src/entities/Treadable.h index d82ff52b..9e4de59a 100644 --- a/src/entities/Treadable.h +++ b/src/entities/Treadable.h @@ -8,8 +8,7 @@ public: static void *operator new(size_t); static void operator delete(void*, size_t); - int16 m_nodeIndicesCars[12]; - int16 m_nodeIndicesPeds[12]; + int16 m_nodeIndices[2][12]; // first car, then ped bool GetIsATreadable(void) { return true; } }; diff --git a/src/math/Vector2D.h b/src/math/Vector2D.h index 76664522..a090155c 100644 --- a/src/math/Vector2D.h +++ b/src/math/Vector2D.h @@ -18,7 +18,7 @@ public: x *= invsqrt; y *= invsqrt; }else - x = 0.0f; + x = 1.0f; } const CVector2D &operator+=(CVector2D const &right) { x += right.x; @@ -52,6 +52,9 @@ public: CVector2D operator*(float t) const { return CVector2D(x*t, y*t); } + CVector2D operator/(float t) const { + return CVector2D(x/t, y/t); + } }; inline float @@ -65,3 +68,26 @@ CrossProduct2D(const CVector2D &v1, const CVector2D &v2) { return v1.x*v2.y - v1.y*v2.x; } + +inline float +Distance2D(const CVector2D &v, float x, float y) +{ + return Sqrt((v.x-x)*(v.x-x) + (v.y-y)*(v.y-y)); +} + +inline float +DistanceSqr2D(const CVector2D &v, float x, float y) +{ + return (v.x-x)*(v.x-x) + (v.y-y)*(v.y-y); +} + +inline void +NormalizeXY(float &x, float &y) +{ + float l = Sqrt(x*x + y*y); + if(l != 0.0f){ + x /= l; + y /= l; + }else + x = 1.0f; +} diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index e76b6030..f349ae6d 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -32,6 +32,7 @@ #include "TempColModels.h" #include "Pickups.h" #include "Train.h" +#include "TrafficLights.h" WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC430); } WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } @@ -51,6 +52,7 @@ WRAPPER void CPed::StartFightDefend(uint8, uint8, uint8) { EAXJMP(0x4E7780); } WRAPPER void CPed::SetDirectionToWalkAroundObject(CEntity*) { EAXJMP(0x4CCEB0); } WRAPPER void CPed::SetRadioStation(void) { EAXJMP(0x4D7BC0); } WRAPPER void CPed::MakeTyresMuddySectorList(CPtrList&) { EAXJMP(0x53CFD0); } +WRAPPER void CPed::ProcessObjective(void) { EAXJMP(0x4D94E0); } bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; @@ -1890,7 +1892,7 @@ CPed::PlayFootSteps(void) CVector2D top(forward * 0.26f); CVector2D right(GetRight() * 0.14f); - CShadows::AddPermanentShadow(1, gpBloodPoolTex, &footPos, + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &footPos, top.x, top.y, right.x, right.y, 255, 255, 0, 0, 4.0f, 3000.0f, 1.0f); @@ -3854,7 +3856,7 @@ CPed::SetWanderPath(int8 pathStateDest) if (pathStateDest == 0) pathStateDest = CGeneral::GetRandomNumberInRange(1, 7); - ThePaths.FindNextNodeWandering(1, GetPosition(), &m_pNextPathNode, &m_pLastPathNode, + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pNextPathNode, &m_pLastPathNode, m_nPathState, &nextPathState); // Circular loop until we find a node for current m_nPathState @@ -3867,7 +3869,7 @@ CPed::SetWanderPath(int8 pathStateDest) SetIdle(); return false; } - ThePaths.FindNextNodeWandering(1, GetPosition(), &m_pNextPathNode, &m_pLastPathNode, + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pNextPathNode, &m_pLastPathNode, m_nPathState, &nextPathState); } @@ -6283,13 +6285,513 @@ CPed::Fight(void) } } +// Some helper function which doesn't exist in og game. +inline void +SelectClosestNodeForSeek(CPed *ped, CPathNode *node, CVector2D closeDist, CVector2D farDist, CPathNode *closeNode, CPathNode *closeNode2, int runCount = 3) +{ + for (int i = 0; i < node->numLinks; i++) { + + CPathNode *testNode = &ThePaths.m_pathNodes[ThePaths.m_connections[i + node->firstLink]]; + + if (testNode && testNode != closeNode && testNode != closeNode2) { + CVector2D posDiff(ped->m_vecSeekVehicle - testNode->pos); + float dist = posDiff.MagnitudeSqr(); + + if (farDist.MagnitudeSqr() > dist) { + + if (closeDist.MagnitudeSqr() <= dist) { + ped->m_pLastPathNode = closeNode; + closeDist = posDiff; + } else { + ped->m_pLastPathNode = (closeNode2 ? closeNode2 : testNode); + farDist = posDiff; + } + } + + if (--runCount > 0) + SelectClosestNodeForSeek(ped, testNode, closeDist, farDist, closeNode, (closeNode2 ? closeNode2 : testNode), runCount); + } + } +} + +bool +CPed::FindBestCoordsFromNodes(CVector unused, CVector *bestCoords) +{ + if (m_pLastPathNode || !bIsFleeing) + return false; + + CVector ourPos = GetPosition(); + + int closestNodeId = ThePaths.FindNodeClosestToCoors(GetPosition(), 1, 999999.9f, false, false); + + CVector seekObjPos = m_vecSeekVehicle; + seekObjPos.z += 1.0f; + + if (CWorld::GetIsLineOfSightClear(ourPos, seekObjPos, true, false, false, true, false, false, false)) + return false; + + m_pLastPathNode = nil; + + CVector2D seekObjDist (m_vecSeekVehicle - ourPos); + + CPathNode *closestNode = &ThePaths.m_pathNodes[closestNodeId]; + CVector2D closeDist(m_vecSeekVehicle - closestNode->pos); + + SelectClosestNodeForSeek(this, closestNode, closeDist, seekObjDist, closestNode, nil); + + // Above function decided that going to the next node is more logical than seeking the object. + if (m_pLastPathNode) { + + CVector pathToNextNode = m_pLastPathNode->pos - ourPos; + if (pathToNextNode.MagnitudeSqr2D() < seekObjDist.MagnitudeSqr()) { + *bestCoords = m_pLastPathNode->pos; + return true; + } + m_pLastPathNode = nil; + } + + return false; +} + +void +CPed::FinishDieAnimCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (ped->bIsPedDieAnimPlaying) + ped->bIsPedDieAnimPlaying = false; +} + +void +CPed::FinishFightMoveCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (tFightMoves[ped->m_lastFightMove].animId == animAssoc->animId) { + ped->m_fightUnk2 = -2; + animAssoc->blendDelta = -1000.0f; + } +} + +void +CPed::FinishHitHeadCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + + if (ped->m_nPedState == PED_JUMP) + ped->RestorePreviousState(); + + ped->bIsLanding = false; +} + +void +CPed::FinishJumpCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed* ped = (CPed*)arg; + + ped->m_ped_flagG4 = true; + ped->bIsLanding = false; + + animAssoc->blendDelta = -1000.0f; +} + +void +CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (ped->m_nPedState != PED_JUMP) + return; + + CVector forward(0.15f * ped->GetForward() + ped->GetPosition()); + forward.z += CModelInfo::GetModelInfo(ped->m_modelIndex)->GetColModel()->spheres->center.z + 0.25f; + + CEntity *foundEnt = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); + if (!foundEnt) { + // Forward of forward + forward += 0.15f * ped->GetForward(); + forward.z += 0.15f; + foundEnt = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); + } + + if (foundEnt) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimBlendAssociation *handsCoverAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_HANDSCOWER, 8.0f); + handsCoverAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + handsCoverAssoc->SetFinishCallback(FinishHitHeadCB, ped); + ped->bIsLanding = true; + return; + } + + float velocityFromAnim = 0.1f; + CAnimBlendAssociation *sprintAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_SPRINT); + + if (sprintAssoc) { + velocityFromAnim = 0.05f * sprintAssoc->blendAmount + 0.17f; + } else { + CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_RUN); + if (runAssoc) { + velocityFromAnim = 0.07f * runAssoc->blendAmount + 0.1f; + } + } + + if (ped->IsPlayer()) + ped->ApplyMoveForce(0.0f, 0.0f, 8.5f); + else + ped->ApplyMoveForce(0.0f, 0.0f, 4.5f); + + if (sq(velocityFromAnim) > ped->m_vecMoveSpeed.MagnitudeSqr2D()) { + + if (TheCamera.Cams[0].Using3rdPersonMouseCam()) { + float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur); + ped->m_vecMoveSpeed.x = -velocityFromAnim * sin(fpsAngle); + ped->m_vecMoveSpeed.y = velocityFromAnim * cos(fpsAngle); + } else { + ped->m_vecMoveSpeed.x = -velocityFromAnim * sin(ped->m_fRotationCur); + ped->m_vecMoveSpeed.y = velocityFromAnim * cos(ped->m_fRotationCur); + } + } + + ped->bIsStanding = false; + ped->bIsInTheAir = true; + animAssoc->blendDelta = -1000.0f; + CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_JUMP_GLIDE); + + if (ped->bDoBloodyFootprints) { + CVector bloodPos(0.0f, 0.0f, 0.0f); + for (RwFrame *i = ped->GetNodeFrame(PED_FOOTL); i; i = RwFrameGetParent(i)) + RwV3dTransformPoints(bloodPos, bloodPos, 1, RwFrameGetMatrix(i)); + + bloodPos.z -= 0.1f; + bloodPos += 0.2f * ped->GetForward(); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + 0.26f * ped->GetForward().x, + 0.26f * ped->GetForward().y, + 0.14f * ped->GetRight().x, + 0.14f * ped->GetRight().y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + bloodPos = CVector(0.0f, 0.0f, 0.0f); + for (RwFrame* j = ped->GetNodeFrame(PED_FOOTR); j; j = RwFrameGetParent(j)) + RwV3dTransformPoints(bloodPos, bloodPos, 1, RwFrameGetMatrix(j)); + + bloodPos.z -= 0.1f; + bloodPos += 0.2f * ped->GetForward(); + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + 0.26f * ped->GetForward().x, + 0.26f * ped->GetForward().y, + 0.14f * ped->GetRight().x, + 0.14f * ped->GetRight().y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + if (ped->m_bloodyFootprintCount <= 40) { + ped->m_bloodyFootprintCount = 0; + ped->bDoBloodyFootprints = false; + } else { + ped->m_bloodyFootprintCount -= 40; + } + } +} + +void +CPed::FinishedWaitCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed* ped = (CPed*)arg; + + ped->m_nWaitTimer = 0; + ped->RestoreHeadingRate(); + ped->Wait(); +} + +void +CPed::Wait(void) +{ + AnimationId mustHaveAnim = NUM_ANIMS; + CAnimBlendAssociation *animAssoc; + CPed *pedWeLook; + + if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) { + m_nWaitState = WAITSTATE_FALSE; + RestoreHeadingRate(); + return; + } + + switch (m_nWaitState) { + + case WAITSTATE_TRAFFIC_LIGHTS: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (CTrafficLights::LightForPeds() == PED_LIGHTS_WALK) { + m_nWaitState = WAITSTATE_FALSE; + SetMoveState(PEDMOVE_WALK); + } + } + break; + + case WAITSTATE_CROSS_ROAD: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (CGeneral::GetRandomNumber() & 1 || !m_nWaitTimer) + m_nWaitState = WAITSTATE_FALSE; + else + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, nil); + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_LOOK_PED: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + m_nWaitState = WAITSTATE_FALSE; + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_DOUBLEBACK: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { + uint32 timeLeft = m_nWaitTimer - CTimer::GetTimeInMilliseconds(); + if (timeLeft < 2500 && timeLeft > 2000) { + m_nWaitTimer -= 500; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f); + } + } else { + m_nWaitState = WAITSTATE_FALSE; + SetMoveState(PEDMOVE_WALK); + } + break; + + case WAITSTATE_HITWALL: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { + if (m_lastThreatTimer > CTimer::GetTimeInMilliseconds()) { + m_lastThreatTimer = CTimer::GetTimeInMilliseconds() + 2500; + } + } else { + m_nWaitState = WAITSTATE_FALSE; + } + break; + + case WAITSTATE_TURN180: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + m_nWaitState = WAITSTATE_FALSE; + SetMoveState(PEDMOVE_WALK); + m_fRotationCur = m_fRotationCur + PI; + if (m_nPedState == PED_INVESTIGATE) + ClearInvestigateEvent(); + } + + if (m_lastThreatTimer > CTimer::GetTimeInMilliseconds()) { + m_lastThreatTimer = CTimer::GetTimeInMilliseconds() + 2500; + } + break; + + case WAITSTATE_SURPRISE: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_HIT_WALL)) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + } else { + m_nWaitState = WAITSTATE_FALSE; + } + } + break; + + case WAITSTATE_STUCK: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) + break; + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_TIRED); + + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_TURN_180); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS); + + if (animAssoc) { + if (animAssoc->IsPartial()) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } else { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 4.0f); + } + + if (animAssoc->animId == ANIM_TURN_180) { + m_fRotationCur = CGeneral::LimitRadianAngle(PI + m_fRotationCur); + m_nWaitState = WAITSTATE_FALSE; + SetMoveState(PEDMOVE_WALK); + m_nStoredMoveState = PEDMOVE_NONE; + m_panicCounter = 0; + return; + } + } + + AnimationId animToRun; + + switch (CGeneral::GetRandomNumber() & 3) { + case 0: + animToRun = ANIM_ROAD_CROSS; + break; + case 1: + animToRun = ANIM_IDLE_TIRED; + break; + case 2: + animToRun = ANIM_XPRESS_SCRATCH; + break; + case 3: + animToRun = ANIM_TURN_180; + break; + default: + break; + } + + animAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, animToRun, 4.0f); + + if (animToRun == ANIM_TURN_180) + animAssoc->SetFinishCallback(FinishedWaitCB, this); + + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(1500, 5000); + break; + + case WAITSTATE_LOOK_ABOUT: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + m_nWaitState = WAITSTATE_FALSE; + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_PLAYANIM_HANDSUP: + mustHaveAnim = ANIM_HANDSUP; + + case WAITSTATE_PLAYANIM_HANDSCOWER: + if (mustHaveAnim == NUM_ANIMS) + mustHaveAnim = ANIM_HANDSCOWER; + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + pedWeLook = (CPed*) m_pLookTarget; + + if ((!m_pLookTarget || !m_pLookTarget->IsPed() || pedWeLook->m_pPointGunAt) + && m_nPedState != PED_FLEE_ENTITY + && m_nPedState != PED_ATTACK + && CTimer::GetTimeInMilliseconds() <= m_nWaitTimer + && animAssoc) { + + TurnBody(); + } else { + m_nWaitState = WAITSTATE_FALSE; + m_nWaitTimer = 0; + if (m_pLookTarget && m_pLookTarget->IsPed()) { + + if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_ATTACK) { + + if (m_pedStats->m_fear <= 100 - pedWeLook->m_pedStats->m_temper) { + + if (GetWeapon()->IsTypeMelee()) { + + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); + if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) { + + bIsFleeing = true; + m_pLastPathNode = nil; + } + if (m_nMoveState != PEDMOVE_RUN) + SetMoveState(PEDMOVE_WALK); + + if (m_nPedType != PEDTYPE_COP) { + ProcessObjective(); + SetMoveState(PEDMOVE_WALK); + } + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_pLookTarget); + SetObjectiveTimer(20000); + } + } else { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); + if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) + { + bIsFleeing = true; + m_pLastPathNode = nil; + } + SetMoveState(PEDMOVE_RUN); + Say(SOUND_PED_FLEE_RUN); + } + } + } + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + case WAITSTATE_PLAYANIM_COWER: + mustHaveAnim = ANIM_HANDSCOWER; + + case WAITSTATE_PLAYANIM_DUCK: + if (mustHaveAnim == NUM_ANIMS) + mustHaveAnim = ANIM_DUCK_DOWN; + + case WAITSTATE_PLAYANIM_TAXI: + if (mustHaveAnim == NUM_ANIMS) + mustHaveAnim = ANIM_IDLE_TAXI; + + case WAITSTATE_PLAYANIM_CHAT: + if (mustHaveAnim == NUM_ANIMS) + mustHaveAnim = ANIM_IDLE_CHAT; + + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + m_nWaitState = WAITSTATE_FALSE; + } + break; + + case WAITSTATE_FINISH_FLEE: + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_TIRED); + if (animAssoc) { + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 4.0f); + int timer = 2000; + m_nWaitState = WAITSTATE_FALSE; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &timer); + } + } else { + m_nWaitState = WAITSTATE_FALSE; + } + break; + default: + break; + } + + if(!m_nWaitState) + RestoreHeadingRate(); +} + WRAPPER void CPed::PedGetupCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE810); } WRAPPER void CPed::PedStaggerCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8D0); } WRAPPER void CPed::PedEvadeCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D36E0); } -WRAPPER void CPed::FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D3950); } -WRAPPER void CPed::FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D6520); } -WRAPPER void CPed::FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7490); } -WRAPPER void CPed::FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A80); } WRAPPER void CPed::PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEC80); } WRAPPER void CPed::PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DE500); } WRAPPER void CPed::PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEAF0); } @@ -6302,9 +6804,7 @@ WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation* dra WRAPPER void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation* dragAssoc, void* arg) { EAXJMP(0x4E2920); } WRAPPER void CPed::PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E3290); } WRAPPER void CPed::PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E36E0); } -WRAPPER void CPed::FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E9830); } WRAPPER void CPed::PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E4B90); } -WRAPPER void CPed::FinishJumpCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A50); } WRAPPER void CPed::PedLandCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8A0); } WRAPPER void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4C6580); } @@ -6444,4 +6944,12 @@ STARTPATCHES InjectHook(0x4E33D0, &CPed::LineUpPedWithTrain, PATCH_JUMP); InjectHook(0x4E18D0, &CPed::ExitCar, PATCH_JUMP); InjectHook(0x4E7EE0, &CPed::Fight, PATCH_JUMP); + InjectHook(0x4D3950, &CPed::FinishDieAnimCB, PATCH_JUMP); + InjectHook(0x4E9830, &CPed::FinishFightMoveCB, PATCH_JUMP); + InjectHook(0x4D7A80, &CPed::FinishHitHeadCB, PATCH_JUMP); + InjectHook(0x4D7A50, &CPed::FinishJumpCB, PATCH_JUMP); + InjectHook(0x4D7490, &CPed::FinishLaunchCB, PATCH_JUMP); + InjectHook(0x4D6520, &CPed::FinishedWaitCB, PATCH_JUMP); + InjectHook(0x4D5D80, &CPed::Wait, PATCH_JUMP); + InjectHook(0x4E3A90, &CPed::FindBestCoordsFromNodes, PATCH_JUMP); ENDPATCHES
\ No newline at end of file diff --git a/src/peds/Ped.h b/src/peds/Ped.h index 7e0e9aa3..d078f4d4 100644 --- a/src/peds/Ped.h +++ b/src/peds/Ped.h @@ -303,7 +303,7 @@ public: uint8 m_ped_flagG1 : 1; uint8 m_ped_flagG2 : 1; uint8 m_ped_flagG4 : 1; - uint8 m_ped_flagG8 : 1; + uint8 m_ped_flagG8 : 1; // ped starts to go somewhere when set uint8 m_ped_flagG10 : 1; uint8 m_ped_flagG20 : 1; uint8 m_ped_flagG40 : 1; @@ -408,7 +408,7 @@ public: CEntity *m_fleeFrom; uint32 m_fleeTimer; uint32 field_344; - uint32 m_lastThreatTimer; + uint32 m_lastThreatTimer; // I don't think so CEntity *m_pCollidingEntity; uint8 m_stateUnused; uint8 pad_351[3]; @@ -597,6 +597,9 @@ public: void LineUpPedWithTrain(void); void ExitCar(void); void Fight(void); + bool FindBestCoordsFromNodes(CVector unused, CVector* a6); + void Wait(void); + void ProcessObjective(void); // Static methods static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 77489e60..99d50e8f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -200,9 +200,9 @@ CRenderer::RenderRoads(void) if(gbShowCarRoadGroups || gbShowPedRoadGroups){ int ind = 0; if(gbShowCarRoadGroups) - ind += ThePaths.m_pathNodes[t->m_nodeIndicesCars[0]].group; + ind += ThePaths.m_pathNodes[t->m_nodeIndices[PATH_CAR][0]].group; if(gbShowPedRoadGroups) - ind += ThePaths.m_pathNodes[t->m_nodeIndicesPeds[0]].group; + ind += ThePaths.m_pathNodes[t->m_nodeIndices[PATH_PED][0]].group; SetAmbientColoursToIndicateRoadGroup(ind); } #endif |