643 lines
21 KiB
C++
643 lines
21 KiB
C++
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE : recklessdriving.cpp
|
|
// PURPOSE : Some code to detect the player driving recklessly.
|
|
// AUTHOR : Obbe.
|
|
// CREATED : 2/8/06
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef _RECKLESSDRIVING_H_
|
|
#include "control/recklessdriving.h"
|
|
#endif
|
|
|
|
// rage includes
|
|
#include "grcore/debugdraw.h"
|
|
#include "fwvehicleai/pathfindtypes.h"
|
|
|
|
// game includes
|
|
#include "ai/stats.h"
|
|
#include "peds/ped.h"
|
|
#include "peds/PlayerSpecialAbility.h"
|
|
#include "control/trafficlights.h"
|
|
#include "vehicleai/pathfind.h"
|
|
#include "vehicles/bike.h"
|
|
#include "physics/gtamaterial.h"
|
|
#include "physics/gtamaterialmanager.h"
|
|
#include "modelinfo/vehiclemodelinfo.h"
|
|
#include "stats/StatsMgr.h"
|
|
|
|
using namespace AIStats;
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_VEHICLE_OPTIMISATIONS()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : CPlayerInfo::UpdateRecklessDriving
|
|
// PURPOSE : Detects whether the player has been performing dangerous driving
|
|
// and sets corresponding variables.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
const float sfPlayerRunTrafficLightSpeed = 5.0f;
|
|
|
|
void CPlayerInfo::UpdateRecklessDriving()
|
|
{
|
|
PF_FUNC(UpdateRecklessDriving);
|
|
|
|
Assert(m_pPlayerPed);
|
|
|
|
bool bNodeSearchDone = false;
|
|
|
|
// We can only be accused of reckless driving if we're actually driving a vehicle
|
|
if(m_pPlayerPed->GetMyVehicle())
|
|
{
|
|
m_pPlayerPed->GetMyVehicle()->m_nVehicleFlags.bShouldBeBeepedAt = false;
|
|
m_pPlayerPed->GetMyVehicle()->m_nVehicleFlags.bPlayerDrivingAgainstTraffic = false;
|
|
}
|
|
|
|
if( m_pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) &&
|
|
m_pPlayerPed->GetMyVehicle() &&
|
|
(m_pPlayerPed->GetMyVehicle()->GetDriver() == m_pPlayerPed) &&
|
|
((m_pPlayerPed->GetMyVehicle()->GetVehicleType() == VEHICLE_TYPE_CAR) || (m_pPlayerPed->GetMyVehicle()->GetVehicleType() == VEHICLE_TYPE_BIKE)|| (m_pPlayerPed->GetMyVehicle()->GetVehicleType() == VEHICLE_TYPE_QUADBIKE) || (m_pPlayerPed->GetMyVehicle()->GetVehicleType() == VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE)))
|
|
{
|
|
CVehicle* pPlayerVeh = m_pPlayerPed->GetMyVehicle();
|
|
Assert(pPlayerVeh);
|
|
|
|
m_bPlayerOnWideRoad = false;
|
|
|
|
const Vector3& vPlayerVehVelocity = pPlayerVeh->GetVelocity();
|
|
if(vPlayerVehVelocity.Mag2() > sfPlayerRunTrafficLightSpeed*sfPlayerRunTrafficLightSpeed)
|
|
{
|
|
CNodeAddress Node1, Node2;
|
|
s16 regionOut = -1;
|
|
s8 nearestLane = 0;
|
|
|
|
// First find the nearest node segment.
|
|
bNodeSearchDone = true;
|
|
m_bPlayerOnHighway = false;
|
|
m_PlayerRoadNode = EmptyNodeAddress;
|
|
m_iPlayerRoadLinkLocalIndex = -1;
|
|
|
|
// check for drifting
|
|
float totalSideSlip = 0.f;
|
|
for (s32 i = 0; i < pPlayerVeh->GetNumWheels(); ++i)
|
|
{
|
|
CWheel* wheel = pPlayerVeh->GetWheel(i);
|
|
totalSideSlip += wheel->GetSideSlipAngle();
|
|
}
|
|
if (totalSideSlip > 5.f)
|
|
{
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_DRIFTING, m_pPlayerPed, totalSideSlip);
|
|
}
|
|
|
|
const Vector3 vPlayerVehPosition = VEC3V_TO_VECTOR3(pPlayerVeh->GetVehiclePosition());
|
|
const Vector3 vPlayerVehForwardDir = VEC3V_TO_VECTOR3(pPlayerVeh->GetVehicleForwardDirection());
|
|
|
|
if(ThePaths.FindNodePairToMatchVehicle(vPlayerVehPosition, vPlayerVehForwardDir, Node1, Node2, false, regionOut, nearestLane, DEFAULT_REJECT_JOIN_TO_ROAD_DIST, false, m_RecklessDrivingHintFrom, m_RecklessDrivingHintTo))
|
|
{
|
|
m_RecklessDrivingHintFrom = Node1;
|
|
m_RecklessDrivingHintTo = Node2;
|
|
}
|
|
else
|
|
{
|
|
m_RecklessDrivingHintFrom.SetEmpty();
|
|
m_RecklessDrivingHintTo.SetEmpty();
|
|
}
|
|
|
|
if(!Node1.IsEmpty())// We actually found some node.
|
|
{
|
|
Vector3 Coors1;
|
|
const CPathNode* pNode1 = ThePaths.FindNodePointer(Node1);
|
|
Assert(pNode1);
|
|
if (pNode1->IsHighway())
|
|
{
|
|
m_bPlayerOnHighway = true;
|
|
}
|
|
pNode1->GetCoors(Coors1);
|
|
|
|
Vector3 Coors2;
|
|
Assert(!Node2.IsEmpty());
|
|
const CPathNode* pNode2 = ThePaths.FindNodePointer(Node2);
|
|
Assert(pNode2);
|
|
pNode2->GetCoors(Coors2);
|
|
|
|
#if __BANK
|
|
// Display the line we have found
|
|
if (ms_bDisplayRecklessDriving)
|
|
{
|
|
grcDebugDraw::Line(Coors1+Vector3(0.0f, 0.0f, 1.0f), Coors2+Vector3(0.0f, 0.0f, 1.0f), Color32( 255, 255, 255, 255 ));
|
|
//CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(Coors1+Vector3(0.0f, 0.0f, 1.0f)), VECTOR3_TO_VEC3V(Coors2+Vector3(0.0f, 0.0f, 1.0f)), Color_white);
|
|
}
|
|
#endif // __BANK
|
|
|
|
// Make sure we travel from node1 to node2
|
|
Vector3 linkDir = Coors2 - Coors1;
|
|
linkDir.Normalize();
|
|
float Velocity = linkDir.Dot(vPlayerVehVelocity);
|
|
if (Velocity < 0.0f)
|
|
{
|
|
CNodeAddress Temp = Node1;
|
|
Node1 = Node2;
|
|
Node2 = Temp;
|
|
|
|
const CPathNode * pTmpNode = pNode1;
|
|
pNode1 = pNode2;
|
|
pNode2 = pTmpNode;
|
|
|
|
Vector3 TempV = Coors1;
|
|
Coors1 = Coors2;
|
|
Coors2 = TempV;
|
|
|
|
linkDir = -linkDir;
|
|
Velocity = -Velocity;
|
|
}
|
|
|
|
// We have to recalculate the link as the nodes may have switched round.
|
|
s16 iLink = -1;
|
|
const bool bLinkFound = ThePaths.FindNodesLinkIndexBetween2Nodes(Node1, Node2, iLink);
|
|
|
|
if(bLinkFound)
|
|
{
|
|
const CPathNodeLink* pLink = &ThePaths.GetNodesLink(pNode1, iLink);
|
|
Assert(pLink);
|
|
|
|
//save this off
|
|
m_PlayerRoadNode = Node1;
|
|
m_iPlayerRoadLinkLocalIndex = iLink;
|
|
|
|
// if (pLink->m_1.m_LanesToOtherNode + pLink->m_1.m_LanesFromOtherNode >= 3)
|
|
if (pLink->m_1.m_LanesToOtherNode >= 2 || pLink->m_1.m_LanesFromOtherNode >= 2)
|
|
{
|
|
m_bPlayerOnWideRoad = true;
|
|
}
|
|
|
|
// Is there a traffic light on this link?
|
|
// Make sure player travels at a decent speed in the direction past the traffic light
|
|
if (pNode2->IsJunctionNode() && Velocity > sfPlayerRunTrafficLightSpeed)
|
|
{
|
|
Vector3 MidPoint = (Coors1 + Coors2) * 0.5f;
|
|
if ((vPlayerVehPosition - MidPoint).Dot(linkDir) >= 0.0f)
|
|
{
|
|
if ((pPlayerVeh->GetPreviousPosition() - MidPoint).Dot(linkDir) < 0.0f)
|
|
{
|
|
const CJunction* pJunction = CJunctions::FindJunctionFromNode(Node2);
|
|
if (pJunction)
|
|
{
|
|
const s32 iEntrance = pJunction->FindEntranceIndexWithNode(Node1);
|
|
if (iEntrance >= 0)
|
|
{
|
|
bool bGoingRightOnRed = false;
|
|
CVehicleNodeList tempNodeList;
|
|
|
|
const s16 iRegionLinkIndex = (s16)ThePaths.GetNodesRegionLinkIndex(pNode1, iLink);
|
|
tempNodeList.Set(0, Node1, iRegionLinkIndex, nearestLane);
|
|
tempNodeList.SetPathNodeAddr(1, Node2);
|
|
tempNodeList.SetTargetNodeIndex(1);
|
|
ETrafficLightCommand iCmd = pJunction->GetTrafficLightCommand(pPlayerVeh, iEntrance, bGoingRightOnRed, false, &tempNodeList);
|
|
|
|
//we don't care about going right on red here--if the player's turning right
|
|
//we shouldn't honk at him
|
|
if (iCmd == TRAFFICLIGHT_COMMAND_STOP)
|
|
{
|
|
m_LastTimePlayerRanLight = fwTimer::GetTimeInMilliseconds();
|
|
//CCrime::ReportCrime(CRIME_RUN_REDLIGHT, NULL, m_pPlayerPed); 1 star light wanted level was confusing. Removed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Work out whether we drive against the flow of traffic.
|
|
if (Velocity > 5.0f)
|
|
{ // Make sure we go at a decent speed.
|
|
// Make sure car is sufficiently alligned with road (don't want to trigger on side roads)
|
|
if (rage::Abs(DotProduct(linkDir, VEC3V_TO_VECTOR3(pPlayerVeh->GetTransform().GetB()))) > 0.7f)
|
|
{
|
|
if (pLink->m_1.m_LanesToOtherNode == 0 && pLink->m_1.m_LanesFromOtherNode != 0)
|
|
{
|
|
m_LastTimePlayerDroveAgainstTraffic = fwTimer::GetTimeInMilliseconds();
|
|
// CCrime::ReportCrime(CRIME_DRIVE_AGAINST_TRAFFIC, NULL, m_pPlayerPed); 1 star light wanted level was confusing. Removed
|
|
// HaveSomeoneBeepAtPlayer();
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_DRIVING_TOWARDS_ONCOMING, m_pPlayerPed);
|
|
}
|
|
else if (pLink->m_1.m_LanesFromOtherNode != 0 && !pLink->IsSingleTrack())
|
|
{ // If we are to the left of the center line we go against traffic.
|
|
Vector3 SideVec;
|
|
SideVec.x = linkDir.y;
|
|
SideVec.y = -linkDir.x;
|
|
SideVec.z = 0.0f;
|
|
SideVec.Normalize();
|
|
float SideDist = (vPlayerVehPosition - Coors1).Dot(SideVec);
|
|
if (SideDist < -2.5f)
|
|
{
|
|
m_LastTimePlayerDroveAgainstTraffic = fwTimer::GetTimeInMilliseconds();
|
|
// CCrime::ReportCrime(CRIME_DRIVE_AGAINST_TRAFFIC, NULL, m_pPlayerPed);
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_DRIVING_TOWARDS_ONCOMING, m_pPlayerPed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Work out whether we're driving on the pavement
|
|
// can do this for any vehicle type now
|
|
bool bOnPavement = true;
|
|
const int playerVehNumWheels = pPlayerVeh->GetNumWheels();
|
|
for (s32 i=0; i<playerVehNumWheels; i++)
|
|
{
|
|
if (!pPlayerVeh->GetWheel(i)->GetIsTouching())
|
|
{
|
|
bOnPavement = false;
|
|
}
|
|
else
|
|
{
|
|
bOnPavement = bOnPavement && pPlayerVeh->GetWheel(i)->GetIsTouchingPavement();
|
|
}
|
|
}
|
|
|
|
static dev_float s_fVelThresholdForDrivingOnPavementSqr = 1.0f * 1.0f;
|
|
if (bOnPavement && vPlayerVehVelocity.Mag2() > s_fVelThresholdForDrivingOnPavementSqr)
|
|
{
|
|
m_LastTimePlayerDroveOnPavement = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
const CCollisionHistory* pCollisionHistory = pPlayerVeh->GetFrameCollisionHistory();
|
|
if(pCollisionHistory->GetNumCollidedEntities() > 0)
|
|
{
|
|
const CCollisionRecord* pCurrent;
|
|
|
|
//Buildings
|
|
if(pCollisionHistory->HasCollidedWithAnyOfTypes(ENTITY_TYPE_MASK_BUILDING | ENTITY_TYPE_MASK_ANIMATED_BUILDING))
|
|
{
|
|
m_LastTimePlayerHitBuilding = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
//Vehicles
|
|
if(pCollisionHistory->HasCollidedWithAnyOfTypes(ENTITY_TYPE_MASK_VEHICLE))
|
|
{
|
|
m_LastTimePlayerHitCar = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
//Peds
|
|
pCurrent = pCollisionHistory->GetFirstPedCollisionRecord();
|
|
while(pCurrent)
|
|
{
|
|
if(pCurrent->m_pRegdCollisionEntity.Get() != static_cast<CEntity*>(m_pPlayerPed.Get()))
|
|
m_LastTimePlayerHitPed = fwTimer::GetTimeInMilliseconds();
|
|
|
|
pCurrent = pCurrent->GetNext();
|
|
}
|
|
|
|
//Objects
|
|
pCurrent = pCollisionHistory->GetFirstObjectCollisionRecord();
|
|
while(pCurrent)
|
|
{
|
|
CObject* pObject = (CObject*)pCurrent->m_pRegdCollisionEntity.Get();
|
|
|
|
if(pObject)
|
|
{
|
|
// Make sure object is of a certain size
|
|
if (Verifyf(pObject->GetModelIndex() != fwModelId::MI_INVALID, "Collided entity has an invalid model index"))
|
|
{
|
|
CBaseModelInfo* pModelInfo = pObject->GetBaseModelInfo();
|
|
Assert(pModelInfo);
|
|
if (pModelInfo->GetBoundingBoxMax().z - pModelInfo->GetBoundingBoxMin().z > 1.5f)
|
|
{
|
|
m_LastTimePlayerHitObject = fwTimer::GetTimeInMilliseconds();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pCurrent = pCurrent->GetNext();
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_INT(RECKLESS_DRIVING, HonkForRunningLightsWithin, 2000, 0, 100000, 1);
|
|
TUNE_GROUP_INT(RECKLESS_DRIVING, HonkForDriveOnPavementWithin, 500, 0, 100000, 1);
|
|
//TUNE_GROUP_INT(RECKLESS_DRIVING, HonkForHitAnotherVehicleWithin, 500, 0, 100000, 1);
|
|
if (fwTimer::GetTimeInMilliseconds()-m_LastTimePlayerRanLight < HonkForRunningLightsWithin ||
|
|
fwTimer::GetTimeInMilliseconds()-m_LastTimePlayerDroveOnPavement < HonkForDriveOnPavementWithin)
|
|
{
|
|
m_pPlayerPed->GetMyVehicle()->m_nVehicleFlags.bShouldBeBeepedAt = true;
|
|
}
|
|
|
|
TUNE_GROUP_INT(RECKLESS_DRIVING, HonkForDriveAgainstTrafficWithin, 2000, 0, 100000, 1);
|
|
if (fwTimer::GetTimeInMilliseconds()-m_LastTimePlayerDroveAgainstTraffic < HonkForDriveAgainstTrafficWithin)
|
|
{
|
|
m_pPlayerPed->GetMyVehicle()->m_nVehicleFlags.bPlayerDrivingAgainstTraffic = true;
|
|
}
|
|
}
|
|
|
|
// If we haven't found the nearest node yet we will once in a while do a node scan to find out whether the player is on a highway (this reduces
|
|
// the ped population by 50%)
|
|
if(!bNodeSearchDone && ((fwTimer::GetSystemFrameCount() & 127) == 95))
|
|
{
|
|
CNodeAddress Node1, Node2;
|
|
s16 regionOut = -1;
|
|
s8 nearestLane = 0;
|
|
m_bPlayerOnHighway = false;
|
|
m_PlayerRoadNode = EmptyNodeAddress;
|
|
m_iPlayerRoadLinkLocalIndex = -1;
|
|
|
|
const Vector3 vPlayerPosition = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
const Vector3 vPlayerForwardDir = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetForward());
|
|
|
|
//ThePaths.FindNodePairWithLineClosestToCoors(VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition()), &Node1, &Node2, &Orientation, 0.0f, 15.0f, false, false, 0);
|
|
ThePaths.FindNodePairToMatchVehicle(vPlayerPosition, vPlayerForwardDir, Node1, Node2, false, regionOut, nearestLane);
|
|
if (!Node1.IsEmpty())
|
|
{
|
|
const CPathNode* pNode1 = ThePaths.FindNodePointer(Node1);
|
|
if (pNode1->IsHighway())
|
|
{
|
|
m_bPlayerOnHighway = true;
|
|
}
|
|
|
|
m_PlayerRoadNode = Node1;
|
|
m_iPlayerRoadLinkLocalIndex = regionOut - pNode1->m_startIndexOfLinks;
|
|
}
|
|
}
|
|
|
|
// Test whether there are nodes at our height. If not we should drop the height requirement when creating vehicles.
|
|
if((fwTimer::GetSystemFrameCount() & 127) == 27)
|
|
{
|
|
m_bNodesNearbyInZ = ThePaths.IsNodeNearbyInZ(VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition()));
|
|
}
|
|
|
|
// Print out some debug stuff.
|
|
#if __BANK
|
|
if (ms_bDisplayRecklessDriving)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Time since player hit car: %d", GetTimeSincePlayerHitCar());
|
|
grcDebugDraw::AddDebugOutput("Time since player hit ped: %d", GetTimeSincePlayerHitPed());
|
|
grcDebugDraw::AddDebugOutput("Time since player hit building: %d", GetTimeSincePlayerHitBuilding());
|
|
grcDebugDraw::AddDebugOutput("Time since player hit object: %d", GetTimeSincePlayerHitObject());
|
|
grcDebugDraw::AddDebugOutput("Time since player drove on pavement: %d", GetTimeSincePlayerDroveOnPavement());
|
|
grcDebugDraw::AddDebugOutput("Time since player ran light: %d", GetTimeSincePlayerRanLight());
|
|
grcDebugDraw::AddDebugOutput("Time since player drove against traffic: %d", GetTimeSincePlayerDroveAgainstTraffic());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static const float NEAR_MISS_MARGIN = 0.9f;
|
|
bool DoBoundsIntersect(const phBound& boundA, Mat34V_In wldMtxA, const phBound& boundB, Mat34V_In wldMtxB)
|
|
{
|
|
// get the centre and half width of both bounds
|
|
Vec3V vHalfWidthsA, vHalfWidthsB;
|
|
Vec3V vCentreA, vCentreB;
|
|
boundA.GetBoundingBoxHalfWidthAndCenter(vHalfWidthsA, vCentreA);
|
|
boundB.GetBoundingBoxHalfWidthAndCenter(vHalfWidthsB, vCentreB);
|
|
|
|
// apply margin
|
|
Vec3V vMargin = Vec3VFromF32(NEAR_MISS_MARGIN);
|
|
vHalfWidthsA += vMargin;
|
|
vHalfWidthsB += vMargin;
|
|
|
|
vHalfWidthsA.SetW(ScalarV(V_ZERO));
|
|
vHalfWidthsB.SetW(ScalarV(V_ZERO));
|
|
|
|
// adjust the matrices so that they are positioned in the centre of the bounding box
|
|
Mat34V mtxA = wldMtxA;
|
|
Vec3V offsetA = Transform3x3(mtxA, vCentreA);
|
|
mtxA.SetCol3(mtxA.GetCol3() + offsetA);
|
|
|
|
Mat34V mtxB = wldMtxB;
|
|
Vec3V offsetB = Transform3x3(mtxB, vCentreB);
|
|
mtxB.SetCol3(mtxB.GetCol3() + offsetB);
|
|
|
|
// calculate matrix A relative to matrix B
|
|
Mat34V invParentMtx;
|
|
InvertTransformOrtho(invParentMtx, mtxB);
|
|
Mat34V ARelativeToBMtx;
|
|
Transform(ARelativeToBMtx, invParentMtx, mtxA);
|
|
|
|
// test if the two bounds intersect
|
|
return geomBoxes::TestBoxToBoxOBB(RCC_VECTOR3(vHalfWidthsA), RCC_VECTOR3(vHalfWidthsB), RCC_MATRIX34(ARelativeToBMtx));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : NearVehicleMiss
|
|
// PURPOSE : Records a near vehicle miss when player is driving
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPlayerInfo::NearVehicleMiss(const CVehicle* playerVehicle, const CVehicle* otherVehicle, bool trackPreciseNearMiss /* = false */)
|
|
{
|
|
// make sure it's the player info is correct
|
|
CPed* playerDriver = playerVehicle->GetDriver();
|
|
if (!playerDriver)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (playerDriver->GetPlayerInfo() != this)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static float PLAYER_MINIMUM_SPEED = 6.0f;
|
|
static float PLAYER_MINIMUM_SPEED_FOR_PRECISE = 9.0f;
|
|
|
|
// early out if player drives too slow
|
|
if(trackPreciseNearMiss)
|
|
{
|
|
if(playerVehicle->GetVelocity().Mag2() < rage::square(PLAYER_MINIMUM_SPEED_FOR_PRECISE))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(playerVehicle->GetVelocity().Mag2() < rage::square(PLAYER_MINIMUM_SPEED))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// early out if player has crashed in the last second
|
|
u32 mostRecentCollisionsTime = playerVehicle->GetFrameCollisionHistory()->GetMostRecentCollisionTime();
|
|
if ( mostRecentCollisionsTime != COLLISION_HISTORY_TIMER_NEVER_HIT
|
|
&& fwTimer::GetTimeInMilliseconds() < mostRecentCollisionsTime + CStatsMgr::NEAR_MISS_TIMER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if(!trackPreciseNearMiss)
|
|
{
|
|
//ignore trailers - parent vehicle will be the near miss and stationary cars (e.g. parked)
|
|
if(otherVehicle->GetVelocity().Mag2() <= 0.0001f || otherVehicle->InheritsFromTrailer())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(otherVehicle->GetDriver() == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// go through entire ring buffer and make sure the vehicle hasn't been added recently
|
|
u8 next = m_nearVehiclePut;
|
|
do
|
|
{
|
|
if (m_nearVehicles[next].veh == otherVehicle && m_nearVehicles[next].time + 2000 > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
return;
|
|
}
|
|
|
|
next = (next - 1) & NEAR_VEHICLE_MASK;
|
|
} while (next != m_nearVehiclePut);
|
|
|
|
// if we got this far we have a new vehicle as a near miss, or an old one that's still close
|
|
// this could trigger if we drive along side a vehicle for a long time at a high speed
|
|
Assert(!otherVehicle->GetIsInPopulationCache());
|
|
Assert(otherVehicle->GetCurrentPhysicsInst()->GetArchetype());
|
|
phBound* otherBound = otherVehicle->GetCurrentPhysicsInst()->GetArchetype()->GetBound();
|
|
|
|
Assert(playerVehicle->GetCurrentPhysicsInst()->GetArchetype());
|
|
phBound* playerBound = playerVehicle->GetCurrentPhysicsInst()->GetArchetype()->GetBound();
|
|
|
|
if (DoBoundsIntersect(*playerBound, playerVehicle->GetMatrix(), *otherBound, otherVehicle->GetMatrix()))
|
|
{
|
|
float velDiffSqr = (playerVehicle->GetVelocity() - otherVehicle->GetVelocity()).Mag2();
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_NEAR_VEHICLE_MISS, playerVehicle->GetDriver(), velDiffSqr, mostRecentCollisionsTime);
|
|
|
|
if(trackPreciseNearMiss)
|
|
{
|
|
bool hasAvoidedANewVehicle = true;
|
|
for(int i = 0; i < NUMBER_OF_DRIVERS_NEAR_MISS; i++)
|
|
{
|
|
if(playerDriver->GetPlayerInfo()->m_lastNearMissedVehicles[i] == otherVehicle)
|
|
{
|
|
hasAvoidedANewVehicle = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(hasAvoidedANewVehicle)
|
|
{
|
|
if(m_nearMissedDriverQueueIndex > NUMBER_OF_DRIVERS_NEAR_MISS - 1)
|
|
{
|
|
m_nearMissedDriverQueueIndex = 0;
|
|
}
|
|
playerDriver->GetPlayerInfo()->m_lastNearMissedVehicles[m_nearMissedDriverQueueIndex] = otherVehicle;
|
|
m_nearMissedDriverQueueIndex++;
|
|
CStatsMgr::RegisterVehicleNearMissPrecise(playerVehicle->GetDriver(), fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CStatsMgr::RegisterVehicleNearMiss(playerVehicle->GetDriver(), mostRecentCollisionsTime);
|
|
|
|
m_nearVehiclePut = (m_nearVehiclePut + 1) & NEAR_VEHICLE_MASK;
|
|
m_nearVehicles[m_nearVehiclePut].veh = otherVehicle;
|
|
m_nearVehicles[m_nearVehiclePut].time = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
static float s_fMinSpeedToSay = 20.0f;
|
|
static u32 s_uMinTimeDrivingAgainstTrafficToSay = 500;
|
|
|
|
if (GetTimeSincePlayerDroveAgainstTraffic() < s_uMinTimeDrivingAgainstTrafficToSay)
|
|
{
|
|
if (playerVehicle->GetVelocity().Mag2() >= s_fMinSpeedToSay * s_fMinSpeedToSay)
|
|
{
|
|
static float s_fChancesToSay = 0.15f;
|
|
playerDriver->RandomlySay("NEAR_MISS_VEHICLE", s_fChancesToSay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const CVehicle* CPlayerInfo::GetLastNearMissVehicle() const
|
|
{
|
|
const CVehicle* retVeh = NULL;
|
|
if (m_nearVehicles[m_nearVehiclePut].veh && m_nearVehicles[m_nearVehiclePut].time + 2000 > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
retVeh = m_nearVehicles[m_nearVehiclePut].veh;
|
|
}
|
|
return retVeh;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSinceLastNearMiss() const
|
|
{
|
|
if (m_nearVehicles[m_nearVehiclePut].veh)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_nearVehicles[m_nearVehiclePut].time;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : GetTimeSincePlayerHitCar, GetTimeSincePlayerHitPed etc
|
|
// PURPOSE : Access functions for the reckless driving.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerHitCar()
|
|
{
|
|
if (m_LastTimePlayerHitCar)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerHitCar;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerHitPed()
|
|
{
|
|
if (m_LastTimePlayerHitPed)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerHitPed;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerHitBuilding()
|
|
{
|
|
if (m_LastTimePlayerHitBuilding)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerHitBuilding;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerHitObject()
|
|
{
|
|
if (m_LastTimePlayerHitObject)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerHitObject;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerDroveOnPavement()
|
|
{
|
|
if (m_LastTimePlayerDroveOnPavement)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerDroveOnPavement;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerRanLight()
|
|
{
|
|
if (m_LastTimePlayerRanLight)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerRanLight;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetTimeSincePlayerDroveAgainstTraffic()
|
|
{
|
|
if (m_LastTimePlayerDroveAgainstTraffic)
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_LastTimePlayerDroveAgainstTraffic;
|
|
}
|
|
return ~(u32)0;
|
|
}
|
|
|