From 4ae4bc94c692d9fe5781d36a7be6ab72803f6162 Mon Sep 17 00:00:00 2001 From: Nikolay Korolev Date: Wed, 7 Aug 2019 00:32:19 +0300 Subject: [PATCH] CCarCtrl::GenerateOneRandomVehicle! --- src/control/AutoPilot.h | 13 +- src/control/CarAI.cpp | 3 + src/control/CarAI.h | 5 + src/control/CarCtrl.cpp | 492 +++++++++++++++++++++++++++++++++++++ src/control/CarCtrl.h | 29 +++ src/control/Curves.cpp | 6 + src/control/Curves.h | 9 + src/control/PathFind.cpp | 2 + src/control/PathFind.h | 2 + src/control/Population.cpp | 1 + src/control/Population.h | 1 + src/control/Restart.h | 1 - src/core/IniFile.cpp | 28 +++ src/core/IniFile.h | 10 + src/core/common.h | 2 +- src/entities/Physical.h | 3 + 16 files changed, 599 insertions(+), 8 deletions(-) create mode 100644 src/control/Curves.cpp create mode 100644 src/control/Curves.h create mode 100644 src/core/IniFile.cpp create mode 100644 src/core/IniFile.h diff --git a/src/control/AutoPilot.h b/src/control/AutoPilot.h index b1c824d8..3ace0a51 100644 --- a/src/control/AutoPilot.h +++ b/src/control/AutoPilot.h @@ -1,4 +1,5 @@ #pragma once +#include "Timer.h" class CVehicle; @@ -62,15 +63,15 @@ public: uint32 m_nCurrentRouteNode; uint32 m_nNextRouteNode; uint32 m_nPrevRouteNode; - uint32 m_nTotalSpeedScaleFactor; - uint32 m_nSpeedScaleFactor; + uint32 m_nTimeEnteredCurve; + uint32 m_nCurveSpeedScale; uint32 m_nCurrentPathNodeInfo; uint32 m_nNextPathNodeInfo; uint32 m_nPreviousPathNodeInfo; uint32 m_nTimeToStartMission; uint32 m_nTimeSwitchedToRealPhysics; int8 m_nPreviousDirection; - int8 m_nCurrentDirecton; + int8 m_nCurrentDirection; int8 m_nNextDirection; int8 m_nPreviousLane; int8 m_nCurrentLane; @@ -94,13 +95,13 @@ public: m_nPrevRouteNode = 0; m_nNextRouteNode = m_nPrevRouteNode; m_nCurrentRouteNode = m_nNextRouteNode; - m_nTotalSpeedScaleFactor = 0; - m_nSpeedScaleFactor = 1000; + m_nTimeEnteredCurve = 0; + m_nCurveSpeedScale = 1000; m_nPreviousPathNodeInfo = 0; m_nNextPathNodeInfo = m_nPreviousPathNodeInfo; m_nCurrentPathNodeInfo = m_nNextPathNodeInfo; m_nNextDirection = 1; - m_nCurrentDirecton = m_nNextDirection; + m_nCurrentDirection = m_nNextDirection; m_nPreviousLane = m_nCurrentLane = 0; m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; m_nCarMission = MISSION_NONE; diff --git a/src/control/CarAI.cpp b/src/control/CarAI.cpp index faf27788..5129f112 100644 --- a/src/control/CarAI.cpp +++ b/src/control/CarAI.cpp @@ -4,3 +4,6 @@ WRAPPER void CCarAI::UpdateCarAI(CVehicle*) { EAXJMP(0x413E50); } WRAPPER void CCarAI::MakeWayForCarWithSiren(CVehicle *veh) { EAXJMP(0x416280); } +WRAPPER eCarMission CCarAI::FindPoliceCarMissionForWantedLevel() { EAXJMP(0x415E30); } +WRAPPER int32 CCarAI::FindPoliceCarSpeedForWantedLevel(CVehicle*) { EAXJMP(0x415EB0); } +WRAPPER void CCarAI::AddPoliceOccupants(CVehicle*) { EAXJMP(0x415C60); } diff --git a/src/control/CarAI.h b/src/control/CarAI.h index 5112f769..865cb467 100644 --- a/src/control/CarAI.h +++ b/src/control/CarAI.h @@ -1,5 +1,7 @@ #pragma once +#include "AutoPilot.h" + class CVehicle; class CCarAI @@ -7,4 +9,7 @@ class CCarAI public: static void UpdateCarAI(CVehicle*); static void MakeWayForCarWithSiren(CVehicle *veh); + static int32 FindPoliceCarSpeedForWantedLevel(CVehicle*); + static eCarMission FindPoliceCarMissionForWantedLevel(); + static void AddPoliceOccupants(CVehicle*); }; diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp index 12b444a2..8713c665 100644 --- a/src/control/CarCtrl.cpp +++ b/src/control/CarCtrl.cpp @@ -2,6 +2,26 @@ #include "patcher.h" #include "CarCtrl.h" +#include "Automobile.h" +#include "Camera.h" +#include "CarAI.h" +#include "CarGen.h" +#include "Curves.h" +#include "CutsceneMgr.h" +#include "General.h" +#include "IniFile.h" +#include "ModelIndices.h" +#include "PathFind.h" +#include "Ped.h" +#include "PlayerInfo.h" +#include "PlayerPed.h" +#include "Timer.h" +#include "VisibilityPlugins.h" +#include "Vehicle.h" +#include "Wanted.h" +#include "World.h" +#include "Zones.h" + int &CCarCtrl::NumLawEnforcerCars = *(int*)0x8F1B38; int &CCarCtrl::NumAmbulancesOnDuty = *(int*)0x885BB0; int &CCarCtrl::NumFiretrucksOnDuty = *(int*)0x9411F0; @@ -10,6 +30,9 @@ float& CCarCtrl::CarDensityMultiplier = *(float*)0x5EC8B4; int32 &CCarCtrl::NumMissionCars = *(int32*)0x8F1B54; int32 &CCarCtrl::NumRandomCars = *(int32*)0x943118; int32 &CCarCtrl::NumParkedCars = *(int32*)0x8F29E0; +int8 &CCarCtrl::CountDownToCarsAtStart = *(int8*)0x95CD63; +int32 &CCarCtrl::MaxNumberOfCarsInUse = *(int32*)0x5EC8B8; +uint32 &CCarCtrl::LastTimeLawEnforcerCreated = *(uint32*)0x8F5FF0; WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); } WRAPPER void CCarCtrl::AddToCarArray(int32 id, int32 vehclass) { EAXJMP(0x4182F0); } @@ -21,6 +44,471 @@ WRAPPER void CCarCtrl::SteerAICarWithPhysics(CVehicle*) { EAXJMP(0x41DA60); } WRAPPER void CCarCtrl::UpdateCarOnRails(CVehicle*) { EAXJMP(0x418880); } WRAPPER void CCarCtrl::ScanForPedDanger(CVehicle *veh) { EAXJMP(0x418F40); } WRAPPER void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* v) { EAXJMP(0x41F7A0); } +WRAPPER void CCarCtrl::GenerateEmergencyServicesCar(void) { EAXJMP(0x41FC50); } +WRAPPER int32 CCarCtrl::ChooseModel(CZoneInfo*, CVector*, int*) { EAXJMP(0x417EC0); } +WRAPPER int32 CCarCtrl::ChoosePoliceCarModel(void) { EAXJMP(0x4181F0); } + +void +CCarCtrl::GenerateRandomCars() +{ + if (CCutsceneMgr::IsCutsceneProcessing()) + return; + if (NumRandomCars < 30){ + if (CountDownToCarsAtStart == 0){ + GenerateOneRandomCar(); + } + else if (--CountDownToCarsAtStart == 0) { + for (int i = 0; i < 50; i++) + GenerateOneRandomCar(); + CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter = 20; + } + } + /* Approximately once per 4 seconds. */ + if ((CTimer::GetTimeInMilliseconds() & 0xFFFFF000) != (CTimer::GetPreviousTimeInMilliseconds() & 0xFFFFF000)) + GenerateEmergencyServicesCar(); +} + +void +CCarCtrl::GenerateOneRandomCar() +{ + static int32 unk = 0; + CPlayerInfo* pPlayer = &CWorld::Players[CWorld::PlayerInFocus]; + CVector vecTargetPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + CVector2D vecPlayerSpeed = FindPlayerSpeed(); + CZoneInfo zone; + CTheZones::GetZoneInfoForTimeOfDay(&vecTargetPos, &zone); + pPlayer->m_nTrafficMultiplier = pPlayer->m_fRoadDensity * zone.carDensity; + if (NumRandomCars >= pPlayer->m_nTrafficMultiplier * CarDensityMultiplier * CIniFile::CarNumberMultiplier) + return; + if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars + NumLawEnforcerCars + NumRandomCars >= MaxNumberOfCarsInUse) + return; + CWanted* pWanted = pPlayer->m_pPed->m_pWanted; + int carClass; + int carModel; + if (pWanted->m_nWantedLevel > 1 && NumLawEnforcerCars < pWanted->m_MaximumLawEnforcerVehicles && + pWanted->m_CurrentCops < pWanted->m_MaxCops && ( + pWanted->m_nWantedLevel > 3 || + pWanted->m_nWantedLevel > 2 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 5000 || + pWanted->m_nWantedLevel > 1 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 8000)) { + /* Last pWanted->m_nWantedLevel > 1 is unnecessary but I added it for better readability. */ + /* Wouldn't be surprised it was there originally but was optimized out. */ + carClass = COPS; + carModel = ChoosePoliceCarModel(); + }else{ + carModel = ChooseModel(&zone, &vecTargetPos, &carClass); + if (carClass == COPS && pWanted->m_nWantedLevel >= 1) + /* All cop spawns with wanted level are handled by condition above. */ + /* In particular it means that cop cars never spawn if player has wanted level of 1. */ + return; + } + float frontX, frontY; + float preferredDistance, angleLimit; + bool invertAngleLimitTest; + CVector spawnPosition; + int32 curNodeId, nextNodeId; + float positionBetweenNodes; + bool testForCollision; + CVehicle* pPlayerVehicle = FindPlayerVehicle(); + CVector2D vecPlayerVehicleSpeed; + float fPlayerVehicleSpeed; + if (pPlayerVehicle) { + vecPlayerVehicleSpeed = FindPlayerVehicle()->GetMoveSpeed(); + fPlayerVehicleSpeed = vecPlayerVehicleSpeed.Magnitude(); + } + if (TheCamera.GetForward().z < -0.9f){ + /* Player uses topdown camera. */ + /* Spawn essentially anywhere. */ + frontX = frontY = 0.707f; /* 45 degrees */ + angleLimit = -1.0f; + invertAngleLimitTest = true; + preferredDistance = 40.0f; + /* BUG: testForCollision not initialized in original game. */ + testForCollision = false; + }else if (!pPlayerVehicle){ + /* Player is not in vehicle. */ + testForCollision = true; + frontX = TheCamera.CamFrontXNorm; + frontY = TheCamera.CamFrontYNorm; + switch (CTimer::GetFrameCounter() & 1) { + case 0: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = 40.0f; + break; + } + }else if (fPlayerVehicleSpeed > 0.4f){ /* 72 km/h */ + /* Player is moving fast in vehicle */ + /* Prefer spawning vehicles very far away from him. */ + frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; + frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; + testForCollision = false; + switch (CTimer::GetFrameCounter() & 3) { + case 0: + case 1: + /* Spawn a vehicle in a very narrow gap in front of a player */ + angleLimit = 0.85f; /* approx 30 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 2: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 3: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = 40.0f; + break; + } + }else if (fPlayerVehicleSpeed > 0.1f){ /* 18 km/h */ + /* Player is moving moderately fast in vehicle */ + /* Spawn more vehicles to player's side. */ + frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; + frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; + testForCollision = false; + switch (CTimer::GetFrameCounter() & 3) { + case 0: + /* Spawn a vehicle in a very narrow gap in front of a player */ + angleLimit = 0.85f; /* approx 30 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 2: + case 3: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = 40.0f; + break; + } + }else{ + /* Player is in vehicle but moving very slow. */ + /* Then use camera direction instead of vehicle direction. */ + testForCollision = true; + frontX = TheCamera.CamFrontXNorm; + frontY = TheCamera.CamFrontYNorm; + switch (CTimer::GetFrameCounter() & 1) { + case 0: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = 120.0f * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = 40.0f; + break; + } + } + if (!ThePaths.NewGenerateCarCreationCoors(vecTargetPos.x, vecTargetPos.y, frontX, frontY, + preferredDistance, angleLimit, invertAngleLimitTest, &spawnPosition, &curNodeId, &nextNodeId, + &positionBetweenNodes, carClass == COPS && pWanted->m_nWantedLevel >= 1)) + return; + int16 colliding; + CWorld::FindObjectsKindaColliding(spawnPosition, 10.0f, true, &colliding, 2, nil, false, true, true, false, false); + if (colliding) + /* If something is already present in spawn position, do not create vehicle*/ + return; + if (!ThePaths.TestCoorsCloseness(vecTargetPos, false, spawnPosition)) + /* Testing if spawn position can reach target position via valid path. */ + return; + int16 idInNode = 0; + CPathNode* pNode1 = &ThePaths.m_pathNodes[curNodeId]; + while (idInNode < pNode1->numLinks && + ThePaths.m_connections[idInNode + pNode1->firstLink] != nextNodeId) + idInNode++; + int16 connectionId = ThePaths.m_carPathConnections[idInNode + ThePaths.m_pathNodes[curNodeId].firstLink]; + CCarPathLink* pPathLink = &ThePaths.m_carPathLinks[connectionId]; + int16 lanesOnCurrentRoad = pPathLink->pathNodeIndex == nextNodeId ? pPathLink->numLeftLanes : pPathLink->numRightLanes; + CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(carModel); + if (lanesOnCurrentRoad == 0 || pModelInfo->m_vehicleType == VEHICLE_TYPE_BIKE) + /* Not spawning vehicle if road is one way and intended direction is opposide to that way. */ + /* Also not spawning bikes but they don't exist in final game. */ + return; + CAutomobile* pCar = new CAutomobile(carModel, RANDOM_VEHICLE); + pCar->AutoPilot.m_nPrevRouteNode = 0; + pCar->AutoPilot.m_nCurrentRouteNode = curNodeId; + pCar->AutoPilot.m_nNextRouteNode = nextNodeId; + switch (carClass) { + case POOR: + case RICH: + case EXEC: + case WORKER: + case SPECIAL: + case BIG: + case TAXI: + case MAFIA: + case TRIAD: + case DIABLO: + case YAKUZA: + case YARDIE: + case COLOMB: + case NINES: + case GANG8: + case GANG9: + { + pCar->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(9, 14); + if (carClass == EXEC) + pCar->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 18); + else if (carClass == POOR || carClass == SPECIAL) + pCar->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(7, 10); + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(pCar->GetModelIndex()); + if (pVehicleInfo->GetColModel()->boundingBox.max.y - pVehicleInfo->GetColModel()->boundingBox.min.y > 10.0f || carClass == BIG) { + pCar->AutoPilot.m_nCruiseSpeed *= 3; + pCar->AutoPilot.m_nCruiseSpeed /= 4; + } + pCar->AutoPilot.m_fMaxTrafficSpeed = pCar->AutoPilot.m_nCruiseSpeed; + pCar->AutoPilot.m_nCarMission = MISSION_CRUISE; + pCar->AutoPilot.m_nAnimationId = TEMPACT_NONE; + pCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + break; + } + case COPS: + pCar->AutoPilot.m_nAnimationId = TEMPACT_NONE; + if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->m_nWantedLevel != 0){ + pCar->AutoPilot.m_nCruiseSpeed = CCarAI::FindPoliceCarSpeedForWantedLevel(pCar); + pCar->AutoPilot.m_fMaxTrafficSpeed = pCar->AutoPilot.m_nCruiseSpeed / 2; + pCar->AutoPilot.m_nCarMission = CCarAI::FindPoliceCarMissionForWantedLevel(); + pCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + }else{ + pCar->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 16); + pCar->AutoPilot.m_fMaxTrafficSpeed = pCar->AutoPilot.m_nCruiseSpeed; + pCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + pCar->AutoPilot.m_nCarMission = MISSION_CRUISE; + } + if (carModel == MI_FBICAR){ + pCar->m_currentColour1 = 0; + pCar->m_currentColour2 = 0; + /* FBI cars are gray in carcols, but we want them black if they going after player. */ + } + default: + break; + } + if (pCar && pCar->GetModelIndex() == MI_MRWHOOP) + pCar->m_bSirenOrAlarm = true; + pCar->AutoPilot.m_nNextPathNodeInfo = connectionId; + pCar->AutoPilot.m_nCurrentLane = pCar->AutoPilot.m_nPreviousLane = CGeneral::GetRandomNumber() % lanesOnCurrentRoad; + CColBox* boundingBox = &CModelInfo::GetModelInfo(pCar->GetModelIndex())->GetColModel()->boundingBox; + float carLength = 1.0f + (boundingBox->max.y - boundingBox->min.y) / 2; + float distanceBetweenNodes = (ThePaths.m_pathNodes[curNodeId].pos - ThePaths.m_pathNodes[nextNodeId].pos).Magnitude2D(); + /* If car is so long that it doesn't fit between two car nodes, place it directly in the middle. */ + /* Otherwise put it at least in a way that full vehicle length fits between two nodes. */ + if (distanceBetweenNodes / 2 < carLength) + positionBetweenNodes = 0.5f; + else + positionBetweenNodes = min(1.0f - carLength / distanceBetweenNodes, max(carLength / distanceBetweenNodes, positionBetweenNodes)); + pCar->AutoPilot.m_nNextDirection = (curNodeId >= nextNodeId) ? 1 : -1; + if (ThePaths.m_pathNodes[curNodeId].numLinks == 1){ + /* Do not create vehicle if there is nowhere to go. */ + delete pCar; + return; + } + int16 nextConnection = pCar->AutoPilot.m_nNextPathNodeInfo; + int16 newLink; + while (nextConnection == pCar->AutoPilot.m_nNextPathNodeInfo){ + newLink = CGeneral::GetRandomNumber() % ThePaths.m_pathNodes[curNodeId].numLinks; + nextConnection = ThePaths.m_carPathConnections[newLink + ThePaths.m_pathNodes[curNodeId].firstLink]; + } + pCar->AutoPilot.m_nCurrentPathNodeInfo = nextConnection; + pCar->AutoPilot.m_nCurrentDirection = (ThePaths.m_connections[newLink + ThePaths.m_pathNodes[curNodeId].firstLink] >= curNodeId) ? 1 : -1; + CVector2D vecBetweenNodes = ThePaths.m_pathNodes[nextNodeId].pos - ThePaths.m_pathNodes[curNodeId].pos; + float forwardX, forwardY; + float distBetweenNodes = vecBetweenNodes.Magnitude(); + if (distanceBetweenNodes == 0.0f){ + forwardX = 1.0f; + forwardY = 0.0f; + }else{ + forwardX = vecBetweenNodes.x / distBetweenNodes; + forwardY = vecBetweenNodes.y / distBetweenNodes; + } + /* I think the following might be some form of SetRotateZOnly. */ + /* Setting up direction between two car nodes. */ + pCar->GetForward() = CVector(forwardX, forwardY, 0.0f); + pCar->GetRight() = CVector(forwardY, -forwardX, 0.0f); + pCar->GetUp() = CVector(0.0f, 0.0f, 1.0f); + + float currentPathLinkForwardX = pCar->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pCar->AutoPilot.m_nCurrentPathNodeInfo].dirX; + float currentPathLinkForwardY = pCar->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pCar->AutoPilot.m_nCurrentPathNodeInfo].dirY; + float nextPathLinkForwardX = pCar->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pCar->AutoPilot.m_nNextPathNodeInfo].dirX; + float nextPathLinkForwardY = pCar->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pCar->AutoPilot.m_nNextPathNodeInfo].dirY; + + /* Selecting lane. */ + CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pCar->AutoPilot.m_nCurrentPathNodeInfo]; + float currentLaneCoefficient = (pCurrentLink->numLeftLanes == 0) ? (0.5f - 0.5f * pCurrentLink->numRightLanes) : + ((pCurrentLink->numRightLanes == 0) ? (0.5f - 0.5f * pCurrentLink->numLeftLanes) : 0.5f); + /* 5.0f is most likely lane width. TODO: enum */ + float roadShiftAlongCurrentNode = (pCar->AutoPilot.m_nPreviousLane + currentLaneCoefficient) * 5.0f; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pCar->AutoPilot.m_nNextPathNodeInfo]; + float nextLaneCoefficient = (pNextLink->numLeftLanes == 0) ? (0.5f - 0.5f * pNextLink->numRightLanes) : + ((pNextLink->numRightLanes == 0) ? (0.5f - 0.5f * pNextLink->numLeftLanes) : 0.5f); + float roadShiftAlongNextNode = (pCar->AutoPilot.m_nCurrentLane + nextLaneCoefficient) * 5.0f; + CVector positionOnCurrentLinkIncludingLane( + pCurrentLink->posX + roadShiftAlongCurrentNode * currentPathLinkForwardY, + pCurrentLink->posY - roadShiftAlongCurrentNode * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->posX + roadShiftAlongNextNode * nextPathLinkForwardY, + pNextLink->posY - roadShiftAlongNextNode * nextPathLinkForwardX, + 0.0f); + float directionCurrentLinkX = pCurrentLink->dirX * pCar->AutoPilot.m_nCurrentDirection; + float directionCurrentLinkY = pCurrentLink->dirY * pCar->AutoPilot.m_nCurrentDirection; + float directionNextLinkX = pNextLink->dirX * pCar->AutoPilot.m_nNextDirection; + float directionNextLinkY = pNextLink->dirY * pCar->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pCar->AutoPilot.m_nCurveSpeedScale = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pCar->AutoPilot.m_fMaxTrafficSpeed); +#ifdef FIX_BUGS + /* Casting timer to float is very unwanted. In this case it's not awful */ + /* but in CAutoPilot::ModifySpeed it can even cause crashes (see SilentPatch). */ + pCar->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - + (uint32)((0.5f + positionBetweenNodes) * pCar->AutoPilot.m_nCurveSpeedScale); +#else + pCar->AutoPilot.m_nTotalSpeedScaleFactor = CTimer::GetTimeInMilliseconds() - + (0.5f + positionBetweenNodes) * pCar->AutoPilot.m_nSpeedScaleFactor; +#endif + uint32 timeAlreadyInCurve = CTimer::GetTimeInMilliseconds() - pCar->AutoPilot.m_nTimeEnteredCurve; + float positionAlongCurve = (float)timeAlreadyInCurve / pCar->AutoPilot.m_nCurveSpeedScale; + CVector directionCurrentLink(directionCurrentLinkX, directionCurrentLinkY, 0.0f); + CVector directionNextLink(directionNextLinkX, directionNextLinkY, 0.0f); + CVector positionIncludingCurve; + CVector directionIncludingCurve; + CCurves::CalcCurvePoint( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + &directionCurrentLink, + &directionNextLink, + positionAlongCurve, + pCar->AutoPilot.m_nCurveSpeedScale, + &positionIncludingCurve, + &directionIncludingCurve + ); + CVector vectorBetweenNodes = ThePaths.m_pathNodes[curNodeId].pos - ThePaths.m_pathNodes[nextNodeId].pos; + CVector finalPosition = positionIncludingCurve + vectorBetweenNodes * 2.0f / vectorBetweenNodes.Magnitude(); + finalPosition.z = positionBetweenNodes * ThePaths.m_pathNodes[nextNodeId].pos.z + + (1.0f - positionBetweenNodes) * ThePaths.m_pathNodes[curNodeId].pos.z; + float groundZ = 1000000000.0f; // TODO: define/enum + CColPoint colPoint; + CEntity* pEntity; + if (CWorld::ProcessVerticalLine(finalPosition, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) + groundZ = colPoint.point.z; + if (CWorld::ProcessVerticalLine(finalPosition, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)){ + if (ABS(colPoint.point.z - finalPosition.z) < ABS(groundZ - finalPosition.z)) + groundZ = colPoint.point.z; + } + if (groundZ == 1000000000.0f || ABS(groundZ - finalPosition.z) > 7.0f) { + /* Failed to find ground or too far from expected position. */ + delete pCar; + return; + } + finalPosition.z = groundZ + pCar->GetHeightAboveRoad(); + pCar->GetPosition() = finalPosition; + pCar->SetMoveSpeed(directionIncludingCurve / 60.0f); + CVector2D speedDifferenceWithTarget = (CVector2D)pCar->GetMoveSpeed() - vecPlayerSpeed; + CVector2D distanceToTarget = positionIncludingCurve - vecTargetPos; + switch (carClass) { + case POOR: + case RICH: + case EXEC: + case WORKER: + case SPECIAL: + case BIG: + case TAXI: + case MAFIA: + case TRIAD: + case DIABLO: + case YAKUZA: + case YARDIE: + case COLOMB: + case NINES: + case GANG8: + case GANG9: + pCar->m_status = STATUS_SIMPLE; + break; + case COPS: + pCar->m_status = (pCar->AutoPilot.m_nCarMission == MISSION_CRUISE) ? STATUS_SIMPLE : STATUS_PHYSICS; + pCar->ChangeLawEnforcerState(1); + break; + default: + break; + } + CVisibilityPlugins::SetClumpAlpha(pCar->GetClump(), 0); + if (!pCar->GetIsOnScreen()){ + if ((vecTargetPos - pCar->GetPosition()).Magnitude2D() > 50.0f) { + /* Too far away cars that are not visible aren't needed. */ + delete pCar; + return; + } + }else if((vecTargetPos - pCar->GetPosition()).Magnitude2D() > TheCamera.GenerationDistMultiplier * 130.0f || + (vecTargetPos - pCar->GetPosition()).Magnitude2D() < TheCamera.GenerationDistMultiplier * 110.0f){ + delete pCar; + return; + }else if((TheCamera.GetPosition() - pCar->GetPosition()).Magnitude2D() < 90.0f * TheCamera.GenerationDistMultiplier){ + delete pCar; + return; + } + CVehicleModelInfo* pVehicleModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(pCar->GetModelIndex()); + float radiusToTest = pVehicleModel->GetColModel()->boundingSphere.radius; + if (testForCollision){ + CWorld::FindObjectsKindaColliding(pCar->GetPosition(), radiusToTest + 20.0f, true, &colliding, 2, nil, false, true, false, false, false); + if (colliding){ + delete pCar; + return; + } + } + CWorld::FindObjectsKindaColliding(pCar->GetPosition(), radiusToTest, true, &colliding, 2, nil, false, true, false, false, false); + if (colliding){ + delete pCar; + return; + } + if (speedDifferenceWithTarget.x * distanceToTarget.x + + speedDifferenceWithTarget.y * distanceToTarget.y >= 0.0f){ + delete pCar; + return; + } + pVehicleModel->AvoidSameVehicleColour(&pCar->m_currentColour1, &pCar->m_currentColour2); + CWorld::Add(pCar); + if (carClass == COPS) + CCarAI::AddPoliceOccupants(pCar); + else + pCar->SetUpDriver(); + if ((CGeneral::GetRandomNumber() & 0x3F) == 0){ /* 1/64 probability */ + pCar->m_status = STATUS_PHYSICS; + pCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pCar->AutoPilot.m_nCruiseSpeed += 10; + } + if (carClass == COPS) + LastTimeLawEnforcerCreated = CTimer::GetTimeInMilliseconds(); +} bool CCarCtrl::MapCouldMoveInThisArea(float x, float y) @@ -29,3 +517,7 @@ CCarCtrl::MapCouldMoveInThisArea(float x, float y) return x > -342.0f && x < -219.0f && y > -677.0f && y < -580.0f; } + +STARTPATCHES +InjectHook(0x416580, &CCarCtrl::GenerateRandomCars, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h index 2ad52d49..6b1fce8c 100644 --- a/src/control/CarCtrl.h +++ b/src/control/CarCtrl.h @@ -1,9 +1,30 @@ #pragma once class CVehicle; +class CZoneInfo; class CCarCtrl { + enum eCarClass { + POOR = 0, + RICH, + EXEC, + WORKER, + SPECIAL, + BIG, + TAXI, + CLASS7, + MAFIA, + TRIAD, + DIABLO, + YAKUZA, + YARDIE, + COLOMB, + NINES, + GANG8, + GANG9, + COPS + }; public: static void SwitchVehicleToRealPhysics(CVehicle*); static void AddToCarArray(int32 id, int32 vehclass); @@ -16,6 +37,11 @@ public: static bool MapCouldMoveInThisArea(float x, float y); static void ScanForPedDanger(CVehicle *veh); static void RemoveFromInterestingVehicleList(CVehicle*); + static void GenerateRandomCars(void); + static void GenerateOneRandomCar(void); + static void GenerateEmergencyServicesCar(void); + static int32 ChooseModel(CZoneInfo*, CVector*, int*); + static int32 ChoosePoliceCarModel(void); static int32 &NumLawEnforcerCars; static int32 &NumAmbulancesOnDuty; @@ -25,4 +51,7 @@ public: static int32 &NumParkedCars; static bool &bCarsGeneratedAroundCamera; static float &CarDensityMultiplier; + static int8 &CountDownToCarsAtStart; + static int32 &MaxNumberOfCarsInUse; + static uint32 &LastTimeLawEnforcerCreated; }; diff --git a/src/control/Curves.cpp b/src/control/Curves.cpp new file mode 100644 index 00000000..84d4af5a --- /dev/null +++ b/src/control/Curves.cpp @@ -0,0 +1,6 @@ +#include "common.h" +#include "patcher.h" +#include "Curves.h" + +WRAPPER float CCurves::CalcSpeedScaleFactor(CVector*, CVector*, float, float, float, float) { EAXJMP(0x420410); } +WRAPPER void CCurves::CalcCurvePoint(CVector*, CVector*, CVector*, CVector*, float, int32, CVector*, CVector*) { EAXJMP(0x4204D0); } diff --git a/src/control/Curves.h b/src/control/Curves.h new file mode 100644 index 00000000..5d4e05a7 --- /dev/null +++ b/src/control/Curves.h @@ -0,0 +1,9 @@ +#pragma once +class CVector; + +class CCurves +{ +public: + static float CalcSpeedScaleFactor(CVector*, CVector*, float, float, float, float); + static void CalcCurvePoint(CVector*, CVector*, CVector*, CVector*, float, int32, CVector*, CVector*); +}; diff --git a/src/control/PathFind.cpp b/src/control/PathFind.cpp index f90e0c8f..1e128457 100644 --- a/src/control/PathFind.cpp +++ b/src/control/PathFind.cpp @@ -6,6 +6,8 @@ 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 diff --git a/src/control/PathFind.h b/src/control/PathFind.h index 9d97de3f..b5255704 100644 --- a/src/control/PathFind.h +++ b/src/control/PathFind.h @@ -130,6 +130,8 @@ public: 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); } diff --git a/src/control/Population.cpp b/src/control/Population.cpp index 31c475f0..83259616 100644 --- a/src/control/Population.cpp +++ b/src/control/Population.cpp @@ -8,6 +8,7 @@ bool &CPopulation::ms_bGivePedsWeapons = *(bool*)0x95CCF6; int32 &CPopulation::m_AllRandomPedsThisType = *(int32*)0x5FA570; float &CPopulation::PedDensityMultiplier = *(float*)0x5FA56C; uint32 &CPopulation::ms_nTotalMissionPeds = *(uint32*)0x8F5F70; +int32 &CPopulation::MaxNumberOfPedsInUse = *(int32*)0x5FA574; WRAPPER void CPopulation::UpdatePedCount(uint32, bool) { EAXJMP(0x4F5A60); } WRAPPER void CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool) { EAXJMP(0x4F6200); } diff --git a/src/control/Population.h b/src/control/Population.h index e067562a..7e4b40d8 100644 --- a/src/control/Population.h +++ b/src/control/Population.h @@ -17,6 +17,7 @@ public: static int32 &m_AllRandomPedsThisType; static float &PedDensityMultiplier; static uint32 &ms_nTotalMissionPeds; + static int32 &MaxNumberOfPedsInUse; static void UpdatePedCount(uint32, bool); static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool); diff --git a/src/control/Restart.h b/src/control/Restart.h index 90da8e89..f49ed79c 100644 --- a/src/control/Restart.h +++ b/src/control/Restart.h @@ -1,5 +1,4 @@ #pragma once -#pragma once class CRestart { diff --git a/src/core/IniFile.cpp b/src/core/IniFile.cpp new file mode 100644 index 00000000..08b30876 --- /dev/null +++ b/src/core/IniFile.cpp @@ -0,0 +1,28 @@ +#include "common.h" +#include "patcher.h" +#include "IniFile.h" + +#include "CarCtrl.h" +#include "FileMgr.h" +#include "main.h" +#include "Population.h" + +float &CIniFile::PedNumberMultiplier = *(float*)0x6182F4; +float &CIniFile::CarNumberMultiplier = *(float*)0x6182F8; + +void CIniFile::LoadIniFile() +{ + CFileMgr::SetDir(""); + int f = CFileMgr::OpenFile("gta3.ini", "r"); + if (f){ + CFileMgr::ReadLine(f, gString, 200); + sscanf(gString, "%f", &PedNumberMultiplier); + PedNumberMultiplier = min(3.0f, max(0.5f, PedNumberMultiplier)); + CFileMgr::ReadLine(f, gString, 200); + sscanf(gString, "%f", &CarNumberMultiplier); + CarNumberMultiplier = min(3.0f, max(0.5f, CarNumberMultiplier)); + CFileMgr::CloseFile(f); + } + CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier; + CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier; +} \ No newline at end of file diff --git a/src/core/IniFile.h b/src/core/IniFile.h new file mode 100644 index 00000000..9a98151b --- /dev/null +++ b/src/core/IniFile.h @@ -0,0 +1,10 @@ +#pragma once + +class CIniFile +{ +public: + static void LoadIniFile(); + + static float& PedNumberMultiplier; + static float& CarNumberMultiplier; +}; diff --git a/src/core/common.h b/src/core/common.h index 97a25a3f..2b4c466a 100644 --- a/src/core/common.h +++ b/src/core/common.h @@ -179,7 +179,7 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) < (b)) ? (a) : (b)) -#define ABS(a) (((a) < 0) ? (-a) : (a)) +#define ABS(a) (((a) < 0) ? (-(a)) : (a)) #define norm(value, min, max) (((value) < (min)) ? 0 : (((value) > (max)) ? 1 : (((value) - (min)) / ((max) - (min))))) diff --git a/src/entities/Physical.h b/src/entities/Physical.h index 26ef0086..ee75d059 100644 --- a/src/entities/Physical.h +++ b/src/entities/Physical.h @@ -115,6 +115,9 @@ public: m_vecMoveSpeed.y = y; m_vecMoveSpeed.z = z; } + void SetMoveSpeed(const CVector& speed) { + m_vecMoveSpeed = speed; + } const CVector &GetTurnSpeed() { return m_vecTurnSpeed; } void SetTurnSpeed(float x, float y, float z) { m_vecTurnSpeed.x = x;