Merge branch 'develop-Fireboyd78' of github.com:OpenDriver2/REDRIVER2 into develop-Fireboyd78

This commit is contained in:
SoapyMan 2023-04-21 22:25:09 +06:00
commit 0287aa6722
21 changed files with 362 additions and 544 deletions

View File

@ -162,13 +162,12 @@ void DrawDebugOverlays()
int lane = GetLaneByPositionOnRoad(&roadInfo, carPos); int lane = GetLaneByPositionOnRoad(&roadInfo, carPos);
sprintf(tempBuf, "%s %d flg %d%d%d spd %d len %d", sprintf(tempBuf, "%s %d PRK(%d-%d) SPD(%d) LEN(%d)",
roadInfo.straight ? "STR" : "CRV", roadInfo.straight ? "STR" : "CRV",
roadInfo.surfId, roadInfo.surfId,
(roadInfo.NumLanes & 0x20) > 0, // flag 0 - first lane? ROAD_IS_LEFTMOST_LANE_PARKING(&roadInfo),
(roadInfo.NumLanes & 0x40) > 0, // flag 1 - leftmost park ROAD_IS_RIGHTMOST_LANE_PARKING(&roadInfo),
(roadInfo.NumLanes & 0x80) > 0, // flag 2 - rightmost park ROAD_SPEED_LIMIT(&roadInfo),
ROAD_SPEED_LIMIT(&roadInfo), // speed limit id
segLen segLen
); );
@ -185,7 +184,7 @@ void DrawDebugOverlays()
PrintString(tempBuf, 10, 195); PrintString(tempBuf, 10, 195);
sprintf(tempBuf, "c %d %d %d %d", sprintf(tempBuf, "c %d %d %d %d",
(int)(*roadInfo.ConnectIdx)[0], (int)(*roadInfo.ConnectIdx)[1], (int)(*roadInfo.ConnectIdx)[2], (int)(*roadInfo.ConnectIdx)[3]); (int)roadInfo.ConnectIdx[0], (int)roadInfo.ConnectIdx[1], (int)roadInfo.ConnectIdx[2], (int)roadInfo.ConnectIdx[3]);
PrintString(tempBuf, 10, 205); PrintString(tempBuf, 10, 205);
} }
@ -193,10 +192,16 @@ void DrawDebugOverlays()
{ {
DRIVER2_JUNCTION* junc = GET_JUNCTION(roadInfo.surfId); DRIVER2_JUNCTION* junc = GET_JUNCTION(roadInfo.surfId);
sprintf(tempBuf, "JUN %d flg %d - c %d %d %d %d",roadInfo.surfId, junc->flags, sprintf(tempBuf, "JUN %d TL(%d) YLD(%d)",
(int)(*roadInfo.ConnectIdx)[0], (int)(*roadInfo.ConnectIdx)[1], (int)(*roadInfo.ConnectIdx)[2], (int)(*roadInfo.ConnectIdx)[3]); roadInfo.surfId,
(junc->flags & 1), (junc->flags & 2));
PrintString(tempBuf, 10, 180); PrintString(tempBuf, 10, 180);
sprintf(tempBuf, "c %d %d %d %d",
(int)roadInfo.ConnectIdx[0], (int)roadInfo.ConnectIdx[1], (int)roadInfo.ConnectIdx[2], (int)roadInfo.ConnectIdx[3]);
PrintString(tempBuf, 10, 205);
} }

View File

@ -111,8 +111,10 @@ int FrustrumCheck16(PACKED_CELL_OBJECT* pcop, int bounding_sphere)
ang = FRUSTUM_THRESHOLD - bounding_sphere; ang = FRUSTUM_THRESHOLD - bounding_sphere;
if (ang <= MIN(result.vx, result.vz)) if (ang <= result.vx && ang <= result.vy && ang <= result.vz)
{
return 0; return 0;
}
return -1; return -1;
} }
@ -132,8 +134,10 @@ int FrustrumCheck(VECTOR* pos, int bounding_sphere)
ang = FRUSTUM_THRESHOLD - bounding_sphere; ang = FRUSTUM_THRESHOLD - bounding_sphere;
if (ang <= MIN(result.vx, result.vz)) if (ang <= result.vx && ang <= result.vy && ang <= result.vz)
{
return 0; return 0;
}
return -1; return -1;
} }

View File

@ -103,6 +103,8 @@ char UglyLowCarLODs[4][10] = {
}; };
#endif #endif
int GetNodePos(DRIVER2_STRAIGHT* straight, DRIVER2_JUNCTION* junction, DRIVER2_CURVE* curve, int distAlongPath, CAR_DATA* cp, int* x, int* z, int laneNo);
// [D] [T] // [D] [T]
int InitCar(CAR_DATA* cp, int direction, LONGVECTOR4* startPos, unsigned char control, int model, int palette, char* extraData) int InitCar(CAR_DATA* cp, int direction, LONGVECTOR4* startPos, unsigned char control, int model, int palette, char* extraData)
{ {
@ -265,6 +267,66 @@ void CivCarFX(CAR_DATA* cp)
AddBrakeLight(cp); AddBrakeLight(cp);
} }
int GetLeftBoundLane(DRIVER2_ROAD_INFO& roadInfo, int oppDir)
{
int i, laneCount, laneNo;
laneCount = ROAD_WIDTH_IN_LANES(&roadInfo);
laneNo = laneCount;
for (i = laneNo - 1; i >= 0; i--)
{
if (ROAD_IS_AI_LANE(&roadInfo, i) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, i))
{
test42 = ROAD_LANE_DIR(&roadInfo, i) ^ 1;
laneNo = i;
if (test42 == 0)
{
if (oppDir != 0)
break;
}
else
{
if (oppDir == 0)
break;
}
}
}
return laneNo;
};
int GetRightBoundLane(DRIVER2_ROAD_INFO& roadInfo, int oppDir)
{
int i, laneCount, laneNo;
laneCount = ROAD_WIDTH_IN_LANES(&roadInfo);
laneNo = ROAD_IS_LEFTMOST_LANE_PARKING(&roadInfo);
for (i = laneNo; i < laneCount; i++)
{
if (ROAD_IS_AI_LANE(&roadInfo, i) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, i))
{
test555 = ROAD_LANE_DIR(&roadInfo, i) ^ 1;
laneNo = i;
if (test555 == 0)
{
if (oppDir != 0)
break;
}
else
{
if (oppDir == 0)
break;
}
}
}
return laneNo;
}
// [D] [T] // [D] [T]
int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist, CIV_ROUTE_ENTRY* oldNode) int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist, CIV_ROUTE_ENTRY* oldNode)
{ {
@ -274,34 +336,22 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
DRIVER2_ROAD_INFO currentRoadInfo; DRIVER2_ROAD_INFO currentRoadInfo;
DRIVER2_ROAD_INFO roadInfo; DRIVER2_ROAD_INFO roadInfo;
int currentRoadId = 0;
int tmpNewRoad[2]; int tmpNewRoad[2];
int newExit = 0;
int tmpNewLane[2]; int tmpNewLane[2];
int laneFit[2]; int laneFit[2];
short validExitIdx[4]; short validExitIdx[4];
int newLane; int newRoad, newLane, newExit, numExits;
int newRoad; int currentRoadId, leftLane, rightLane;
int numExits; int oppDir, oldOppDir;
int leftLane; int turnDir, currentLaneDir;
int rightLane;
int oldOppDir;
int oppDir;
int turnDir;
int currentLaneDir;
currentRoadId = cp->ai.c.currentRoad; currentRoadId = cp->ai.c.currentRoad;
if (GetSurfaceRoadInfo(&currentRoadInfo, currentRoadId)) if (GetSurfaceRoadInfo(&currentRoadInfo, currentRoadId))
{ {
int widthInLanes;
int laneNo; int laneNo;
int count;
widthInLanes = ROAD_WIDTH_IN_LANES(&currentRoadInfo);
currentLaneDir = ROAD_LANE_DIR(&currentRoadInfo, cp->ai.c.currentLane); currentLaneDir = ROAD_LANE_DIR(&currentRoadInfo, cp->ai.c.currentLane);
@ -315,76 +365,36 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
dx = oldNode->x - currentRoadInfo.curve->Midx; dx = oldNode->x - currentRoadInfo.curve->Midx;
dz = oldNode->z - currentRoadInfo.curve->Midz; dz = oldNode->z - currentRoadInfo.curve->Midz;
oldOppDir = DIFF_ANGLES(ratan2(dx, dz), oldNode->dir); // (((oldNode->dir - ratan2(dx, dz)) + 2048U & 0xfff) - 2048); oldOppDir = DIFF_ANGLES(ratan2(dx, dz), oldNode->dir);
oldOppDir = (oldOppDir < 1) * 2048; oldOppDir = (oldOppDir < 1) * 2048;
} }
// first road is picked from road direction // first road is picked from road direction
tmpNewRoad[0] = ((short*)currentRoadInfo.ConnectIdx)[(oldOppDir > 0) * 2]; tmpNewRoad[0] = currentRoadInfo.ConnectIdx[(oldOppDir > 0) * 2];
// and second picked from lane direction // and second picked from lane direction
tmpNewRoad[1] = ((short*)currentRoadInfo.ConnectIdx)[(currentLaneDir > 0) ? 3 : 1]; tmpNewRoad[1] = currentRoadInfo.ConnectIdx[(currentLaneDir > 0) ? 3 : 1];
laneNo = GetLeftBoundLane(currentRoadInfo, currentLaneDir);
count = widthInLanes; // counter if (oldOppDir == 0)
laneNo = widthInLanes; // bestLane leftLane = laneNo;
do
{
count--;
if (ROAD_IS_AI_LANE(&currentRoadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&currentRoadInfo, count))
{
test42 = ROAD_LANE_DIR(&currentRoadInfo, count);
laneNo = count;
}
} while (count >= 0);
if (currentLaneDir == 0)
leftLane = laneNo & 0xff;
else else
rightLane = laneNo & 0xff; rightLane = laneNo;
// ifhas fast lane, use second lane laneNo = GetRightBoundLane(currentRoadInfo, currentLaneDir);
count = ROAD_HAS_FAST_LANES(&currentRoadInfo);
laneNo = widthInLanes;
while (count < widthInLanes) if (oldOppDir != 0)
{ leftLane = laneNo;
if (ROAD_IS_AI_LANE(&currentRoadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&currentRoadInfo, count))
{
test555 = ROAD_LANE_DIR(&currentRoadInfo, count) ^ 1;
laneNo = count;
if (test555 == 0)
{
if (currentLaneDir != 0)
break;
}
else
{
if (currentLaneDir == 0)
break;
}
}
laneNo = widthInLanes;
count++;
}
if (currentLaneDir != 0)
leftLane = laneNo & 0xff;
else else
rightLane = laneNo & 0xff; rightLane = laneNo;
} }
newLane = -1; newLane = -1;
if (IS_JUNCTION_SURFACE(tmpNewRoad[0])) if (IS_JUNCTION_SURFACE(tmpNewRoad[0]))
{ {
int bestExit; int exitFrom, exitCnt, bestExit;
int exitFrom;
int exitCnt;
int rnd; int rnd;
numExits = 0;
cp->ai.c.changeLaneCount = 0; cp->ai.c.changeLaneCount = 0;
jn = GET_JUNCTION(tmpNewRoad[0]); jn = GET_JUNCTION(tmpNewRoad[0]);
@ -393,15 +403,13 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
// check if road is valid for this junction // check if road is valid for this junction
// by determining connection with junction // by determining connection with junction
exitCnt = 0; for(exitCnt = 0; exitCnt < 4; exitCnt++)
while(exitCnt < 4)
{ {
if(jn->ExitIdx[exitCnt] == currentRoadId) if(jn->ExitIdx[exitCnt] == currentRoadId)
{ {
exitFrom = exitCnt; exitFrom = exitCnt;
break; break;
} }
exitCnt++;
} }
if (exitFrom == -1) if (exitFrom == -1)
@ -411,45 +419,46 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
} }
// check directions of exits // check directions of exits
//printWarning("Checking directions, exitFrom: %d, \n", exitFrom); numExits = 0;
exitCnt = 0;
do { // only need 3 cycles because we don't want make U-turns
int exitSurfId; for (exitCnt = 0; exitCnt < 3; exitCnt++)
int exitIdx = (exitFrom + exitCnt + 1) % 4; {
int valid; int exitSurfId, exitIdx, valid;
exitIdx = (exitFrom + exitCnt + 1) % 4;
valid = 0; valid = 0;
exitSurfId = jn->ExitIdx[exitIdx]; exitSurfId = jn->ExitIdx[exitIdx];
if (exitSurfId != -1) if (exitSurfId != -1)
{ {
int turnAng;
int exitDir; int exitDir;
exitDir = ((exitIdx + 4) - exitFrom) % 4; exitDir = ((exitIdx + 4) - exitFrom) % 4;
//exitDir = exitDir - (exitDir / 4) * 4;
if (exitDir == 1) if (exitDir == 1)
*turnAngle = -1024; // left turnAng = -1024; // left
else if (exitDir == 2) else if (exitDir == 2)
*turnAngle = 0; // forward turnAng = 0; // forward
else if (exitDir == 3) else if (exitDir == 3)
*turnAngle = 1024; // right turnAng = 1024; // right
else else
*turnAngle = 0; // forward again? turnAng = 0; // forward again?
test123 = 666; test123 = 666;
test555 = 666; test555 = 666;
test42 = 666; test42 = 666;
// current node direction and new direction // current node direction and new direction
turnDir = oldNode->dir + *turnAngle; turnDir = oldNode->dir + turnAng;
if (GetSurfaceRoadInfo(&roadInfo, exitSurfId)) if (GetSurfaceRoadInfo(&roadInfo, exitSurfId))
{ {
int turnAng;
int dx, dz; int dx, dz;
int laneCount; int numLanes;
laneCount = ROAD_WIDTH_IN_LANES(&roadInfo); numLanes = ROAD_WIDTH_IN_LANES(&roadInfo);
if(roadInfo.straight) if(roadInfo.straight)
{ {
@ -460,94 +469,42 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
dx = oldNode->x - roadInfo.curve->Midx; dx = oldNode->x - roadInfo.curve->Midx;
dz = oldNode->z - roadInfo.curve->Midz; dz = oldNode->z - roadInfo.curve->Midz;
oppDir = DIFF_ANGLES(ratan2(dx, dz), turnDir);// ((turnDir - ratan2(dx, dz)) + 2048U & 0xfff) - 2048; // [A] oppDir = DIFF_ANGLES(ratan2(dx, dz), turnDir);
oppDir = (oppDir < 1) * 2048; oppDir = (oppDir < 1) * 2048;
} }
turnAng = *turnAngle;
if (oppDir == 0)
turnAng = -turnAng;
if (turnAng == 0) // going forward if (turnAng == 0) // going forward
{ {
if (oppDir != oldOppDir) // next road is flipped if (oppDir != oldOppDir) // next road is flipped
newLane = laneCount - (cp->ai.c.currentLane + 1); newLane = numLanes - (cp->ai.c.currentLane + 1);
else else
newLane = cp->ai.c.currentLane; newLane = cp->ai.c.currentLane;
// goes on invalid lane?
// [A] bug fix with exitDir == 0
if (oppDir == 0 && exitDir == 0 || !ROAD_IS_AI_LANE(&roadInfo, newLane))
newLane = -1;
} }
else if (turnAng == -1024) // going left else if (turnAng == -1024) // going left
{ {
int count; newLane = GetRightBoundLane(roadInfo, oppDir);
//printInfo("check left\n");
count = ROAD_HAS_FAST_LANES(&roadInfo); // lane counter
newLane = laneCount;
// check if allowed to go on any of lanes
while (count < laneCount)
{
if (ROAD_IS_AI_LANE(&roadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, count))
{
test555 = (ROAD_LANE_DIR(&roadInfo, count) ^ 1) & 1;
newLane = count;
if (test555 == 0)
{
if (oppDir != 0)
break;
}
else
{
if (oppDir == 0)
break;
}
}
count++;
newLane = laneCount;
}
} }
else if (turnAng == 1024) else if (turnAng == 1024)
{ {
int count; newLane = GetLeftBoundLane(roadInfo, oppDir);
count = laneCount; // lane counter
newLane = laneCount;
do
{
if (ROAD_IS_AI_LANE(&roadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, count))
{
test42 = (ROAD_LANE_DIR(&roadInfo, count) ^ 1) & 1;
if (test42 == 0)
{
if (oppDir != 0)
newLane = count;
}
else
{
if (oppDir == 0)
newLane = count;
}
}
count--;
} while (count >= 0);
} }
// validate lane // validate lane
if (turnAng == 0 || newLane >= 0 && newLane < laneCount) if (newLane >= 0 && newLane < numLanes)
{ {
valid = ROAD_IS_AI_LANE(&roadInfo, newLane) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, newLane); valid = ROAD_IS_AI_LANE(&roadInfo, newLane) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, newLane);
} }
if (oppDir != oldOppDir)
{
if (currentLaneDir == ROAD_LANE_DIR(&roadInfo, newLane))
valid = 0;
}
else
{
if (currentLaneDir != ROAD_LANE_DIR(&roadInfo, newLane))
valid = 0;
}
} }
} }
@ -560,10 +517,7 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
{ {
validExitIdx[exitCnt] = 42; validExitIdx[exitCnt] = 42;
} }
}
// only need 3 cycles because we don't want make U-turns
exitCnt++;
} while (exitCnt < 3);
if (leftLane != rightLane && numExits != 1 && ROAD_LANES_COUNT(&currentRoadInfo) > 1) if (leftLane != rightLane && numExits != 1 && ROAD_LANES_COUNT(&currentRoadInfo) > 1)
{ {
@ -611,11 +565,9 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
newRoad = jn->ExitIdx[bestExit]; newRoad = jn->ExitIdx[bestExit];
if (turnAngle != NULL)
{ {
int invExit; int invExit;
invExit = (bestExit + 4 - exitFrom) % 4; invExit = (bestExit + 4 - exitFrom) % 4;
//invExit = invExit - (invExit / 4) * 4;
if (invExit == 1) if (invExit == 1)
*turnAngle = -1024; *turnAngle = -1024;
@ -691,9 +643,9 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
turnDir = oldNode->dir + *turnAngle; turnDir = oldNode->dir + *turnAngle;
// determine the new lane on the new road
{ {
int numLanes; int numLanes, turnAng;
int turnAng;
numLanes = ROAD_WIDTH_IN_LANES(&roadInfo); numLanes = ROAD_WIDTH_IN_LANES(&roadInfo);
turnAng = *turnAngle; turnAng = *turnAngle;
@ -708,7 +660,7 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
dx = oldNode->x - roadInfo.curve->Midx; dx = oldNode->x - roadInfo.curve->Midx;
dz = oldNode->z - roadInfo.curve->Midz; dz = oldNode->z - roadInfo.curve->Midz;
oppDir = DIFF_ANGLES(ratan2(dx, dz), turnDir); // (((turnDir - ratan2(dx, dz)) + 2048U & 0xfff) - 2048); oppDir = DIFF_ANGLES(ratan2(dx, dz), turnDir);
oppDir = (oppDir < 1) * 2048; oppDir = (oppDir < 1) * 2048;
} }
@ -737,68 +689,14 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
} }
else if (turnAng == -1024) else if (turnAng == -1024)
{ {
int count; newLane = GetRightBoundLane(roadInfo, oppDir);
count = ROAD_HAS_FAST_LANES(&roadInfo);
newLane = numLanes;
// check if allowed to go on any of lanes
while (count < numLanes)
{
if (ROAD_IS_AI_LANE(&roadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, count))
{
test555 = (ROAD_LANE_DIR(&roadInfo, count) ^ 1) & 1;
newLane = count;
if (test555 == 0)
{
if (oppDir != 0)
break;
}
else
{
if (oppDir == 0)
break;
}
}
count++;
newLane = numLanes;
}
//printWarning("car %d check left lane, chosen %d\n", cp->id, newLane);
} }
else if (turnAng == 1024) else if (turnAng == 1024)
{ {
int count; newLane = GetLeftBoundLane(roadInfo, oppDir);
count = numLanes;
newLane = numLanes;
do
{
if (ROAD_IS_AI_LANE(&roadInfo, count) && !ROAD_IS_PARKING_ALLOWED_AT(&roadInfo, count))
{
test42 = (ROAD_LANE_DIR(&roadInfo, count) ^ 1) & 1;
if (test42 == 0)
{
if (oppDir != 0)
newLane = count;
}
else
{
if (oppDir == 0)
newLane = count;
}
}
count--;
} while (count >= 0);
//printWarning("car %d check right lane, chosen %d\n", cp->id, newLane);
} }
if (*turnAngle != 0) if (*turnAngle != 0)
{ {
if (numLanes - 1 == newLane) if (numLanes - 1 == newLane)
@ -843,11 +741,23 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
{ {
if (tmpNewRoad[roadCnt] != -1) if (tmpNewRoad[roadCnt] != -1)
{ {
int px, pz;
int dx, dz; int dx, dz;
int numLanes; int numLanes;
numLanes = 0; numLanes = 0;
// [A] fix lane changingg issues
if(cp->ai.c.changeLaneCount > 0)
{
GetNodePos(currentRoadInfo.straight, NULL, currentRoadInfo.curve, oldNode->distAlongSegment, NULL, &px, &pz, cp->ai.c.currentLane);
}
else
{
px = oldNode->x;
pz = oldNode->z;
}
if (GetSurfaceRoadInfo(&roadInfo, tmpNewRoad[roadCnt])) if (GetSurfaceRoadInfo(&roadInfo, tmpNewRoad[roadCnt]))
{ {
numLanes = ROAD_WIDTH_IN_LANES(&roadInfo); numLanes = ROAD_WIDTH_IN_LANES(&roadInfo);
@ -855,8 +765,8 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
// determine new lane by old node position // determine new lane by old node position
if(roadInfo.straight) if(roadInfo.straight)
{ {
dx = (oldNode->x - roadInfo.straight->Midx); dx = (px - roadInfo.straight->Midx);
dz = (oldNode->z - roadInfo.straight->Midz); dz = (pz - roadInfo.straight->Midz);
tmpNewLane[roadCnt] = ROAD_LANES_COUNT(&roadInfo) tmpNewLane[roadCnt] = ROAD_LANES_COUNT(&roadInfo)
- (FIXEDH(dx * RCOS(roadInfo.straight->angle) - dz * RSIN(roadInfo.straight->angle)) + 512 >> 9); - (FIXEDH(dx * RCOS(roadInfo.straight->angle) - dz * RSIN(roadInfo.straight->angle)) + 512 >> 9);
@ -864,8 +774,8 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
} }
else else
{ {
dx = oldNode->x - roadInfo.curve->Midx; dx = px - roadInfo.curve->Midx;
dz = oldNode->z - roadInfo.curve->Midz; dz = pz - roadInfo.curve->Midz;
tmpNewLane[roadCnt] = (SquareRoot0(dx * dx + dz * dz) >> 9) - roadInfo.curve->inside * 2; tmpNewLane[roadCnt] = (SquareRoot0(dx * dx + dz * dz) >> 9) - roadInfo.curve->inside * 2;
} }
@ -882,7 +792,6 @@ int GetNextRoadInfo(CAR_DATA* cp, int randomExit, int* turnAngle, int* startDist
} }
} }
// fit new lane // fit new lane
newLane = tmpNewLane[roadCnt]; newLane = tmpNewLane[roadCnt];
@ -1055,11 +964,44 @@ void InitNodeList(CAR_DATA* cp, EXTRA_CIV_DATA* extraData)
cr->distAlongSegment = extraData->distAlongSegment; cr->distAlongSegment = extraData->distAlongSegment;
} }
// [A] bug fixes the incorrect lane changes
void RemapLaneChange(CAR_DATA* cp)
{
CIV_ROUTE_ENTRY* currentNode;
DRIVER2_ROAD_INFO roadInfo;
int numLanes, dx, dz;
int newLane;
if (!GetSurfaceRoadInfo(&roadInfo, cp->ai.c.currentRoad))
return;
currentNode = &cp->ai.c.targetRoute[cp->ai.c.currentNode];
numLanes = ROAD_WIDTH_IN_LANES(&roadInfo);
// determine lane on new road by old node position
if (roadInfo.straight)
{
dx = (currentNode->x - roadInfo.straight->Midx);
dz = (currentNode->z - roadInfo.straight->Midz);
newLane = ROAD_LANES_COUNT(&roadInfo)
- (FIXEDH(dx * RCOS(roadInfo.straight->angle) - dz * RSIN(roadInfo.straight->angle)) + 512 >> 9);
}
else
{
dx = currentNode->x - roadInfo.curve->Midx;
dz = currentNode->z - roadInfo.curve->Midz;
newLane = (SquareRoot0(dx * dx + dz * dz) >> 9) - roadInfo.curve->inside * 2;
}
cp->ai.c.currentLane = newLane;
}
// [D] [T] // [D] [T]
int GetNodePos(DRIVER2_STRAIGHT* straight, DRIVER2_JUNCTION* junction, DRIVER2_CURVE* curve, int distAlongPath, CAR_DATA* cp, int* x, int* z, int laneNo) int GetNodePos(DRIVER2_STRAIGHT* straight, DRIVER2_JUNCTION* junction, DRIVER2_CURVE* curve, int distAlongPath, CAR_DATA* cp, int* x, int* z, int laneNo)
{ {
unsigned char oldLane; u_char oldLane, changeLaneCount;
unsigned char changeLaneCount;
int angle; int angle;
int distFromCentre; int distFromCentre;
int sideShift; int sideShift;
@ -1135,13 +1077,10 @@ int GetNodePos(DRIVER2_STRAIGHT* straight, DRIVER2_JUNCTION* junction, DRIVER2_C
// [D] [T] // [D] [T]
int CheckChangeLanes(DRIVER2_STRAIGHT* straight, DRIVER2_CURVE* curve, int distAlongSegment, CAR_DATA* cp, int tryToPark) int CheckChangeLanes(DRIVER2_STRAIGHT* straight, DRIVER2_CURVE* curve, int distAlongSegment, CAR_DATA* cp, int tryToPark)
{ {
int oldLane; int oldLane, newLane, currentLane;
int currentLane;
int newLane;
int trials; int trials;
CAR_COSMETICS* car_cos; CAR_COSMETICS* car_cos;
int dx; int dx, dz;
int dz;
u_int theta; u_int theta;
int length; int length;
CAR_DATA* lcp; CAR_DATA* lcp;
@ -2244,7 +2183,7 @@ int PingInCivCar(int minPingInDist)
// this is closest to OG decompiled. Works different! // this is closest to OG decompiled. Works different!
//if (( //if ((
// ((tryPingInParkedCars && allowedToPark))) || // ((tryPingInParkedCars && allowedToPark))) ||
// ((ROAD_IS_AI_LANE(straight, i) && (((i != 0 || ((straight->NumLanes & 0x40U) == 0)) && (((straight->NumLanes & 0xffffff0f) * 2 - 1 != i || ((straight->NumLanes & 0x80U) == 0)))))))) // ((ROAD_IS_AI_LANE(&roadInfo, i) && (((i != 0 || ((roadInfo.NumLanes & 0x40U) == 0)) && (((roadInfo.NumLanes & 0xffffff0f) * 2 - 1 != i || ((roadInfo.NumLanes & 0x80U) == 0))))))))
// pick only non-parkable driveable lanes if parked cars not requested // pick only non-parkable driveable lanes if parked cars not requested
if (tryPingInParkedCars && allowedToPark || ROAD_IS_AI_LANE(&roadInfo, i) && !allowedToPark) if (tryPingInParkedCars && allowedToPark || ROAD_IS_AI_LANE(&roadInfo, i) && !allowedToPark)
@ -2943,7 +2882,7 @@ void SetUpCivCollFlags(void)
if (ABS(cd[0].x.vx - cd[1].x.vx) >= dist || if (ABS(cd[0].x.vx - cd[1].x.vx) >= dist ||
ABS(cd[0].x.vz - cd[1].x.vz) >= dist || ABS(cd[0].x.vz - cd[1].x.vz) >= dist ||
isTanner && ABS(cd[0].x.vy - cd[1].x.vy) >= 500) ABS(cd[0].x.vy - cd[1].x.vy) >= 500)
{ {
continue; continue;
} }
@ -3251,10 +3190,8 @@ int CivFindPointOnPath(CAR_DATA * cp, int station, VECTOR * ppoint)
CIV_ROUTE_ENTRY* start; CIV_ROUTE_ENTRY* start;
CIV_ROUTE_ENTRY* currentNode; CIV_ROUTE_ENTRY* currentNode;
CIV_ROUTE_ENTRY* retNode; CIV_ROUTE_ENTRY* retNode;
int dx; int dx, dz;
int dz; int sx, sz;
int sx;
int sz;
start = cp->ai.c.pnode; start = cp->ai.c.pnode;
@ -3741,8 +3678,8 @@ int CivControl(CAR_DATA* cp)
#if 0 #if 0
{ {
//maxCivCars = 2; //maxCivCars = 2;
//maxCopCars = 0; //maxCopCars = 0;
extern void Debug_AddLine(VECTOR & pointA, VECTOR & pointB, CVECTOR & color); extern void Debug_AddLine(VECTOR & pointA, VECTOR & pointB, CVECTOR & color);
extern void Debug_AddLineOfs(VECTOR & pointA, VECTOR & pointB, VECTOR & ofs, CVECTOR & color); extern void Debug_AddLineOfs(VECTOR & pointA, VECTOR & pointB, VECTOR & ofs, CVECTOR & color);

View File

@ -25,6 +25,22 @@ CAR_COSMETICS car_cosmetics[MAX_CAR_MODELS];
CAR_COSMETICS levelSpecCosmetics[5]; CAR_COSMETICS levelSpecCosmetics[5];
#endif #endif
#ifndef PSX
// [A] loads car cosmetics from file
void LoadCustomCarCosmetics(CAR_COSMETICS* dest, int modelNumber)
{
char filename[64];
sprintf(filename, "LEVELS\\%s\\CARMODEL_%d.COS", LevelNames[GameLevel], modelNumber);
if (!FileExists(filename))
{
return;
}
LoadfileSeg(filename, (char*)dest, 0, sizeof(CAR_COSMETICS));
}
#endif
// [D] [T] // [D] [T]
void ProcessCosmeticsLump(char *lump_ptr, int lump_size) void ProcessCosmeticsLump(char *lump_ptr, int lump_size)
{ {
@ -51,6 +67,9 @@ void ProcessCosmeticsLump(char *lump_ptr, int lump_size)
offset = *(int*)(lump_ptr + model * sizeof(int)); offset = *(int*)(lump_ptr + model * sizeof(int));
car_cosmetics[i] = *(CAR_COSMETICS*)((u_char*)lump_ptr + offset); car_cosmetics[i] = *(CAR_COSMETICS*)((u_char*)lump_ptr + offset);
#ifndef PSX
LoadCustomCarCosmetics(&car_cosmetics[i], model);
#endif
FixCarCos(&car_cosmetics[i], model); FixCarCos(&car_cosmetics[i], model);
} }
} }

View File

@ -86,55 +86,6 @@ void FreeCutsceneBuffer();
int IsCutsceneResident(int cutscene); int IsCutsceneResident(int cutscene);
#ifndef PSX
char gUserReplayFolderList[MAX_USER_REPLAYS][48];
int gNumUserChases = 0;
int gUserChaseLoaded = -1;
// [A] user replay folders initialization
void InitUserReplays(const char* str)
{
int quit;
char* ptr;
char* strStart;
gNumUserChases = 0;
if (!str)
return;
ptr = (char*)str;
strStart = NULL;
memset(gUserReplayFolderList, 0, sizeof(gUserReplayFolderList));
quit = 0;
while(true)
{
if (strStart == NULL)
strStart = ptr;
// if we're encountered string end go on
if(*ptr == ',' || *ptr == ' ' || *ptr == '\0')
{
if (*ptr == '\0')
quit = 1;
*ptr = '\0';
strcpy(gUserReplayFolderList[gNumUserChases++], strStart);
strStart = NULL;
}
ptr++;
if (quit)
break;
}
}
#endif
// [D] [T] // [D] [T]
void InitInGameCutsceneVariables(void) void InitInGameCutsceneVariables(void)
{ {
@ -159,10 +110,6 @@ void InitInGameCutsceneVariables(void)
gSkipInGameCutscene = 0; gSkipInGameCutscene = 0;
#ifndef PSX
gUserChaseLoaded = -1;
#endif
FreeCutsceneBuffer(); FreeCutsceneBuffer();
} }
@ -236,18 +183,6 @@ void DrawInGameCutscene(void)
if (gInGameCutsceneActive == 0 && gInGameCutsceneDelay == 0) if (gInGameCutsceneActive == 0 && gInGameCutsceneDelay == 0)
{ {
#ifndef PSX
if(gInGameChaseActive && gUserChaseLoaded != -1 && (CameraCnt - frameStart) < 200)
{
// [A] print user chaser name on screen
char tempStr[80];
sprintf(tempStr, "%s %s", G_LTXT(GTXT_GetawayIs), gUserReplayFolderList[gUserChaseLoaded]);
SetTextColour(128, 128, 64);
PrintString(tempStr, gOverlayXPos, 230);
}
#endif
return; return;
} }
@ -392,34 +327,8 @@ int SelectCutsceneFile(char* filename, int init, int subindex)
if (init) if (init)
{ {
// try load replacement bundle // try load replacement bundle
#ifndef PSX sprintf(filename, "REPLAYS\\ReChases\\CUT%d_N.R", gCurrentMissionNumber);
int userId = -1; gReChaseAvailable = FileExists(filename);
// [A] REDRIVER2 PC - custom user chases
if (gNumUserChases)
{
userId = rand() % (gNumUserChases + 1);
// if random decides to have no user chase - get og or replacement one
if (userId == gNumUserChases)
userId = -1;
}
// try loading user chase
if (userId != -1)
sprintf(filename, "REPLAYS\\UserChases\\%s\\CUT%d_N.R", (char*)gUserReplayFolderList[userId], gCurrentMissionNumber);
if (FileExists(filename))
{
gUserChaseLoaded = userId;
gReChaseAvailable = 0;
}
else
#endif
{
sprintf(filename, "REPLAYS\\ReChases\\CUT%d_N.R", gCurrentMissionNumber);
gReChaseAvailable = FileExists(filename);
}
} }
if (subindex >= 2) if (subindex >= 2)
@ -427,21 +336,14 @@ int SelectCutsceneFile(char* filename, int init, int subindex)
if (gReChaseAvailable == 1) if (gReChaseAvailable == 1)
{ {
sprintf(filename, "REPLAYS\\ReChases\\CUT%d_N.R", gCurrentMissionNumber); sprintf(filename, "REPLAYS\\ReChases\\CUT%d_N.R", gCurrentMissionNumber);
return FileExists(filename);
} }
#ifndef PSX
else if (gUserChaseLoaded != -1)
{
sprintf(filename, "REPLAYS\\UserChases\\%s\\CUT%d_N.R", (char*)gUserReplayFolderList[gUserChaseLoaded], gCurrentMissionNumber);
}
#endif
} }
if (gCurrentMissionNumber < 21)
sprintf(filename, "REPLAYS\\CUT%d.R", gCurrentMissionNumber);
else else
{ sprintf(filename, "REPLAYS\\A\\CUT%d.R", gCurrentMissionNumber);
if (gCurrentMissionNumber < 21)
sprintf(filename, "REPLAYS\\CUT%d.R", gCurrentMissionNumber);
else
sprintf(filename, "REPLAYS\\A\\CUT%d.R", gCurrentMissionNumber);
}
return FileExists(filename); return FileExists(filename);
} }
@ -462,7 +364,7 @@ int CalcInGameCutsceneSize(void)
LoadfileSeg(filename, (char*)&CutsceneHeader, 0, sizeof(CUTSCENE_HEADER)); LoadfileSeg(filename, (char*)&CutsceneHeader, 0, sizeof(CUTSCENE_HEADER));
// load re-chase file header // load re-chase file header
if(SelectCutsceneFile(filename, 0, 2)) if (SelectCutsceneFile(filename, 0, 2))
LoadfileSeg(filename, (char*)&ChaseHeader, 0, sizeof(CUTSCENE_HEADER)); LoadfileSeg(filename, (char*)&ChaseHeader, 0, sizeof(CUTSCENE_HEADER));
maxSize = 0; maxSize = 0;

View File

@ -13,16 +13,6 @@ struct CUTSCENE_HEADER
CUTSCENE_INFO data[15]; CUTSCENE_INFO data[15];
}; };
#ifndef PSX
#define MAX_USER_REPLAYS 16
extern char gUserReplayFolderList[MAX_USER_REPLAYS][48];
extern int gNumUserChases;
extern void InitUserReplays(const char* str);
#endif // PSX
extern int NumCutsceneStreams; extern int NumCutsceneStreams;
extern int gSkipInGameCutscene; extern int gSkipInGameCutscene;
extern int gInGameCutsceneID; extern int gInGameCutsceneID;

View File

@ -2054,18 +2054,17 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
if (z < 0) if (z < 0)
z = 0; z = 0;
if (z < 10000)
tail_width = (10000 - z) >> 0xd;
else
tail_width = 0;
addPrim(current->ot + z, poly); addPrim(current->ot + z, poly);
current->primptr += sizeof(POLY_FT4); current->primptr += sizeof(POLY_FT4);
if (CameraCnt <= 4 || NumPlayers > 1) // [A] don't draw trails in multiplayer if (CameraCnt <= 4 || NumPlayers > 1) // [A] don't draw trails in multiplayer
return; return;
if (z < 10000)
tail_width = (10000 - z) >> 13;
else
return;
if ((col->cd & 0x20) && gLightsOn) if ((col->cd & 0x20) && gLightsOn)
{ {
trails = Known_Lamps[LightIndex].light_trails; trails = Known_Lamps[LightIndex].light_trails;
@ -2093,15 +2092,15 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
} }
#ifndef PSX #ifndef PSX
x = (poly->x0 + poly->x3) / 2.0f; x = (poly->x0 + poly->x3) * 0.5f;
y = (poly->y0 + poly->y3) / 2.0f; y = (poly->y0 + poly->y3) * 0.5f;
#else #else
x = (poly->x0 + poly->x3) / 2; x = (poly->x0 + poly->x3) / 2;
y = (poly->y0 + poly->y3) / 2; y = (poly->y0 + poly->y3) / 2;
#endif #endif
// unified drawing both for car and lamps // unified drawing both for car and lamps
if (CameraChanged == 0 && *clock == (FrameCnt & 0xffffU)-1) if (CameraChanged == 0 && *clock == (FrameCnt & 0xffffU) - 1)
{ {
int old_x, old_y; int old_x, old_y;
@ -2114,7 +2113,11 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
if (size > 1 && ABS(old_x - x) + ABS(old_y - y) > 1) if (size > 1 && ABS(old_x - x) + ABS(old_y - y) > 1)
{ {
int angle, width; int angle, width;
VERTTYPE dx, dy; #ifdef PSX
int dx, dy;
#else
float dx, dy;
#endif
trail = (POLY_G4 *)current->primptr; trail = (POLY_G4 *)current->primptr;
@ -2124,21 +2127,20 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
angle = -ratan2(old_x - x,old_y - y) & 0xfff; angle = -ratan2(old_x - x,old_y - y) & 0xfff;
width = ABS(poly->x0 - poly->x3); width = ABS(poly->x0 - poly->x3);
#ifdef PSX
dx = RCOS(angle) * width * 3; dx = RCOS(angle) * width * 3;
dy = RSIN(angle) * width * 3; dy = RSIN(angle) * width * 3;
if (col->cd & 0x40) if (col->cd & 0x40)
{ {
dx >>= 0x10; dx /= 1 << 16;
dy >>= 0x10; dy /= 1 << 16;
} }
else else
{ {
dx >>= 0xf; dx /= 1 << 15;
dy >>= 0xf; dy /= 1 << 15;
} }
trail->x0 = x + dx * tail_width; trail->x0 = x + dx * tail_width;
trail->y0 = y + dy * tail_width; trail->y0 = y + dy * tail_width;
@ -2150,34 +2152,6 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
trail->x3 = old_x - dx; trail->x3 = old_x - dx;
trail->y3 = old_y - dy; trail->y3 = old_y - dy;
#else
// [A] slightly bigger light trail
dx = RCOS(angle);
dy = RSIN(angle);
if (col->cd & 0x40)
{
dx = dx / 40000.0f;
dy = dy / 40000.0f;
}
else
{
dx = dx / 32768.0f;
dy = dy / 32768.0f;
}
trail->x0 = x + dx * width * 3 * tail_width;
trail->y0 = y + dy * width * 3 * tail_width;
trail->x1 = x - dx * width * 3 * tail_width;
trail->y1 = y - dy * width * 3 * tail_width;
trail->x2 = old_x + dx * width * 3;
trail->y2 = old_y + dy * width * 3;
trail->x3 = old_x - dx * width * 3;
trail->y3 = old_y - dy * width * 3;
#endif
if (col->cd & 0x18) if (col->cd & 0x18)
{ {
@ -2212,8 +2186,8 @@ void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
addPrim(current->ot + z, null); addPrim(current->ot + z, null);
current->primptr += sizeof(POLY_FT3); current->primptr += sizeof(POLY_FT3);
}
} }
}
else else
{ {
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)

View File

@ -101,7 +101,7 @@ int GetSurfaceRoadInfo(DRIVER2_ROAD_INFO* outRoadInfo, int surfId)
if(IS_CURVED_SURFACE(surfId)) if(IS_CURVED_SURFACE(surfId))
{ {
outRoadInfo->curve = curve = GET_CURVE(surfId); outRoadInfo->curve = curve = GET_CURVE(surfId);
outRoadInfo->ConnectIdx = &curve->ConnectIdx; outRoadInfo->ConnectIdx = curve->ConnectIdx;
outRoadInfo->NumLanes = curve->NumLanes; outRoadInfo->NumLanes = curve->NumLanes;
outRoadInfo->LaneDirs = curve->LaneDirs; outRoadInfo->LaneDirs = curve->LaneDirs;
outRoadInfo->AILanes = curve->AILanes; outRoadInfo->AILanes = curve->AILanes;
@ -110,7 +110,7 @@ int GetSurfaceRoadInfo(DRIVER2_ROAD_INFO* outRoadInfo, int surfId)
else if (IS_STRAIGHT_SURFACE(surfId)) else if (IS_STRAIGHT_SURFACE(surfId))
{ {
outRoadInfo->straight = straight = GET_STRAIGHT(surfId); outRoadInfo->straight = straight = GET_STRAIGHT(surfId);
outRoadInfo->ConnectIdx = &straight->ConnectIdx; outRoadInfo->ConnectIdx = straight->ConnectIdx;
outRoadInfo->NumLanes = straight->NumLanes; outRoadInfo->NumLanes = straight->NumLanes;
outRoadInfo->LaneDirs = straight->LaneDirs; outRoadInfo->LaneDirs = straight->LaneDirs;
outRoadInfo->AILanes = straight->AILanes; outRoadInfo->AILanes = straight->AILanes;
@ -119,7 +119,7 @@ int GetSurfaceRoadInfo(DRIVER2_ROAD_INFO* outRoadInfo, int surfId)
else if (IS_JUNCTION_SURFACE(surfId)) else if (IS_JUNCTION_SURFACE(surfId))
{ {
junction = GET_JUNCTION(surfId); junction = GET_JUNCTION(surfId);
outRoadInfo->ConnectIdx = &junction->ExitIdx; outRoadInfo->ConnectIdx = junction->ExitIdx;
} }
return 0; return 0;

View File

@ -12,20 +12,25 @@
#define GET_CURVE(surfid) (Driver2CurvesPtr + ((surfid) & 0x1FFF)) #define GET_CURVE(surfid) (Driver2CurvesPtr + ((surfid) & 0x1FFF))
#define GET_JUNCTION(surfid) (Driver2JunctionsPtr + ((surfid) & 0x1FFF)) #define GET_JUNCTION(surfid) (Driver2JunctionsPtr + ((surfid) & 0x1FFF))
// AI lanes - each bit represents lane being enabled for Civ AI to be driven
// Lane dirs - each bit represents lane direction invertion
// NumLanes - | Lane count (4 bits - max 15) | speed limit id (2 bits - max 3) | Leftmost Lane parking | Rightmost Lane parking
// $DEPRECATED: as it's used to detect lane direction, use ROAD_LANE_DIR instead // $DEPRECATED: as it's used to detect lane direction, use ROAD_LANE_DIR instead
#define IS_NARROW_ROAD(rd) \ #define IS_NARROW_ROAD(rd) \
((*(ushort*)&(rd)->NumLanes & 0xFFFF) == 0xFF01) ((*(ushort*)&(rd)->NumLanes & 0xFFFF) == 0xFF01)
// those macros can be applied to straights and junctions // those macros can be applied to straights and junctions
#define ROAD_LANES_COUNT(rd) ((u_int)(rd)->NumLanes & 0xF) // lane count #define ROAD_IS_AI_LANE(rd, lane) ((u_char)(rd)->AILanes >> ((lane) / 2) & 1U) // lane AI driveable flag
#define ROAD_WIDTH_IN_LANES(rd) (ROAD_LANES_COUNT(rd) * 2) // width in lanes #define ROAD_LANE_DIRECTION_BIT(rd, lane) ((u_char)(rd)->LaneDirs >> ((lane) / 2) & 1U) // direction bit
#define ROAD_IS_AI_LANE(rd, lane) ((u_char)(rd)->AILanes >> ((lane) / 2) & 1U) // lane AI driveable flag
#define ROAD_IS_LEFTMOST_LANE_PARKING(rd) (((u_char)(rd)->NumLanes & 0x40) != 0) // allows parking on leftmost lane
#define ROAD_IS_RIGHTMOST_LANE_PARKING(rd) (((u_char)(rd)->NumLanes & 0x80) != 0) // allows parking on rightmost lane
#define ROAD_LANE_DIRECTION_BIT(rd, lane) ((u_char)(rd)->LaneDirs >> ((lane) / 2) & 1U) // direction bit
#define ROAD_SPEED_LIMIT(rd) (((u_char)(rd)->NumLanes >> 4) & 3) // speed limit id
#define ROAD_HAS_FAST_LANES(rd) (((u_char)(rd)->NumLanes >> 6) & 1) // & 0x20; in fact speed limit check too
#define ROAD_LANES_COUNT(rd) ((u_int)(rd)->NumLanes & 15) // lane count
#define ROAD_WIDTH_IN_LANES(rd) (ROAD_LANES_COUNT(rd) * 2) // width in lanes
#define ROAD_IS_LEFTMOST_LANE_PARKING(rd) (((u_char)(rd)->NumLanes & 0x40) != 0) // bit 7 allows parking on leftmost lane
#define ROAD_IS_RIGHTMOST_LANE_PARKING(rd) (((u_char)(rd)->NumLanes & 0x80) != 0) // bit 8 allows parking on rightmost lane
#define ROAD_SPEED_LIMIT(rd) (((u_char)(rd)->NumLanes >> 4) & 3) // speed limit id
#define ROAD_LANE_DIR(rd, lane) \ #define ROAD_LANE_DIR(rd, lane) \
(((u_char)(rd)->LaneDirs == 0xFF && (rd)->NumLanes == 1) ? ((lane) & 1) : ROAD_LANE_DIRECTION_BIT(rd, lane)) (((u_char)(rd)->LaneDirs == 0xFF && (rd)->NumLanes == 1) ? ((lane) & 1) : ROAD_LANE_DIRECTION_BIT(rd, lane))
@ -38,7 +43,7 @@ struct DRIVER2_ROAD_INFO
{ {
int surfId; int surfId;
short (*ConnectIdx)[4]; short* ConnectIdx; // [4]
char NumLanes; char NumLanes;
char LaneDirs; char LaneDirs;
char AILanes; char AILanes;

View File

@ -4324,8 +4324,8 @@ int StopUserMissionEvents(MR_THREAD *thread)
int RunUserMissionEvents(MR_THREAD *thread) int RunUserMissionEvents(MR_THREAD *thread)
{ {
if (gCurrentMissionNumber >= 50 && gCurrentMissionNumber <= 65) //if (gCurrentMissionNumber >= 50 && gCurrentMissionNumber <= 65)
maxCopCars = 32; // maxCopCars = 32;
return (*fnMissionEventHandlers)[GameLevel](thread, 0); return (*fnMissionEventHandlers)[GameLevel](thread, 0);
} }

View File

@ -262,10 +262,11 @@ void FixCarCos(CAR_COSMETICS* carCos, int externalModelNumber)
car_cosmetics[2].mass *= 3; car_cosmetics[2].mass *= 3;
} }
// [A] vegas box truck // [A] wibbly wobbly fuckery hacks...
if (GameLevel == 2 && externalModelNumber == 10) // flag certain cars that have a tendency to go CRAZY at a stand-still
if (GameLevel == 2 && externalModelNumber == 10) // vegas box truck
{ {
carCos->extraInfo |= 4; // wibbly wobbly fuckery hack... carCos->extraInfo |= 4;
} }
} }
@ -334,6 +335,22 @@ void GlobalTimeStep(void)
st = &cp->st; st = &cp->st;
// [A] bugfix: wibbly wobbly cars
// (see end of FixCarCos for cars that have this flag)
if (car_cosmetics[cp->ap.model].extraInfo & 4)
{
if (cp->handbrake && cp->wasOnGround && cp->hd.speed < 3)
{
cp->hd.aacc[0] >>= 1;
cp->hd.aacc[1] >>= 1;
cp->hd.aacc[2] >>= 1;
cp->hd.acc[0] >>= 1;
cp->hd.acc[1] >>= 1;
cp->hd.acc[2] >>= 1;
}
}
st->n.linearVelocity[0] += cp->hd.acc[0]; st->n.linearVelocity[0] += cp->hd.acc[0];
st->n.linearVelocity[1] += cp->hd.acc[1]; st->n.linearVelocity[1] += cp->hd.acc[1];
st->n.linearVelocity[2] += cp->hd.acc[2]; st->n.linearVelocity[2] += cp->hd.acc[2];

View File

@ -383,7 +383,7 @@ void SetupResidentModels()
// force palette // force palette
if (singlePal) if (singlePal)
PlayerStartInfo[i]->palette = 0; PlayerStartInfo[i]->palette = 0;
else else if (wantedColour[i] != -1)
PlayerStartInfo[i]->palette = wantedColour[i]; PlayerStartInfo[i]->palette = wantedColour[i];
// store for replay if necessary // store for replay if necessary
@ -1280,7 +1280,7 @@ void HandleTimer(MR_TIMER *timer)
// [D] [T] // [D] [T]
void RegisterChaseHit(int car1, int car2) void RegisterChaseHit(int car1, int car2)
{ {
if (!Mission.ChaseTarget || Mission.ChaseHitDelay == 0) if (!Mission.ChaseTarget || Mission.ChaseHitDelay != 0)
return; return;
int player_id; int player_id;

View File

@ -1719,7 +1719,7 @@ int DrawCharacter(LPPEDESTRIAN pPed)
if (pUsedPeds->pNext == NULL && pPed->pedType == TANNER_MODEL) if (pUsedPeds->pNext == NULL && pPed->pedType == TANNER_MODEL)
#else #else
// draw a nice shadow for non-civilian peds :) // draw a nice shadow for non-civilian peds :)
if (pPed->pedType != CIVILIAN) if (pPed->pedType == TANNER_MODEL || pPed->pedType == OTHER_MODEL)
#endif #endif
{ {
v.vx = (pPed->position.vx - camera_position.vx) + Skel[ROOT].pvOrigPos->vx; v.vx = (pPed->position.vx - camera_position.vx) + Skel[ROOT].pvOrigPos->vx;

View File

@ -1001,7 +1001,12 @@ void DrawMultiplayerMap(void)
map_z_offset = 0; map_z_offset = 0;
xPos = gMapXOffset; xPos = gMapXOffset;
#ifndef PSX
// [A] add room for speedometer
yPos = gMapYOffset - 4;
#else
yPos = gMapYOffset; yPos = gMapYOffset;
#endif
DrawMultiplayerTargets(); DrawMultiplayerTargets();

View File

@ -33,6 +33,7 @@ struct XZDIR
short dx, dz; short dx, dz;
}; };
// Fast Marching method
ushort distanceCache[16384]; ushort distanceCache[16384];
char omap[128][16]; // obstacle map 128x128 (bit field) char omap[128][16]; // obstacle map 128x128 (bit field)
int dunyet[32][2]; // scanned cell map (32x32, multi-level bitfield) int dunyet[32][2]; // scanned cell map (32x32, multi-level bitfield)
@ -147,7 +148,7 @@ void DebugDisplayObstacleMap()
n.vx = player[0].pos[0]; n.vx = player[0].pos[0];
n.vz = player[0].pos[2]; n.vz = player[0].pos[2];
n.vy = pos_y; n.vy = pos_y;
n.vy = MapHeight((VECTOR*)&n); //n.vy = MapHeight((VECTOR*)&n);
for (int i = 0; i < 128; i++) for (int i = 0; i < 128; i++)
{ {
@ -179,7 +180,7 @@ void DebugDisplayObstacleMap()
n.vx = px << 8; n.vx = px << 8;
n.vz = pz << 8; n.vz = pz << 8;
n.vy = MapHeight((VECTOR*)&n); n.vy = 0;// MapHeight((VECTOR*)&n);
int dist = distanceCache[(n.vx >> 2 & 0x3f80U | n.vz >> 9 & 0x7fU) ^ (n.vy & 1U) * 0x2040 ^ (n.vy & 2U) << 0xc];// distanceCache[((pos_x+i & 127) * 128) + (j + pos_z & 127)]; int dist = distanceCache[(n.vx >> 2 & 0x3f80U | n.vz >> 9 & 0x7fU) ^ (n.vy & 1U) * 0x2040 ^ (n.vy & 2U) << 0xc];// distanceCache[((pos_x+i & 127) * 128) + (j + pos_z & 127)];
@ -270,9 +271,9 @@ void WunCell(VECTOR* pbase)
int i, j; int i, j;
#if ENABLE_GAME_FIXES #if ENABLE_GAME_FIXES
// [A] hack with height map (fixes some bits in Havana) // start with the base (player) height
height1 = MapHeight(pbase); height1 = pbase->vy;
pbase->vx += 512; pbase->vx += 512;
pbase->vz += 512; pbase->vz += 512;
@ -281,7 +282,9 @@ void WunCell(VECTOR* pbase)
pbase->vx -= 512; pbase->vx -= 512;
pbase->vz -= 512; pbase->vz -= 512;
if (height1 - v[0].vy > 100) // if base height differs from map height too much (we are on bridge etc)
// we then use base height to ensure that obstacles are locally correct
if (ABS(height1 - v[0].vy) > 100)
v[0].vy = height1; v[0].vy = height1;
v[0].vy += 32; v[0].vy += 32;
@ -300,6 +303,17 @@ void WunCell(VECTOR* pbase)
} }
} }
#else #else
pbase->vx = pbase->vx + 512;
pbase->vz = pbase->vz + 512;
height1 = MapHeight(pbase);
v[0].vy = height1 + 60;
pbase->vx = pbase->vx - 512;
pbase->vz = pbase->vz - 512;
v[1].vy = v[0].vy;
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
{ {
if (i != 0) if (i != 0)
@ -321,14 +335,10 @@ void WunCell(VECTOR* pbase)
dz = v[0].vz + v[1].vz >> 1; dz = v[0].vz + v[1].vz >> 1;
OMapSet(dx >> 8, dz >> 8, lineClear(&v[0], &v[1]) == 0); OMapSet(dx >> 8, dz >> 8, lineClear(&v[0], &v[1]) == 0);
j++;
} }
if (i != 0) if (i != 0)
pbase->vx -= 512; pbase->vx -= 512;
i++;
} }
#endif #endif
} }
@ -501,9 +511,15 @@ int blocked(tNode* v1, tNode* v2)
if (slowWallTests != 0) if (slowWallTests != 0)
return lineClear((VECTOR*)v1, (VECTOR*)v2) == 0; return lineClear((VECTOR*)v1, (VECTOR*)v2) == 0;
x = v1->vx + v1->vx >> 9; x = v1->vx + v2->vx >> 9;
z = v1->vz + v2->vz >> 9; z = v1->vz + v2->vz >> 9;
int prev = DONEMAP_V(x >> 2, z >> 2);
int val = DONEMAP_GETVALUE(x >> 2, z >> 2, prev, 0);
if (val != 0)
return 1;
return OMAP_GETVALUE(x, z); return OMAP_GETVALUE(x, z);
} }
@ -516,10 +532,10 @@ void setDistance(tNode* n, ushort dist)
} }
// [A] // [A]
void SetNodeDistanceWithParents(tNode* startNode, ushort dist); void pushNode(tNode* startNode, ushort dist);
// [D] [T] // [D] [T]
void iterate(void) int iterate(void)
{ {
tNode pathNodes[8]; tNode pathNodes[8];
@ -536,16 +552,14 @@ void iterate(void)
int r; int r;
if (numHeapEntries == 0) if (numHeapEntries == 0)
return; return 0;
popNode(&itHere); popNode(&itHere);
nbr = pathNodes; nbr = pathNodes;
// check directions // check directions
for(dir = 0; dir < 6; dir++) for(dir = 0; dir < 6; dir++, nbr++)
{ {
nbr++;
nbr->vx = itHere.vx + dirs[dir].dx; nbr->vx = itHere.vx + dirs[dir].dx;
nbr->vy = itHere.vy; nbr->vy = itHere.vy;
nbr->vz = itHere.vz + dirs[dir].dz; nbr->vz = itHere.vz + dirs[dir].dz;
@ -560,9 +574,7 @@ void iterate(void)
{ {
if (ABS(nbr->vy - itHere.vy) < 201) if (ABS(nbr->vy - itHere.vy) < 201)
{ {
if ((dist & 1) == 0) nbr->dist = 0;
nbr->dist = 0;
continue; continue;
} }
} }
@ -581,18 +593,18 @@ void iterate(void)
// now we have distance let's compute the rest of the map // now we have distance let's compute the rest of the map
for(dir = 0; dir < 6; dir++) for(dir = 0; dir < 6; dir++)
{ {
if (pathNodes[dir + 1].dist != 0) if (pathNodes[dir].dist != 0)
continue; continue;
if (dir != 5) if (dir != 5)
nr = pathNodes[dir + 2].dist; nr = pathNodes[dir + 1].dist;
else else
nr = pathNodes[1].dist; nr = pathNodes[0].dist;
if (dir != 0) if (dir != 0)
nl = pathNodes[dir].dist; nl = pathNodes[dir - 1].dist;
else else
nl = pathNodes[6].dist; nl = pathNodes[5].dist;
// uhhmm... distance function selection? // uhhmm... distance function selection?
if (nl < 2) if (nl < 2)
@ -627,8 +639,10 @@ void iterate(void)
} }
} }
SetNodeDistanceWithParents(&pathNodes[dir + 1], dist); pushNode(&pathNodes[dir], dist);
} }
return numHeapEntries > 0;
} }
// [D] [T] // [D] [T]
@ -853,7 +867,7 @@ void addCivs(void)
} }
// [A] // [A]
void SetNodeDistanceWithParents(tNode* startNode, ushort dist) void pushNode(tNode* startNode, ushort dist)
{ {
int i; int i;
u_int pnode, parent; u_int pnode, parent;
@ -863,6 +877,7 @@ void SetNodeDistanceWithParents(tNode* startNode, ushort dist)
setDistance(startNode, dist); setDistance(startNode, dist);
// up heap
i = numHeapEntries + 1; i = numHeapEntries + 1;
pnode = i; pnode = i;
@ -881,7 +896,7 @@ void SetNodeDistanceWithParents(tNode* startNode, ushort dist)
} }
// [A] // [A]
void ComputeDistanceFromSearchTarget(tNode* startNode) void pushSeedNode(tNode* startNode)
{ {
u_short dist; u_short dist;
int i, dx, dz; int i, dx, dz;
@ -894,7 +909,7 @@ void ComputeDistanceFromSearchTarget(tNode* startNode)
dist = SquareRoot0( dx * dx + dz * dz ) >> 1; dist = SquareRoot0( dx * dx + dz * dz ) >> 1;
SetNodeDistanceWithParents(startNode, dist); pushNode(startNode, dist);
} }
// [D] [T] // [D] [T]
@ -939,14 +954,21 @@ void UpdateCopMap(void)
i = 36; i = 36;
while (--i >= 0) while (--i >= 0)
iterate(); {
if (!iterate())
{
pathFrames = 0;
break;
}
}
DebugDisplayObstacleMap(); DebugDisplayObstacleMap();
// remove cars // remove cars
addCivs(); addCivs();
} }
else
if(pathFrames == 0)
{ {
// restart from new search target position // restart from new search target position
if (player[0].playerType == 1 && (CopsCanSeePlayer != 0 || numActiveCops == 0)) if (player[0].playerType == 1 && (CopsCanSeePlayer != 0 || numActiveCops == 0))
@ -1001,30 +1023,30 @@ void UpdateCopMap(void)
if (dz < dx + dz / 2) if (dz < dx + dz / 2)
{ {
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
startNode.vx += 256; startNode.vx += 256;
startNode.vz += 512; startNode.vz += 512;
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
startNode.vx += 256; startNode.vx += 256;
startNode.vz -= 512; startNode.vz -= 512;
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
} }
else else
{ {
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
startNode.vx += 256; startNode.vx += 256;
startNode.vz += 512; startNode.vz += 512;
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
startNode.vx -= 512; startNode.vx -= 512;
ComputeDistanceFromSearchTarget(&startNode); pushSeedNode(&startNode);
} }
} }

View File

@ -1282,7 +1282,7 @@ void SetupGetInCar(LPPEDESTRIAN pPed)
// HEY! // HEY!
CreatePedAtLocation(&pos, 8); CreatePedAtLocation(&pos, 8);
Start3DSoundVolPitch(-1, SOUND_BANK_TANNER, sample, pos[0], pos[1], pos[2], 0, 4096); Start3DSoundVolPitch(-1, SOUND_BANK_TANNER, 5, pos[0], pos[1], pos[2], 0, 4096);
carToGetIn->controlFlags |= CONTROL_FLAG_WAS_PARKED; carToGetIn->controlFlags |= CONTROL_FLAG_WAS_PARKED;
} }

View File

@ -285,50 +285,11 @@ int LoadReplayFromBuffer(char *buffer)
return 1; return 1;
} }
#ifndef PSX
int LoadUserAttractReplay(int mission, int userId)
{
char customFilename[64];
if (userId >= 0 && userId < gNumUserChases)
{
sprintf(customFilename, "REPLAYS\\User\\%s\\ATTRACT.%d", gUserReplayFolderList[userId], mission);
if (FileExists(customFilename))
{
if (Loadfile(customFilename, (char*)_other_buffer))
return LoadReplayFromBuffer((char*)_other_buffer);
}
}
return 0;
}
#endif
// [D] [T] // [D] [T]
int LoadAttractReplay(int mission) int LoadAttractReplay(int mission)
{ {
char filename[32]; char filename[32];
#ifndef PSX
int userId = -1;
// [A] REDRIVER2 PC - custom attract replays
if (gNumUserChases)
{
userId = rand() % (gNumUserChases + 1);
if (userId == gNumUserChases)
userId = -1;
}
if (LoadUserAttractReplay(mission, userId))
{
printInfo("Loaded custom attract replay (%d) by %s\n", mission, gUserReplayFolderList[userId]);
return 1;
}
#endif
sprintf(filename, "REPLAYS\\ATTRACT.%d", mission); sprintf(filename, "REPLAYS\\ATTRACT.%d", mission);
if (!FileExists(filename)) if (!FileExists(filename))

View File

@ -213,12 +213,15 @@ void AddWheelForcesDriver1(CAR_DATA* cp, CAR_LOCALS* cl)
int player_id; int player_id;
int oldCutRoughness; int oldCutRoughness;
oldSpeed = cp->hd.speed * 3 >> 1; // NB: skips precision every 3rd increment or so
int speed = cp->hd.speed * 3 >> 1;
if (oldSpeed < 32) if (speed < 32)
oldSpeed = oldSpeed * -72 + 3696; oldSpeed = 4096 - (72 * speed);
else else
oldSpeed = 1424 - oldSpeed; oldSpeed = 1824 - speed;
oldSpeed -= 400;
dir = cp->hd.direction; dir = cp->hd.direction;
cdx = RSIN(dir); cdx = RSIN(dir);
@ -528,32 +531,6 @@ void AddWheelForcesDriver1(CAR_DATA* cp, CAR_LOCALS* cl)
else else
{ {
cp->hd.wheel_speed = cdz / 64 * (cl->vel[2] / 64) + cdx / 64 * (cl->vel[0] / 64); cp->hd.wheel_speed = cdz / 64 * (cl->vel[2] / 64) + cdx / 64 * (cl->vel[0] / 64);
// [A] wibbly wobbly fuckery hack...
if (car_cos->extraInfo & 4)
{
if (cp->thrust == 0 && cp->handbrake && (cp->hd.speed > 0 && cp->hd.speed < 4))
{
cp->hd.speed--;
cp->hd.wheel_speed >>= 1;
cp->hd.acc[0] >>= 1;
cp->hd.acc[1] >>= 1;
cp->hd.acc[2] >>= 1;
cp->hd.aacc[0] >>= 2;
cp->hd.aacc[1] >>= 2;
cp->hd.aacc[2] >>= 2;
//cp->st.n.linearVelocity[0] >>= 1;
//cp->st.n.linearVelocity[1] >>= 1;
//cp->st.n.linearVelocity[2] >>= 1;
cp->st.n.angularVelocity[0] >>= 2;
cp->st.n.angularVelocity[1] >>= 2;
cp->st.n.angularVelocity[2] >>= 2;
}
}
} }
} }

View File

@ -16,6 +16,9 @@
#define M_BIT(x) (1 << (x)) #define M_BIT(x) (1 << (x))
// makes RGB int out of three bytes
#define M_INT_RGB(r,g,b) (((r) << 16) | ((g) << 8) | (b))
//--------------------------------------- //---------------------------------------
#if !defined( __TYPEINFOGEN__ ) && !defined( _lint ) && defined(_WIN32) // pcLint has problems with assert_offsetof() #if !defined( __TYPEINFOGEN__ ) && !defined( _lint ) && defined(_WIN32) // pcLint has problems with assert_offsetof()

@ -1 +1 @@
Subproject commit 8fa92c7fba6685c0e9f41804c2f5b68dee4b87e7 Subproject commit a04825ea2c1c5c5eecac83ed97cb637d3cab8928

View File

@ -565,12 +565,9 @@ int main(int argc, char** argv)
{ {
int newScrZ = gCameraDefaultScrZ; int newScrZ = gCameraDefaultScrZ;
const char* dataFolderStr = ini_get(config, "fs", "dataFolder"); const char* dataFolderStr = ini_get(config, "fs", "dataFolder");
const char* userReplaysStr = ini_get(config, "game", "userChases");
if(!cdImageFileName) if(!cdImageFileName)
cdImageFileName = ini_get(config, "cdfs", "image"); cdImageFileName = ini_get(config, "cdfs", "image");
InitUserReplays(userReplaysStr);
// configure Psy-X pads // configure Psy-X pads
ini_sget(config, "pad", "pad1device", "%d", &g_cfg_controllerToSlotMapping[0]); ini_sget(config, "pad", "pad1device", "%d", &g_cfg_controllerToSlotMapping[0]);