summaryrefslogtreecommitdiffstats
path: root/src/control
diff options
context:
space:
mode:
authorFire_Head <Fire-Head@users.noreply.github.com>2019-08-15 04:06:52 +0200
committerGitHub <noreply@github.com>2019-08-15 04:06:52 +0200
commit6909fa283a25410a6d5f2fc93259b71770512ca2 (patch)
treecc187ba965eb71352fae028d3eb7f9f8db463153 /src/control
parentCParticleObject done, cDMAudio done (diff)
parentMerge pull request #189 from Nick007J/master (diff)
downloadre3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.gz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.bz2
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.lz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.xz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.zst
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.zip
Diffstat (limited to 'src/control')
-rw-r--r--src/control/AutoPilot.cpp5
-rw-r--r--src/control/AutoPilot.h33
-rw-r--r--src/control/CarAI.cpp16
-rw-r--r--src/control/CarAI.h6
-rw-r--r--src/control/CarCtrl.cpp864
-rw-r--r--src/control/CarCtrl.h68
-rw-r--r--src/control/Cranes.cpp5
-rw-r--r--src/control/Cranes.h10
-rw-r--r--src/control/Curves.cpp6
-rw-r--r--src/control/Curves.h9
-rw-r--r--src/control/Gangs.cpp2
-rw-r--r--src/control/Gangs.h3
-rw-r--r--src/control/Garages.cpp1
-rw-r--r--src/control/Garages.h1
-rw-r--r--src/control/PathFind.cpp1012
-rw-r--r--src/control/PathFind.h103
-rw-r--r--src/control/Pickups.cpp3
-rw-r--r--src/control/Pickups.h4
-rw-r--r--src/control/Population.cpp1
-rw-r--r--src/control/Population.h1
-rw-r--r--src/control/Restart.cpp7
-rw-r--r--src/control/Restart.h9
-rw-r--r--src/control/RoadBlocks.cpp5
-rw-r--r--src/control/RoadBlocks.h10
-rw-r--r--src/control/Script.cpp486
-rw-r--r--src/control/TrafficLights.cpp17
-rw-r--r--src/control/TrafficLights.h10
27 files changed, 2552 insertions, 145 deletions
diff --git a/src/control/AutoPilot.cpp b/src/control/AutoPilot.cpp
new file mode 100644
index 00000000..54b51454
--- /dev/null
+++ b/src/control/AutoPilot.cpp
@@ -0,0 +1,5 @@
+#include "common.h"
+#include "patcher.h"
+#include "AutoPilot.h"
+
+WRAPPER void CAutoPilot::ModifySpeed(float) { EAXJMP(0x4137B0); }
diff --git a/src/control/AutoPilot.h b/src/control/AutoPilot.h
index b1c824d8..e0adc23a 100644
--- a/src/control/AutoPilot.h
+++ b/src/control/AutoPilot.h
@@ -1,4 +1,5 @@
#pragma once
+#include "Timer.h"
class CVehicle;
@@ -62,26 +63,26 @@ public:
uint32 m_nCurrentRouteNode;
uint32 m_nNextRouteNode;
uint32 m_nPrevRouteNode;
- uint32 m_nTotalSpeedScaleFactor;
- uint32 m_nSpeedScaleFactor;
+ uint32 m_nTimeEnteredCurve;
+ uint32 m_nTimeToSpendOnCurrentCurve;
uint32 m_nCurrentPathNodeInfo;
uint32 m_nNextPathNodeInfo;
uint32 m_nPreviousPathNodeInfo;
uint32 m_nTimeToStartMission;
- uint32 m_nTimeSwitchedToRealPhysics;
+ uint32 m_nAntiReverseTimer;
int8 m_nPreviousDirection;
- int8 m_nCurrentDirecton;
+ int8 m_nCurrentDirection;
int8 m_nNextDirection;
- int8 m_nPreviousLane;
int8 m_nCurrentLane;
+ int8 m_nNextLane;
eCarDrivingStyle m_nDrivingStyle;
eCarMission m_nCarMission;
- eCarTempAction m_nAnimationId;
- uint8 m_nAnimationTime;
+ eCarTempAction m_nTempAction;
+ uint32 m_nTimeTempAction;
float m_fMaxTrafficSpeed;
uint8 m_nCruiseSpeed;
uint8 m_flag1 : 1;
- uint8 m_flag2 : 1;
+ uint8 m_bSlowedDownBecauseOfPeds : 1;
uint8 m_flag4 : 1;
uint8 m_flag8 : 1;
uint8 m_flag10 : 1;
@@ -94,26 +95,28 @@ public:
m_nPrevRouteNode = 0;
m_nNextRouteNode = m_nPrevRouteNode;
m_nCurrentRouteNode = m_nNextRouteNode;
- m_nTotalSpeedScaleFactor = 0;
- m_nSpeedScaleFactor = 1000;
+ m_nTimeEnteredCurve = 0;
+ m_nTimeToSpendOnCurrentCurve = 1000;
m_nPreviousPathNodeInfo = 0;
m_nNextPathNodeInfo = m_nPreviousPathNodeInfo;
m_nCurrentPathNodeInfo = m_nNextPathNodeInfo;
m_nNextDirection = 1;
- m_nCurrentDirecton = m_nNextDirection;
- m_nPreviousLane = m_nCurrentLane = 0;
+ m_nCurrentDirection = m_nNextDirection;
+ m_nCurrentLane = m_nNextLane = 0;
m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
m_nCarMission = MISSION_NONE;
- m_nAnimationId = TEMPACT_NONE;
+ m_nTempAction = TEMPACT_NONE;
m_nCruiseSpeed = 10;
m_fMaxTrafficSpeed = 10.0f;
- m_flag2 = false;
+ m_bSlowedDownBecauseOfPeds = false;
m_flag1 = false;
m_nPathFindNodesCount = 0;
m_pTargetCar = 0;
m_nTimeToStartMission = CTimer::GetTimeInMilliseconds();
- m_nTimeSwitchedToRealPhysics = m_nTimeToStartMission;
+ m_nAntiReverseTimer = m_nTimeToStartMission;
m_flag8 = false;
}
+
+ void ModifySpeed(float);
};
static_assert(sizeof(CAutoPilot) == 0x70, "CAutoPilot: error");
diff --git a/src/control/CarAI.cpp b/src/control/CarAI.cpp
index faf27788..470c3d24 100644
--- a/src/control/CarAI.cpp
+++ b/src/control/CarAI.cpp
@@ -2,5 +2,21 @@
#include "patcher.h"
#include "CarAI.h"
+#include "AutoPilot.h"
+#include "Timer.h"
+#include "Vehicle.h"
+
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); }
+
+void CCarAI::CarHasReasonToStop(CVehicle* pVehicle)
+{
+ pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
+}
+
+STARTPATCHES
+InjectHook(0x415B00, &CCarAI::CarHasReasonToStop, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/control/CarAI.h b/src/control/CarAI.h
index 5112f769..0b0a939b 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,8 @@ class CCarAI
public:
static void UpdateCarAI(CVehicle*);
static void MakeWayForCarWithSiren(CVehicle *veh);
+ static int32 FindPoliceCarSpeedForWantedLevel(CVehicle*);
+ static eCarMission FindPoliceCarMissionForWantedLevel();
+ static void AddPoliceOccupants(CVehicle*);
+ static void CarHasReasonToStop(CVehicle*);
};
diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index 12b444a2..0df42397 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -2,6 +2,38 @@
#include "patcher.h"
#include "CarCtrl.h"
+#include "Automobile.h"
+#include "Camera.h"
+#include "CarAI.h"
+#include "CarGen.h"
+#include "Cranes.h"
+#include "Curves.h"
+#include "CutsceneMgr.h"
+#include "Gangs.h"
+#include "Garages.h"
+#include "General.h"
+#include "IniFile.h"
+#include "ModelIndices.h"
+#include "PathFind.h"
+#include "Ped.h"
+#include "PlayerInfo.h"
+#include "PlayerPed.h"
+#include "Pools.h"
+#include "Renderer.h"
+#include "RoadBlocks.h"
+#include "Timer.h"
+#include "TrafficLights.h"
+#include "Streaming.h"
+#include "VisibilityPlugins.h"
+#include "Vehicle.h"
+#include "Wanted.h"
+#include "World.h"
+#include "Zones.h"
+
+#define DISTANCE_TO_SPAWN_ROADBLOCK_PEDS 51.0f
+#define DISTANCE_TO_SCAN_FOR_DANGER 11.0f
+#define INFINITE_Z 1000000000.0f
+
int &CCarCtrl::NumLawEnforcerCars = *(int*)0x8F1B38;
int &CCarCtrl::NumAmbulancesOnDuty = *(int*)0x885BB0;
int &CCarCtrl::NumFiretrucksOnDuty = *(int*)0x9411F0;
@@ -10,17 +42,833 @@ 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;
+int32 (&CCarCtrl::TotalNumOfCarsOfRating)[7] = *(int32(*)[7])*(uintptr*)0x8F1A60;
+int32 (&CCarCtrl::NextCarOfRating)[7] = *(int32(*)[7])*(uintptr*)0x9412AC;
+int32 (&CCarCtrl::CarArrays)[7][MAX_CAR_MODELS_IN_ARRAY] = *(int32(*)[7][MAX_CAR_MODELS_IN_ARRAY])*(uintptr*)0x6EB860;
+CVehicle* (&apCarsToKeep)[MAX_CARS_TO_KEEP] = *(CVehicle*(*)[MAX_CARS_TO_KEEP])*(uintptr*)0x70D830;
WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); }
-WRAPPER void CCarCtrl::AddToCarArray(int32 id, int32 vehclass) { EAXJMP(0x4182F0); }
WRAPPER void CCarCtrl::UpdateCarCount(CVehicle*, bool) { EAXJMP(0x4202E0); }
-WRAPPER int32 CCarCtrl::ChooseCarModel(int32 vehclass) { EAXJMP(0x418110); }
WRAPPER bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle*, CVector, bool) { EAXJMP(0x41FA00); }
WRAPPER void CCarCtrl::JoinCarWithRoadSystem(CVehicle*) { EAXJMP(0x41F820); }
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 void CCarCtrl::PickNextNodeAccordingStrategy(CVehicle*) { EAXJMP(0x41BA50); }
+WRAPPER void CCarCtrl::DragCarToPoint(CVehicle*, CVector*) { EAXJMP(0x41D450); }
+WRAPPER void CCarCtrl::SlowCarDownForCarsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float) { EAXJMP(0x419B40); }
+WRAPPER void CCarCtrl::SlowCarDownForPedsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float) { EAXJMP(0x419300); }
+
+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* pCurNode = &ThePaths.m_pathNodes[curNodeId];
+ CPathNode* pNextNode = &ThePaths.m_pathNodes[nextNodeId];
+ while (idInNode < pCurNode->numLinks &&
+ ThePaths.m_connections[idInNode + pCurNode->firstLink] != nextNodeId)
+ idInNode++;
+ int16 connectionId = ThePaths.m_carPathConnections[idInNode + pCurNode->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_nTempAction = TEMPACT_NONE;
+ pCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
+ break;
+ }
+ case COPS:
+ pCar->AutoPilot.m_nTempAction = 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_nNextLane = pCar->AutoPilot.m_nCurrentLane = CGeneral::GetRandomNumber() % lanesOnCurrentRoad;
+ CColBox* boundingBox = &CModelInfo::GetModelInfo(pCar->GetModelIndex())->GetColModel()->boundingBox;
+ float carLength = 1.0f + (boundingBox->max.y - boundingBox->min.y) / 2;
+ float distanceBetweenNodes = (pCurNode->pos - pNextNode->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 (pCurNode->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() % pCurNode->numLinks;
+ nextConnection = ThePaths.m_carPathConnections[newLink + pCurNode->firstLink];
+ }
+ pCar->AutoPilot.m_nCurrentPathNodeInfo = nextConnection;
+ pCar->AutoPilot.m_nCurrentDirection = (ThePaths.m_connections[newLink + pCurNode->firstLink] >= curNodeId) ? 1 : -1;
+ CVector2D vecBetweenNodes = pNextNode->pos - pCurNode->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;
+
+ CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pCar->AutoPilot.m_nCurrentPathNodeInfo];
+ CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pCar->AutoPilot.m_nNextPathNodeInfo];
+ CVector positionOnCurrentLinkIncludingLane(
+ pCurrentLink->posX + GetOffsetOfLaneFromCenterOfRoad(pCar->AutoPilot.m_nCurrentLane, pCurrentLink) * currentPathLinkForwardY,
+ pCurrentLink->posY - GetOffsetOfLaneFromCenterOfRoad(pCar->AutoPilot.m_nCurrentLane, pCurrentLink) * currentPathLinkForwardX,
+ 0.0f);
+ CVector positionOnNextLinkIncludingLane(
+ pNextLink->posX + GetOffsetOfLaneFromCenterOfRoad(pCar->AutoPilot.m_nNextLane, pNextLink) * nextPathLinkForwardY,
+ pNextLink->posY - GetOffsetOfLaneFromCenterOfRoad(pCar->AutoPilot.m_nNextLane, pNextLink) * 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_nTimeToSpendOnCurrentCurve = 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_nTimeToSpendOnCurrentCurve);
+#else
+ pCar->AutoPilot.m_nTotalSpeedScaleFactor = CTimer::GetTimeInMilliseconds() -
+ (0.5f + positionBetweenNodes) * pCar->AutoPilot.m_nSpeedScaleFactor;
+#endif
+ CVector directionCurrentLink(directionCurrentLinkX, directionCurrentLinkY, 0.0f);
+ CVector directionNextLink(directionNextLinkX, directionNextLinkY, 0.0f);
+ CVector positionIncludingCurve;
+ CVector directionIncludingCurve;
+ CCurves::CalcCurvePoint(
+ &positionOnCurrentLinkIncludingLane,
+ &positionOnNextLinkIncludingLane,
+ &directionCurrentLink,
+ &directionNextLink,
+ GetPositionAlongCurrentCurve(pCar),
+ pCar->AutoPilot.m_nTimeToSpendOnCurrentCurve,
+ &positionIncludingCurve,
+ &directionIncludingCurve
+ );
+ CVector vectorBetweenNodes = pCurNode->pos - pNextNode->pos;
+ CVector finalPosition = positionIncludingCurve + vectorBetweenNodes * 2.0f / vectorBetweenNodes.Magnitude();
+ finalPosition.z = positionBetweenNodes * pNextNode->pos.z +
+ (1.0f - positionBetweenNodes) * pCurNode->pos.z;
+ float groundZ = INFINITE_Z;
+ 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 == INFINITE_Z || 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();
+}
+
+int32
+CCarCtrl::ChooseModel(CZoneInfo* pZone, CVector* pPos, int* pClass) {
+ int32 model = -1;;
+ while (model == -1 || !CStreaming::HasModelLoaded(model)){
+ int rnd = CGeneral::GetRandomNumberInRange(0, 1000);
+ if (rnd < pZone->carThreshold[0])
+ model = CCarCtrl::ChooseCarModel((*pClass = POOR));
+ else if (rnd < pZone->carThreshold[1])
+ model = CCarCtrl::ChooseCarModel((*pClass = RICH));
+ else if (rnd < pZone->carThreshold[2])
+ model = CCarCtrl::ChooseCarModel((*pClass = EXEC));
+ else if (rnd < pZone->carThreshold[3])
+ model = CCarCtrl::ChooseCarModel((*pClass = WORKER));
+ else if (rnd < pZone->carThreshold[4])
+ model = CCarCtrl::ChooseCarModel((*pClass = SPECIAL));
+ else if (rnd < pZone->carThreshold[5])
+ model = CCarCtrl::ChooseCarModel((*pClass = BIG));
+ else if (rnd < pZone->copThreshold)
+ *pClass = COPS, model = CCarCtrl::ChoosePoliceCarModel();
+ else if (rnd < pZone->gangThreshold[0])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = MAFIA) - MAFIA);
+ else if (rnd < pZone->gangThreshold[1])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = TRIAD) - MAFIA);
+ else if (rnd < pZone->gangThreshold[2])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = DIABLO) - MAFIA);
+ else if (rnd < pZone->gangThreshold[3])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = YAKUZA) - MAFIA);
+ else if (rnd < pZone->gangThreshold[4])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = YARDIE) - MAFIA);
+ else if (rnd < pZone->gangThreshold[5])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = COLOMB) - MAFIA);
+ else if (rnd < pZone->gangThreshold[6])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = NINES) - MAFIA);
+ else if (rnd < pZone->gangThreshold[7])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = GANG8) - MAFIA);
+ else if (rnd < pZone->gangThreshold[8])
+ model = CCarCtrl::ChooseGangCarModel((*pClass = GANG9) - MAFIA);
+ else
+ model = CCarCtrl::ChooseCarModel((*pClass = TAXI));
+ }
+ return model;
+}
+
+int32
+CCarCtrl::ChooseCarModel(int32 vehclass)
+{
+ int32 model = -1;
+ switch (vehclass) {
+ case POOR:
+ case RICH:
+ case EXEC:
+ case WORKER:
+ case SPECIAL:
+ case BIG:
+ case TAXI:
+ {
+ if (TotalNumOfCarsOfRating[vehclass] == 0)
+ debug("ChooseCarModel : No cars of type %d have been declared\n");
+ model = CarArrays[vehclass][NextCarOfRating[vehclass]];
+ int32 total = TotalNumOfCarsOfRating[vehclass];
+ NextCarOfRating[vehclass] += 1 + CGeneral::GetRandomNumberInRange(0, total - 1);
+ while (NextCarOfRating[vehclass] >= total)
+ NextCarOfRating[vehclass] -= total;
+ //NextCarOfRating[vehclass] %= total;
+ TotalNumOfCarsOfRating[vehclass] = total; /* why... */
+ }
+ default:
+ break;
+ }
+ return model;
+}
+
+int32
+CCarCtrl::ChoosePoliceCarModel(void)
+{
+ if (FindPlayerPed()->m_pWanted->AreSwatRequired() &&
+ CStreaming::HasModelLoaded(MI_ENFORCER) &&
+ CStreaming::HasModelLoaded(MI_POLICE))
+ return ((CGeneral::GetRandomNumber() & 0xF) == 0) ? MI_ENFORCER : MI_POLICE;
+ if (FindPlayerPed()->m_pWanted->AreFbiRequired() &&
+ CStreaming::HasModelLoaded(MI_FBICAR) &&
+ CStreaming::HasModelLoaded(MI_FBI))
+ return MI_FBICAR;
+ if (FindPlayerPed()->m_pWanted->AreArmyRequired() &&
+ CStreaming::HasModelLoaded(MI_RHINO) &&
+ CStreaming::HasModelLoaded(MI_BARRACKS) &&
+ CStreaming::HasModelLoaded(MI_RHINO))
+ return CGeneral::GetRandomTrueFalse() ? MI_BARRACKS : MI_RHINO;
+ return MI_POLICE;
+}
+
+int32
+CCarCtrl::ChooseGangCarModel(int32 gang)
+{
+ if (CStreaming::HasModelLoaded(MI_GANG01 + 2 * gang) &&
+ CStreaming::HasModelLoaded(MI_GANG02 + 2 * gang))
+ return CGangs::GetGangVehicleModel(gang);
+ return -1;
+}
+
+void
+CCarCtrl::AddToCarArray(int32 id, int32 vehclass)
+{
+ CarArrays[vehclass][TotalNumOfCarsOfRating[vehclass]++] = id;
+}
+
+void
+CCarCtrl::RemoveDistantCars()
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (--i){
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ PossiblyRemoveVehicle(pVehicle);
+ if (pVehicle->bCreateRoadBlockPeds){
+ if ((pVehicle->GetPosition() - FindPlayerCentreOfWorld(CWorld::PlayerInFocus)).Magnitude2D() < DISTANCE_TO_SPAWN_ROADBLOCK_PEDS) {
+ CRoadBlocks::GenerateRoadBlockCopsForCar(pVehicle, pVehicle->m_nRoadblockType, pVehicle->m_nRoadblockNode);
+ pVehicle->bCreateRoadBlockPeds = false;
+ }
+ }
+ }
+}
+
+void
+CCarCtrl::PossiblyRemoveVehicle(CVehicle* pVehicle)
+{
+ CVector vecPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus);
+ /* BUG: this variable is initialized only in if-block below but can be used outside of it. */
+ if (!IsThisVehicleInteresting(pVehicle) && !pVehicle->bIsLocked &&
+ pVehicle->CanBeDeleted() && !CCranes::IsThisCarBeingTargettedByAnyCrane(pVehicle)){
+ if (pVehicle->bFadeOut && CVisibilityPlugins::GetClumpAlpha(pVehicle->GetClump()) == 0){
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ return;
+ }
+ float distanceToPlayer = (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D();
+ float threshold = 50.0f;
+ if (pVehicle->GetIsOnScreen() ||
+ TheCamera.Cams[TheCamera.ActiveCam].LookingLeft ||
+ TheCamera.Cams[TheCamera.ActiveCam].LookingRight ||
+ TheCamera.Cams[TheCamera.ActiveCam].LookingBehind ||
+ TheCamera.GetLookDirection() == 0 ||
+ pVehicle->VehicleCreatedBy == PARKED_VEHICLE ||
+ pVehicle->GetModelIndex() == MI_AMBULAN ||
+ pVehicle->GetModelIndex() == MI_FIRETRUCK ||
+ pVehicle->bIsLawEnforcer ||
+ pVehicle->bIsCarParkVehicle
+ ){
+ threshold = 130.0f * TheCamera.GenerationDistMultiplier;
+ }
+ if (pVehicle->bExtendedRange)
+ threshold *= 1.5f;
+ if (distanceToPlayer > threshold && !CGarages::IsPointWithinHideOutGarage(&pVehicle->GetPosition())){
+ if (pVehicle->GetIsOnScreen() && CRenderer::IsEntityCullZoneVisible(pVehicle)){
+ pVehicle->bFadeOut = true;
+ }else{
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ }
+ return;
+ }
+ }
+ if ((pVehicle->m_status == STATUS_SIMPLE || pVehicle->m_status == STATUS_PHYSICS && pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS) &&
+ CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 5000 &&
+ !pVehicle->GetIsOnScreen() &&
+ (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D() > 25.0f &&
+ !IsThisVehicleInteresting(pVehicle) &&
+ !pVehicle->bIsLocked &&
+ !CTrafficLights::ShouldCarStopForLight(pVehicle, true) &&
+ !CTrafficLights::ShouldCarStopForBridge(pVehicle) &&
+ !CGarages::IsPointWithinHideOutGarage(&pVehicle->GetPosition())){
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ return;
+ }
+ if (pVehicle->m_status != STATUS_WRECKED || pVehicle->m_nTimeOfDeath == 0)
+ return;
+ if (CTimer::GetTimeInMilliseconds() > pVehicle->m_nTimeOfDeath + 60000 &&
+ (!pVehicle->GetIsOnScreen() || !CRenderer::IsEntityCullZoneVisible(pVehicle))){
+ if ((pVehicle->GetPosition() - vecPlayerPos).MagnitudeSqr() > SQR(7.5f)){
+ if (!CGarages::IsPointWithinHideOutGarage(&pVehicle->GetPosition())){
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ }
+ }
+ }
+}
+
+int32
+CCarCtrl::CountCarsOfType(int32 mi)
+{
+ int32 total = 0;
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--){
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ if (pVehicle->GetModelIndex() == mi)
+ total++;
+ }
+ return total;
+}
+
+bool
+CCarCtrl::IsThisVehicleInteresting(CVehicle* pVehicle)
+{
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (apCarsToKeep[i] == pVehicle)
+ return true;
+ }
+ return false;
+}
+
+void
+CCarCtrl::UpdateCarOnRails(CVehicle* pVehicle)
+{
+ if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_WAIT){
+ pVehicle->SetMoveSpeed(0.0f, 0.0f, 0.0f);
+ pVehicle->AutoPilot.ModifySpeed(0.0f);
+ if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTempAction){
+ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
+ pVehicle->AutoPilot.m_nAntiReverseTimer = 0;
+ pVehicle->AutoPilot.m_nTimeToStartMission = 0;
+ }
+ return;
+ }
+ SlowCarOnRailsDownForTrafficAndLights(pVehicle);
+ if (pVehicle->AutoPilot.m_nTimeEnteredCurve + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve <= CTimer::GetTimeInMilliseconds())
+ PickNextNodeAccordingStrategy(pVehicle);
+ if (pVehicle->m_status == STATUS_PHYSICS)
+ return;
+ CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo];
+ CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo];
+ float currentPathLinkForwardX = pCurrentLink->dirX * pVehicle->AutoPilot.m_nCurrentDirection;
+ float currentPathLinkForwardY = pCurrentLink->dirY * pVehicle->AutoPilot.m_nCurrentDirection;
+ float nextPathLinkForwardX = pNextLink->dirX * pVehicle->AutoPilot.m_nNextDirection;
+ float nextPathLinkForwardY = pNextLink->dirY * pVehicle->AutoPilot.m_nNextDirection;
+ CVector positionOnCurrentLinkIncludingLane(
+ pCurrentLink->posX + GetOffsetOfLaneFromCenterOfRoad(pVehicle->AutoPilot.m_nCurrentLane, pCurrentLink) * currentPathLinkForwardY,
+ pCurrentLink->posY - GetOffsetOfLaneFromCenterOfRoad(pVehicle->AutoPilot.m_nCurrentLane, pCurrentLink) * currentPathLinkForwardX,
+ 0.0f);
+ CVector positionOnNextLinkIncludingLane(
+ pNextLink->posX + GetOffsetOfLaneFromCenterOfRoad(pVehicle->AutoPilot.m_nNextLane, pNextLink) * nextPathLinkForwardY,
+ pNextLink->posY - GetOffsetOfLaneFromCenterOfRoad(pVehicle->AutoPilot.m_nNextLane, pNextLink) * nextPathLinkForwardX,
+ 0.0f);
+ CVector directionCurrentLink(currentPathLinkForwardX, currentPathLinkForwardY, 0.0f);
+ CVector directionNextLink(nextPathLinkForwardX, nextPathLinkForwardY, 0.0f);
+ CVector positionIncludingCurve;
+ CVector directionIncludingCurve;
+ CCurves::CalcCurvePoint(
+ &positionOnCurrentLinkIncludingLane,
+ &positionOnNextLinkIncludingLane,
+ &directionCurrentLink,
+ &directionNextLink,
+ GetPositionAlongCurrentCurve(pVehicle),
+ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve,
+ &positionIncludingCurve,
+ &directionIncludingCurve
+ );
+ positionIncludingCurve.z = 15.0f;
+ DragCarToPoint(pVehicle, &positionIncludingCurve);
+ pVehicle->SetMoveSpeed(directionIncludingCurve / 60.0f);
+}
+
+float
+CCarCtrl::FindMaximumSpeedForThisCarInTraffic(CVehicle* pVehicle)
+{
+ if (pVehicle->AutoPilot.m_nDrivingStyle == MISSION_RAMPLAYER_FARAWAY ||
+ pVehicle->AutoPilot.m_nDrivingStyle == MISSION_RAMPLAYER_CLOSE)
+ return pVehicle->AutoPilot.m_nCruiseSpeed;
+ float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_DANGER;
+ float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_DANGER;
+ float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_DANGER;
+ float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_DANGER;
+ int xstart = max(0, CWorld::GetSectorIndexX(left));
+ int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right));
+ int ystart = max(0, CWorld::GetSectorIndexY(top));
+ int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom));
+ assert(xstart <= xend);
+ assert(ystart <= yend);
+
+ float maxSpeed = pVehicle->AutoPilot.m_nCruiseSpeed;
+
+ CWorld::AdvanceCurrentScanCode();
+
+ for (int y = ystart; y <= yend; y++){
+ for (int x = xstart; x <= xend; x++){
+ CSector* s = CWorld::GetSector(x, y);
+ SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ }
+ }
+ pVehicle->bWarnedPeds = true;
+ if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS)
+ return maxSpeed;
+ return (maxSpeed + pVehicle->AutoPilot.m_nDrivingStyle) / 2;
+}
+
+void
+CCarCtrl::ScanForPedDanger(CVehicle* pVehicle)
+{
+ bool storedSlowDownFlag = pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds;
+ float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_DANGER;
+ float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_DANGER;
+ float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_DANGER;
+ float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_DANGER;
+ int xstart = max(0, CWorld::GetSectorIndexX(left));
+ int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right));
+ int ystart = max(0, CWorld::GetSectorIndexY(top));
+ int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom));
+ assert(xstart <= xend);
+ assert(ystart <= yend);
+
+ float maxSpeed = pVehicle->AutoPilot.m_nCruiseSpeed;
+
+ CWorld::AdvanceCurrentScanCode();
+
+ for (int y = ystart; y <= yend; y++) {
+ for (int x = xstart; x <= xend; x++) {
+ CSector* s = CWorld::GetSector(x, y);
+ SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed);
+ }
+ }
+ pVehicle->bWarnedPeds = true;
+ pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds = storedSlowDownFlag;
+}
+
+void
+CCarCtrl::SlowCarOnRailsDownForTrafficAndLights(CVehicle* pVehicle)
+{
+ float maxSpeed;
+ if (CTrafficLights::ShouldCarStopForLight(pVehicle, false) || CTrafficLights::ShouldCarStopForBridge(pVehicle)){
+ CCarAI::CarHasReasonToStop(pVehicle);
+ maxSpeed = 0.0f;
+ }else{
+ maxSpeed = FindMaximumSpeedForThisCarInTraffic(pVehicle);
+ }
+ float curSpeed = pVehicle->AutoPilot.m_fMaxTrafficSpeed;
+ if (maxSpeed >= curSpeed){
+ if (maxSpeed > curSpeed)
+ pVehicle->AutoPilot.ModifySpeed(min(maxSpeed, curSpeed + 0.05f * CTimer::GetTimeStep()));
+ }else{
+ if (curSpeed == 0.0f)
+ return;
+ if (curSpeed >= 0.1f)
+ pVehicle->AutoPilot.ModifySpeed(max(maxSpeed, curSpeed - 0.5f * CTimer::GetTimeStep()));
+ else if (curSpeed != 0.0f) /* no need to check */
+ pVehicle->AutoPilot.ModifySpeed(0.0f);
+ }
+}
bool
CCarCtrl::MapCouldMoveInThisArea(float x, float y)
@@ -29,3 +877,11 @@ 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);
+InjectHook(0x417EC0, &CCarCtrl::ChooseModel, PATCH_JUMP);
+InjectHook(0x418320, &CCarCtrl::RemoveDistantCars, PATCH_JUMP);
+InjectHook(0x418430, &CCarCtrl::PossiblyRemoveVehicle, PATCH_JUMP);
+InjectHook(0x418C10, &CCarCtrl::FindMaximumSpeedForThisCarInTraffic, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h
index 2ad52d49..735dc89c 100644
--- a/src/control/CarCtrl.h
+++ b/src/control/CarCtrl.h
@@ -1,9 +1,38 @@
#pragma once
+#include "PathFind.h"
+#include "Vehicle.h"
-class CVehicle;
+class CZoneInfo;
+
+enum{
+ MAX_CARS_TO_KEEP = 2,
+ MAX_CAR_MODELS_IN_ARRAY = 256,
+};
+
+#define LANE_WIDTH 5.0f
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 +45,35 @@ 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 ChooseGangCarModel(int32 gang);
+ static void RemoveDistantCars(void);
+ static void PossiblyRemoveVehicle(CVehicle*);
+ static bool IsThisVehicleInteresting(CVehicle*);
+ static int32 CountCarsOfType(int32 mi);
+ static void SlowCarOnRailsDownForTrafficAndLights(CVehicle*);
+ static void PickNextNodeAccordingStrategy(CVehicle*);
+ static void DragCarToPoint(CVehicle*, CVector*);
+ static float FindMaximumSpeedForThisCarInTraffic(CVehicle*);
+ static void SlowCarDownForCarsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float);
+ static void SlowCarDownForPedsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float);
+
+
+ static float GetOffsetOfLaneFromCenterOfRoad(int8 lane, CCarPathLink* pLink)
+ {
+ return (lane + ((pLink->numLeftLanes == 0) ? (0.5f - 0.5f * pLink->numRightLanes) :
+ ((pLink->numRightLanes == 0) ? (0.5f - 0.5f * pLink->numLeftLanes) : 0.5f))) * LANE_WIDTH;
+ }
+
+ static float GetPositionAlongCurrentCurve(CVehicle* pVehicle)
+ {
+ uint32 timeInCurve = CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeEnteredCurve;
+ return (float)timeInCurve / pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve;
+ }
static int32 &NumLawEnforcerCars;
static int32 &NumAmbulancesOnDuty;
@@ -25,4 +83,12 @@ public:
static int32 &NumParkedCars;
static bool &bCarsGeneratedAroundCamera;
static float &CarDensityMultiplier;
+ static int8 &CountDownToCarsAtStart;
+ static int32 &MaxNumberOfCarsInUse;
+ static uint32 &LastTimeLawEnforcerCreated;
+ static int32 (&TotalNumOfCarsOfRating)[7];
+ static int32 (&NextCarOfRating)[7];
+ static int32 (&CarArrays)[7][MAX_CAR_MODELS_IN_ARRAY];
};
+
+extern CVehicle* (&apCarsToKeep)[MAX_CARS_TO_KEEP]; \ No newline at end of file
diff --git a/src/control/Cranes.cpp b/src/control/Cranes.cpp
new file mode 100644
index 00000000..f641bc75
--- /dev/null
+++ b/src/control/Cranes.cpp
@@ -0,0 +1,5 @@
+#include "common.h"
+#include "patcher.h"
+#include "Cranes.h"
+
+WRAPPER bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle*) { EAXJMP(0x5451E0); }
diff --git a/src/control/Cranes.h b/src/control/Cranes.h
new file mode 100644
index 00000000..e262d0c3
--- /dev/null
+++ b/src/control/Cranes.h
@@ -0,0 +1,10 @@
+#pragma once
+#include "common.h"
+
+class CVehicle;
+
+class CCranes
+{
+public:
+ static bool IsThisCarBeingTargettedByAnyCrane(CVehicle*);
+};
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/Gangs.cpp b/src/control/Gangs.cpp
index fc77ad72..9ff40ef3 100644
--- a/src/control/Gangs.cpp
+++ b/src/control/Gangs.cpp
@@ -25,7 +25,7 @@ void CGangs::Initialize(void)
Gang[GANG_8].m_nVehicleMI = -1;
}
-void CGangs::SetGangVehicleModel(int16 gang, int model)
+void CGangs::SetGangVehicleModel(int16 gang, int32 model)
{
GetGangInfo(gang)->m_nVehicleMI = model;
}
diff --git a/src/control/Gangs.h b/src/control/Gangs.h
index 2366614b..dd24ddcb 100644
--- a/src/control/Gangs.h
+++ b/src/control/Gangs.h
@@ -33,7 +33,8 @@ class CGangs
{
public:
static void Initialize(void);
- static void SetGangVehicleModel(int16, int);
+ static void SetGangVehicleModel(int16, int32);
+ static int32 GetGangVehicleModel(int16 gang) { return Gang[gang].m_nVehicleMI; }
static void SetGangWeapons(int16, eWeaponType, eWeaponType);
static void SetGangPedModelOverride(int16, int8);
static int8 GetGangPedModelOverride(int16);
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 0629ac0c..3c5c142c 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -69,6 +69,7 @@ bool CGarages::HasCarBeenCrushed(int32 handle)
}
WRAPPER void CGarages::TriggerMessage(char *text, int16, uint16 time, int16) { EAXJMP(0x426B20); }
+WRAPPER bool CGarages::IsPointWithinHideOutGarage(CVector*) { EAXJMP(0x428260); }
#if 0
WRAPPER void CGarages::PrintMessages(void) { EAXJMP(0x426310); }
diff --git a/src/control/Garages.h b/src/control/Garages.h
index 45a9345d..d338c71b 100644
--- a/src/control/Garages.h
+++ b/src/control/Garages.h
@@ -25,4 +25,5 @@ public:
static void TriggerMessage(char *text, int16, uint16 time, int16);
static void PrintMessages(void);
static bool HasCarBeenCrushed(int32);
+ static bool IsPointWithinHideOutGarage(CVector*);
};
diff --git a/src/control/PathFind.cpp b/src/control/PathFind.cpp
index f90e0c8f..e9b33395 100644
--- a/src/control/PathFind.cpp
+++ b/src/control/PathFind.cpp
@@ -1,42 +1,25 @@
#include "common.h"
#include "patcher.h"
+#include "General.h"
+#include "FileMgr.h" // only needed for empty function
+#include "Camera.h"
+#include "Vehicle.h"
+#include "World.h"
#include "PathFind.h"
CPathFind &ThePaths = *(CPathFind*)0x8F6754;
-WRAPPER int32 CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool disabled, bool betweenLevels) { EAXJMP(0x42CC30); }
-WRAPPER CPathNode** CPathFind::FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*) { EAXJMP(0x42B9F0); }
-int TempListLength;
-
enum
{
NodeTypeExtern = 1,
NodeTypeIntern = 2,
- PathTypeCar = 0,
- PathTypePed = 1,
-
- PathNodeFlag1 = 1, // used?
- PathNodeFlag2 = 2,
- PathNodeDeadEnd = 4,
- PathNodeDisabled = 8,
- PathNodeBetweenLevels = 0x10,
+ ObjectFlag1 = 1,
+ ObjectEastWest = 2,
- ConnectionCrossRoad = 1,
- ConnectionTrafficLight = 2,
+ MAX_DIST = INT16_MAX-1
};
-// link flags:
-// 1: crosses road
-// 2: ped traffic light
-// pathnode flags:
-// 1:
-// 2:
-// 4: dead end
-// 8: switched off
-// 10: road between levels??
-// navi node flags:
-// 1: bridge light
// object flags:
// 1
// 2 east/west road(?)
@@ -48,10 +31,55 @@ CTempDetachedNode *&DetachedNodesCars = *(CTempDetachedNode**)0x8E2824;
CTempDetachedNode *&DetachedNodesPeds = *(CTempDetachedNode**)0x8E28A0;
void
-CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing)
+CPathFind::Init(void)
{
int i;
+ m_numPathNodes = 0;
+ m_numMapObjects = 0;
+ m_numConnections = 0;
+ m_numCarPathLinks = 0;
+ unk = 0;
+
+ for(i = 0; i < NUM_PATHNODES; i++)
+ m_pathNodes[i].distance = MAX_DIST;
+}
+
+void
+CPathFind::AllocatePathFindInfoMem(int16 numPathGroups)
+{
+ delete[] InfoForTileCars;
+ InfoForTileCars = nil;
+ delete[] InfoForTilePeds;
+ InfoForTilePeds = nil;
+
+ InfoForTileCars = new CPathInfoForObject[12*numPathGroups];
+ memset(InfoForTileCars, 0, 12*numPathGroups*sizeof(CPathInfoForObject));
+ InfoForTilePeds = new CPathInfoForObject[12*numPathGroups];
+ memset(InfoForTilePeds, 0, 12*numPathGroups*sizeof(CPathInfoForObject));
+
+ // unused
+ delete[] DetachedNodesCars;
+ DetachedNodesCars = nil;
+ delete[] DetachedNodesPeds;
+ DetachedNodesPeds = nil;
+ DetachedNodesCars = new CTempDetachedNode[100];
+ memset(DetachedNodesCars, 0, 100*sizeof(CTempDetachedNode));
+ DetachedNodesPeds = new CTempDetachedNode[50];
+ memset(DetachedNodesPeds, 0, 50*sizeof(CTempDetachedNode));
+}
+
+void
+CPathFind::RegisterMapObject(CTreadable *mapObject)
+{
+ m_mapObjects[m_numMapObjects++] = mapObject;
+}
+
+void
+CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing)
+{
+ int i, j;
+
i = id*12 + node;
InfoForTilePeds[i].type = type;
InfoForTilePeds[i].next = next;
@@ -61,12 +89,23 @@ CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x,
InfoForTilePeds[i].numLeftLanes = 0;
InfoForTilePeds[i].numRightLanes = 0;
InfoForTilePeds[i].crossing = crossing;
+
+ if(type)
+ for(i = 0; i < node; i++){
+ j = id*12 + i;
+ if(x == InfoForTilePeds[j].x && y == InfoForTilePeds[j].y){
+ printf("^^^^^^^^^^^^^ AARON IS TOO CHICKEN TO EAT MEAT!\n");
+ printf("Several ped nodes on one road segment have identical coordinates (%d==%d && %d==%d)\n",
+ x, InfoForTilePeds[j].x, y, InfoForTilePeds[j].y);
+ printf("Modelindex of cullprit: %d\n\n", id);
+ }
+ }
}
void
CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight)
{
- int i;
+ int i, j;
i = id*12 + node;
InfoForTileCars[i].type = type;
@@ -76,12 +115,34 @@ CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x,
InfoForTileCars[i].z = z;
InfoForTileCars[i].numLeftLanes = numLeft;
InfoForTileCars[i].numRightLanes = numRight;
+
+ if(type)
+ for(i = 0; i < node; i++){
+ j = id*12 + i;
+ if(x == InfoForTileCars[j].x && y == InfoForTileCars[j].y){
+ printf("^^^^^^^^^^^^^ AARON IS TOO CHICKEN TO EAT MEAT!\n");
+ printf("Several car nodes on one road segment have identical coordinates (%d==%d && %d==%d)\n",
+ x, InfoForTileCars[j].x, y, InfoForTileCars[j].y);
+ printf("Modelindex of cullprit: %d\n\n", id);
+ }
+ }
}
void
-CPathFind::RegisterMapObject(CTreadable *mapObject)
+CPathFind::CalcNodeCoors(int16 x, int16 y, int16 z, int id, CVector *out)
{
- m_mapObjects[m_numMapObjects++] = mapObject;
+ CVector pos;
+ pos.x = x / 16.0f;
+ pos.y = y / 16.0f;
+ pos.z = z / 16.0f;
+ *out = m_mapObjects[id]->GetMatrix() * pos;
+}
+
+bool
+CPathFind::LoadPathFindData(void)
+{
+ CFileMgr::SetDir("");
+ return false;
}
void
@@ -93,14 +154,14 @@ CPathFind::PreparePathData(void)
CTempNode *tempNodes;
printf("PreparePathData\n");
- // UNUSED: CPathFind::LoadPathFindData
- if(InfoForTileCars && InfoForTilePeds &&
+ if(!CPathFind::LoadPathFindData() && // empty
+ InfoForTileCars && InfoForTilePeds &&
DetachedNodesCars && DetachedNodesPeds){
tempNodes = new CTempNode[4000];
m_numConnections = 0;
for(i = 0; i < PATHNODESIZE; i++)
- m_pathNodes[i].flags &= ~(PathNodeFlag1 | PathNodeFlag2);
+ m_pathNodes[i].unkBits = 0;
for(i = 0; i < PATHNODESIZE; i++){
numExtern = 0;
@@ -127,9 +188,9 @@ CPathFind::PreparePathData(void)
}
m_numPathNodes = 0;
- PreparePathDataForType(PathTypeCar, tempNodes, InfoForTileCars, 1.0f, DetachedNodesCars, 100);
+ PreparePathDataForType(PATH_CAR, tempNodes, InfoForTileCars, 1.0f, DetachedNodesCars, 100);
m_numCarPathNodes = m_numPathNodes;
- PreparePathDataForType(PathTypePed, tempNodes, InfoForTilePeds, 1.0f, DetachedNodesPeds, 50);
+ PreparePathDataForType(PATH_PED, tempNodes, InfoForTilePeds, 1.0f, DetachedNodesPeds, 50);
m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes;
// TODO: figure out what exactly is going on here
@@ -155,26 +216,26 @@ CPathFind::PreparePathData(void)
if(numIntern == 1 && numExtern == 2){
if(numLanes < 4){
if((i & 7) == 4){ // WHAT?
- m_objectFlags[i] |= PathNodeFlag1;
+ m_objectFlags[i] |= ObjectFlag1;
if(maxX > maxY)
- m_objectFlags[i] |= PathNodeFlag2;
+ m_objectFlags[i] |= ObjectEastWest;
else
- m_objectFlags[i] &= ~PathNodeFlag2;
+ m_objectFlags[i] &= ~ObjectEastWest;
}
}else{
- m_objectFlags[i] |= PathNodeFlag1;
+ m_objectFlags[i] |= ObjectFlag1;
if(maxX > maxY)
- m_objectFlags[i] |= PathNodeFlag2;
+ m_objectFlags[i] |= ObjectEastWest;
else
- m_objectFlags[i] &= ~PathNodeFlag2;
+ m_objectFlags[i] &= ~ObjectEastWest;
}
}
}
delete[] tempNodes;
- CountFloodFillGroups(PathTypeCar);
- CountFloodFillGroups(PathTypePed);
+ CountFloodFillGroups(PATH_CAR);
+ CountFloodFillGroups(PATH_PED);
delete[] InfoForTileCars;
InfoForTileCars = nil;
@@ -198,11 +259,11 @@ CPathFind::CountFloodFillGroups(uint8 type)
CPathNode *node, *prev;
switch(type){
- case PathTypeCar:
+ case PATH_CAR:
start = 0;
end = m_numCarPathNodes;
break;
- case PathTypePed:
+ case PATH_PED:
start = m_numCarPathNodes;
end = start + m_numPedPathNodes;
break;
@@ -229,7 +290,7 @@ CPathFind::CountFloodFillGroups(uint8 type)
node->group = n;
if(node->numLinks == 0){
- if(type == PathTypeCar)
+ if(type == PATH_CAR)
printf("Single car node: %f %f %f (%d)\n",
node->pos.x, node->pos.y, node->pos.z,
m_mapObjects[node->objectIndex]->m_modelIndex);
@@ -258,6 +319,8 @@ CPathFind::CountFloodFillGroups(uint8 type)
printf("GraphType:%d. FloodFill groups:%d\n", type, n);
}
+int32 TempListLength;
+
void
CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float maxdist, CTempDetachedNode *detachednodes, int unused)
@@ -265,7 +328,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
static CVector CoorsXFormed;
int i, j, k, l;
int l1, l2;
- int start, typeoff;
+ int start;
float posx, posy;
float dx, dy, mag;
float nearestDist;
@@ -277,14 +340,13 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
int istart, jstart;
int done, cont;
- typeoff = 12*type;
oldNumPathNodes = m_numPathNodes;
oldNumLinks = m_numConnections;
// Initialize map objects
for(i = 0; i < m_numMapObjects; i++)
for(j = 0; j < 12; j++)
- m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = -1;
+ m_mapObjects[i]->m_nodeIndices[type][j] = -1;
// Calculate internal nodes, store them and connect them to defining object
for(i = 0; i < m_numMapObjects; i++){
@@ -300,8 +362,8 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
&CoorsXFormed);
m_pathNodes[m_numPathNodes].pos = CoorsXFormed;
m_pathNodes[m_numPathNodes].objectIndex = i;
- m_pathNodes[m_numPathNodes].flags |= PathNodeFlag1;
- m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = m_numPathNodes++;
+ m_pathNodes[m_numPathNodes].unkBits = 1;
+ m_mapObjects[i]->m_nodeIndices[type][j] = m_numPathNodes++;
}
}
@@ -347,8 +409,8 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
next++;
}
// link to connecting internal node
- tempnodes[TempListLength].link1 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next];
- if(type == PathTypeCar){
+ tempnodes[TempListLength].link1 = m_mapObjects[i]->m_nodeIndices[type][next];
+ if(type == PATH_CAR){
tempnodes[TempListLength].numLeftLanes = objectpathinfo[start + j].numLeftLanes;
tempnodes[TempListLength].numRightLanes = objectpathinfo[start + j].numRightLanes;
}
@@ -362,7 +424,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
for(k = start; j != objectpathinfo[k].next; k++)
next++;
}
- tempnodes[nearestId].link2 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next];
+ tempnodes[nearestId].link2 = m_mapObjects[i]->m_nodeIndices[type][next];
tempnodes[nearestId].linkState = 2;
// collapse this node with nearest we found
@@ -373,7 +435,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
tempnodes[nearestId].dirX = dx/mag;
tempnodes[nearestId].dirY = dy/mag;
// do something when number of lanes doesn't agree
- if(type == PathTypeCar)
+ if(type == PATH_CAR)
if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 &&
(objectpathinfo[start + j].numLeftLanes == 0 || objectpathinfo[start + j].numRightLanes == 0)){
// why switch left and right here?
@@ -405,9 +467,9 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
dist = m_pathNodes[i].pos - m_pathNodes[m_connections[m_numConnections]].pos;
m_distances[m_numConnections] = dist.Magnitude();
- m_connectionFlags[m_numConnections] = 0;
+ m_connectionFlags[m_numConnections].flags = 0;
- if(type == PathTypeCar){
+ if(type == PATH_CAR){
// IMPROVE: use a goto here
// Find existing car path link
for(k = 0; k < m_numCarPathLinks; k++){
@@ -459,7 +521,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
dist = m_pathNodes[i].pos - m_pathNodes[j].pos;
m_distances[m_numConnections] = dist.Magnitude();
- if(type == PathTypeCar){
+ if(type == PATH_CAR){
posx = (m_pathNodes[i].pos.x + m_pathNodes[j].pos.x)*0.5f;
posy = (m_pathNodes[i].pos.y + m_pathNodes[j].pos.y)*0.5f;
dx = m_pathNodes[j].pos.x - m_pathNodes[i].pos.x;
@@ -498,9 +560,9 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
// Crosses road
if(objectpathinfo[istart + iseg].next == jseg && objectpathinfo[istart + iseg].crossing ||
objectpathinfo[jstart + jseg].next == iseg && objectpathinfo[jstart + jseg].crossing)
- m_connectionFlags[m_numConnections] |= ConnectionCrossRoad;
+ m_connectionFlags[m_numConnections].bCrossesRoad = true;
else
- m_connectionFlags[m_numConnections] &= ~ConnectionCrossRoad;
+ m_connectionFlags[m_numConnections].bCrossesRoad = false;
}
m_pathNodes[i].numLinks++;
@@ -509,7 +571,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
}
}
- if(type == PathTypeCar){
+ if(type == PATH_CAR){
done = 0;
// Set number of lanes for all nodes somehow
// very strange code
@@ -563,20 +625,20 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
}
// Set flags for car nodes
- if(type == PathTypeCar){
+ if(type == PATH_CAR){
do{
cont = 0;
for(i = 0; i < m_numPathNodes; i++){
- m_pathNodes[i].flags &= ~PathNodeDisabled;
- m_pathNodes[i].flags &= ~PathNodeBetweenLevels;
+ m_pathNodes[i].bDisabled = false;
+ m_pathNodes[i].bBetweenLevels = false;
// See if node is a dead end, if so, we're not done yet
- if((m_pathNodes[i].flags & PathNodeDeadEnd) == 0){
+ if(!m_pathNodes[i].bDeadEnd){
k = 0;
for(j = 0; j < m_pathNodes[i].numLinks; j++)
- if((m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].flags & PathNodeDeadEnd) == 0)
+ if(!m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].bDeadEnd)
k++;
if(k < 2){
- m_pathNodes[i].flags |= PathNodeDeadEnd;
+ m_pathNodes[i].bDeadEnd = true;
cont = 1;
}
}
@@ -585,7 +647,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
}
// Remove isolated ped nodes
- if(type == PathTypePed)
+ if(type == PATH_PED)
for(i = oldNumPathNodes; i < m_numPathNodes; i++){
if(m_pathNodes[i].numLinks != 0)
continue;
@@ -602,13 +664,13 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
// Also in treadables
for(j = 0; j < m_numMapObjects; j++)
for(k = 0; k < 12; k++){
- if(m_mapObjects[j]->m_nodeIndicesPeds[k] == i){
+ if(m_mapObjects[j]->m_nodeIndices[PATH_PED][k] == i){
// remove this one
for(l = k; l < 12-1; l++)
- m_mapObjects[j]->m_nodeIndicesPeds[l] = m_mapObjects[j]->m_nodeIndicesPeds[l+1];
- m_mapObjects[j]->m_nodeIndicesPeds[11] = -1;
- }else if(m_mapObjects[j]->m_nodeIndicesPeds[k] > i)
- m_mapObjects[j]->m_nodeIndicesPeds[k]--;
+ m_mapObjects[j]->m_nodeIndices[PATH_PED][l] = m_mapObjects[j]->m_nodeIndices[PATH_PED][l+1];
+ m_mapObjects[j]->m_nodeIndices[PATH_PED][11] = -1;
+ }else if(m_mapObjects[j]->m_nodeIndices[PATH_PED][k] > i)
+ m_mapObjects[j]->m_nodeIndices[PATH_PED][k]--;
}
i--;
@@ -616,20 +678,810 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor
}
}
+float
+CPathFind::CalcRoadDensity(float x, float y)
+{
+ int i, j;
+ float density = 0.0f;
+
+ for(i = 0; i < m_numCarPathNodes; i++){
+ if(Abs(m_pathNodes[i].pos.x - x) < 80.0f &&
+ Abs(m_pathNodes[i].pos.y - y) < 80.0f &&
+ m_pathNodes[i].numLinks > 0){
+ for(j = 0; j < m_pathNodes[i].numLinks; j++){
+ int next = m_connections[m_pathNodes[i].firstLink + j];
+ float dist = (m_pathNodes[i].pos - m_pathNodes[next].pos).Magnitude2D();
+ next = m_carPathConnections[m_pathNodes[i].firstLink + j];
+ density += m_carPathLinks[next].numLeftLanes * dist;
+ density += m_carPathLinks[next].numRightLanes * dist;
+
+ if(m_carPathLinks[next].numLeftLanes < 0)
+ printf("Link from object %d to %d (MIs)\n",
+ m_mapObjects[m_pathNodes[i].objectIndex]->GetModelIndex(),
+ m_mapObjects[m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].objectIndex]->GetModelIndex());
+ if(m_carPathLinks[next].numRightLanes < 0)
+ printf("Link from object %d to %d (MIs)\n",
+ m_mapObjects[m_pathNodes[i].objectIndex]->GetModelIndex(),
+ m_mapObjects[m_pathNodes[m_connections[m_pathNodes[i].firstLink + j]].objectIndex]->GetModelIndex());
+ }
+ }
+ }
+ return density/2500.0f;
+}
+
+bool
+CPathFind::TestForPedTrafficLight(CPathNode *n1, CPathNode *n2)
+{
+ int i;
+ for(i = 0; i < n1->numLinks; i++)
+ if(&m_pathNodes[m_connections[n1->firstLink + i]] == n2)
+ return m_connectionFlags[n1->firstLink + i].bTrafficLight;
+ return false;
+}
+
+bool
+CPathFind::TestCrossesRoad(CPathNode *n1, CPathNode *n2)
+{
+ int i;
+ for(i = 0; i < n1->numLinks; i++)
+ if(&m_pathNodes[m_connections[n1->firstLink + i]] == n2)
+ return m_connectionFlags[n1->firstLink + i].bCrossesRoad;
+ return false;
+}
+
void
-CPathFind::CalcNodeCoors(int16 x, int16 y, int16 z, int id, CVector *out)
+CPathFind::AddNodeToList(CPathNode *node, int32 listId)
{
- CVector pos;
- pos.x = x / 16.0f;
- pos.y = y / 16.0f;
- pos.z = z / 16.0f;
- *out = m_mapObjects[id]->GetMatrix() * pos;
+ int i = listId & 0x1FF;
+ node->next = m_searchNodes[i].next;
+ node->prev = &m_searchNodes[i];
+ if(m_searchNodes[i].next)
+ m_searchNodes[i].next->prev = node;
+ m_searchNodes[i].next = node;
+ node->distance = listId;
+}
+
+void
+CPathFind::RemoveNodeFromList(CPathNode *node)
+{
+ node->prev->next = node->next;
+ if(node->next)
+ node->next->prev = node->prev;
+}
+
+void
+CPathFind::RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n)
+{
+ int i;
+ if(*n < 2)
+ return;
+ if(DotProduct2D(nodes[1]->pos - pos, nodes[0]->pos - pos) < 0.0f){
+ (*n)--;
+ for(i = 0; i < *n; i++)
+ nodes[i] = nodes[i+1];
+ }
+}
+
+void
+CPathFind::SetLinksBridgeLights(float x1, float x2, float y1, float y2, bool enable)
+{
+ int i;
+ for(i = 0; i < m_numCarPathLinks; i++)
+ if(x1 < m_carPathLinks[i].posX && m_carPathLinks[i].posX < x2 &&
+ y1 < m_carPathLinks[i].posY && m_carPathLinks[i].posY < y2)
+ m_carPathLinks[i].bBridgeLights = enable;
+}
+
+void
+CPathFind::SwitchOffNodeAndNeighbours(int32 nodeId, bool disable)
+{
+ int i, next;
+
+ m_pathNodes[nodeId].bDisabled = disable;
+ if(m_pathNodes[nodeId].numLinks < 3)
+ for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
+ next = m_connections[m_pathNodes[nodeId].firstLink + i];
+ if(m_pathNodes[next].bDisabled != disable &&
+ m_pathNodes[next].numLinks < 3)
+ SwitchOffNodeAndNeighbours(next, disable);
+ }
+}
+
+void
+CPathFind::SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable)
+{
+ int i;
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 &&
+ y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 &&
+ z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2)
+ SwitchOffNodeAndNeighbours(i, disable);
+}
+
+void
+CPathFind::SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable)
+{
+ int i;
+
+ for(i = m_numCarPathNodes; i < m_numPathNodes; i++)
+ if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 &&
+ y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 &&
+ z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2)
+ SwitchOffNodeAndNeighbours(i, disable);
+}
+
+void
+CPathFind::SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable)
+{
+ int i;
+ int firstNode, lastNode;
+
+ if(type == PATH_CAR){
+ firstNode = 0;
+ lastNode = m_numCarPathNodes;
+ }else{
+ firstNode = m_numCarPathNodes;
+ lastNode = m_numPathNodes;
+ }
+
+ if(z1 > z2){
+ float tmp = z1;
+ z1 = z2;
+ z2 = tmp;
+ }
+
+ // angle of vector from p2 to p1
+ float angle = CGeneral::GetRadianAngleBetweenPoints(x1, y1, x2, y2) + HALFPI;
+ while(angle < TWOPI) angle += TWOPI;
+ while(angle > TWOPI) angle -= TWOPI;
+ // vector from p1 to p2
+ CVector2D v12(x2 - x1, y2 - y1);
+ float len12 = v12.Magnitude();
+ CVector2D vn12 = v12/len12;
+ // vector from p2 to new point p3
+ CVector2D v23(-Sin(angle)*length, Cos(angle)*length);
+ float len23 = v23.Magnitude(); // obivously just 'length' but whatever
+ CVector2D vn23 = v23/len23;
+
+ bool disable = !enable;
+ for(i = firstNode; i < lastNode; i++){
+ if(m_pathNodes[i].pos.z < z1 || m_pathNodes[i].pos.z > z2)
+ continue;
+ CVector2D d(m_pathNodes[i].pos.x - x1, m_pathNodes[i].pos.y - y1);
+ float dot = DotProduct2D(d, v12);
+ if(dot < 0.0f || dot > len12)
+ continue;
+ dot = DotProduct2D(d, v23);
+ if(dot < 0.0f || dot > len23)
+ continue;
+ if(m_pathNodes[i].bDisabled != disable)
+ SwitchOffNodeAndNeighbours(i, disable);
+ }
+}
+
+void
+CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId)
+{
+ int i, next;
+
+ m_pathNodes[nodeId].bBetweenLevels = true;
+ if(m_pathNodes[nodeId].numLinks < 3)
+ for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
+ next = m_connections[m_pathNodes[nodeId].firstLink + i];
+ if(!m_pathNodes[next].bBetweenLevels &&
+ m_pathNodes[next].numLinks < 3)
+ MarkRoadsBetweenLevelsNodeAndNeighbours(next);
+ }
+}
+
+void
+CPathFind::MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2)
+{
+ int i;
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 &&
+ y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 &&
+ z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2)
+ MarkRoadsBetweenLevelsNodeAndNeighbours(i);
+}
+
+void
+CPathFind::MarkPedRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2)
+{
+ int i;
+
+ for(i = m_numCarPathNodes; i < m_numPathNodes; i++)
+ if(x1 < m_pathNodes[i].pos.x && m_pathNodes[i].pos.x < x2 &&
+ y1 < m_pathNodes[i].pos.y && m_pathNodes[i].pos.y < y2 &&
+ z1 < m_pathNodes[i].pos.z && m_pathNodes[i].pos.z < z2)
+ MarkRoadsBetweenLevelsNodeAndNeighbours(i);
+}
+
+int32
+CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels)
+{
+ int i;
+ int firstNode, lastNode;
+ float dist;
+ float closestDist = 10000.0f;
+ int closestNode = 0;
+
+ switch(type){
+ case PATH_CAR:
+ firstNode = 0;
+ lastNode = m_numCarPathNodes;
+ break;
+ case PATH_PED:
+ firstNode = m_numCarPathNodes;
+ lastNode = m_numPathNodes;
+ break;
+ }
+
+ for(i = firstNode; i < lastNode; i++){
+ if(ignoreDisabled && m_pathNodes[i].bDisabled) continue;
+ if(ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue;
+ switch(m_pathNodes[i].unkBits){
+ case 1:
+ case 2:
+ dist = Abs(m_pathNodes[i].pos.x - coors.x) +
+ Abs(m_pathNodes[i].pos.y - coors.y) +
+ 3.0f*Abs(m_pathNodes[i].pos.z - coors.z);
+ if(dist < closestDist){
+ closestDist = dist;
+ closestNode = i;
+ }
+ break;
+ }
+ }
+ return closestDist < distLimit ? closestNode : -1;
+}
+
+int32
+CPathFind::FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY)
+{
+ int i;
+ int firstNode, lastNode;
+ float dist, dX, dY;
+ NormalizeXY(dirX, dirY);
+ float closestDist = 10000.0f;
+ int closestNode = 0;
+
+ switch(type){
+ case PATH_CAR:
+ firstNode = 0;
+ lastNode = m_numCarPathNodes;
+ break;
+ case PATH_PED:
+ firstNode = m_numCarPathNodes;
+ lastNode = m_numPathNodes;
+ break;
+ }
+
+ for(i = firstNode; i < lastNode; i++){
+ switch(m_pathNodes[i].unkBits){
+ case 1:
+ case 2:
+ dX = m_pathNodes[i].pos.x - coors.x;
+ dY = m_pathNodes[i].pos.y - coors.y;
+ dist = Abs(dX) + Abs(dY) +
+ 3.0f*Abs(m_pathNodes[i].pos.z - coors.z);
+ if(dist < closestDist){
+ NormalizeXY(dX, dY);
+ dist -= (dX*dirX + dY*dirY - 1.0f)*20.0f;
+ if(dist < closestDist){
+ closestDist = dist;
+ closestNode = i;
+ }
+ }
+ break;
+ }
+ }
+ return closestNode;
+}
+
+
+float
+CPathFind::FindNodeOrientationForCarPlacement(int32 nodeId)
+{
+ if(m_pathNodes[nodeId].numLinks == 0)
+ return 0.0;
+ CVector dir = m_pathNodes[m_connections[m_pathNodes[nodeId].firstLink]].pos - m_pathNodes[nodeId].pos;
+ dir.z = 0.0f;
+ dir.Normalise();
+ return RADTODEG(dir.Heading());
+}
+
+float
+CPathFind::FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards)
+{
+ int i;
+
+ CVector targetDir(x - m_pathNodes[nodeId].pos.x, y - m_pathNodes[nodeId].pos.y, 0.0f);
+ targetDir.Normalise();
+ CVector dir;
+
+ if(m_pathNodes[nodeId].numLinks == 0)
+ return 0.0;
+
+ int bestNode = m_connections[m_pathNodes[nodeId].firstLink];
+#ifdef FIX_BUGS
+ float bestDot = towards ? -2.0f : 2.0f;
+#else
+ int bestDot = towards ? -2 : 2; // why int?
+#endif
+
+ for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){
+ dir = m_pathNodes[m_connections[m_pathNodes[nodeId].firstLink + i]].pos - m_pathNodes[nodeId].pos;
+ dir.z = 0.0f;
+ dir.Normalise();
+ float angle = DotProduct2D(dir, targetDir);
+ if(towards){
+ if(angle > bestDot){
+ bestDot = angle;
+ bestNode = m_connections[m_pathNodes[nodeId].firstLink + i];
+ }
+ }else{
+ if(angle < bestDot){
+ bestDot = angle;
+ bestNode = m_connections[m_pathNodes[nodeId].firstLink + i];
+ }
+ }
+ }
+
+ dir = m_pathNodes[bestNode].pos - m_pathNodes[nodeId].pos;
+ dir.z = 0.0f;
+ dir.Normalise();
+ return RADTODEG(dir.Heading());
+}
+
+bool
+CPathFind::NewGenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled)
+{
+ int i, j;
+ int node1, node2;
+ float dist1, dist2, d1, d2;
+
+ if(m_numCarPathNodes == 0)
+ return false;
+
+ for(i = 0; i < 500; i++){
+ node1 = (CGeneral::GetRandomNumber()>>3) % m_numCarPathNodes;
+ if(m_pathNodes[node1].bDisabled && !ignoreDisabled)
+ continue;
+ dist1 = Distance2D(m_pathNodes[node1].pos, x, y);
+ if(dist1 < spawnDist + 60.0f){
+ d1 = dist1 - spawnDist;
+ for(j = 0; j < m_pathNodes[node1].numLinks; j++){
+ node2 = m_connections[m_pathNodes[node1].firstLink + j];
+ if(m_pathNodes[node2].bDisabled && !ignoreDisabled)
+ continue;
+ dist2 = Distance2D(m_pathNodes[node2].pos, x, y);
+ d2 = dist2 - spawnDist;
+ if(d1*d2 < 0.0f){
+ // nodes are on different sides of spawn distance
+ float f2 = Abs(d1)/(Abs(d1) + Abs(d2));
+ float f1 = 1.0f - f2;
+ *pPositionBetweenNodes = f2;
+ CVector pos = m_pathNodes[node1].pos*f1 + m_pathNodes[node2].pos*f2;
+ CVector2D dist2d(pos.x - x, pos.y - y);
+ dist2d.Normalise(); // done manually in the game
+ float dot = DotProduct2D(dist2d, CVector2D(dirX, dirY));
+ if(forward){
+ if(dot > angleLimit){
+ *pNode1 = node1;
+ *pNode2 = node2;
+ *pPosition = pos;
+ return true;
+ }
+ }else{
+ if(dot <= angleLimit){
+ *pNode1 = node1;
+ *pNode2 = node2;
+ *pPosition = pos;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool
+CPathFind::GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix)
+{
+ int i;
+ int node1, node2;
+
+ if(m_numPedPathNodes == 0)
+ return false;
+
+ for(i = 0; i < 400; i++){
+ node1 = m_numCarPathNodes + CGeneral::GetRandomNumber() % m_numPedPathNodes;
+ if(DistanceSqr2D(m_pathNodes[node1].pos, x, y) < sq(maxDist+30.0f)){
+ if(m_pathNodes[node1].numLinks == 0)
+ continue;
+ int link = m_pathNodes[node1].firstLink + CGeneral::GetRandomNumber() % m_pathNodes[node1].numLinks;
+ if(m_connectionFlags[link].bCrossesRoad)
+ continue;
+ node2 = m_connections[link];
+ if(m_pathNodes[node1].bDisabled || m_pathNodes[node2].bDisabled)
+ continue;
+
+ float f2 = (CGeneral::GetRandomNumber()&0xFF)/256.0f;
+ float f1 = 1.0f - f2;
+ *pPositionBetweenNodes = f2;
+ CVector pos = m_pathNodes[node1].pos*f1 + m_pathNodes[node2].pos*f2;
+ if(Distance2D(pos, x, y) < maxDist+20.0f){
+ pos.x += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f;
+ pos.y += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f;
+ float dist = Distance2D(pos, x, y);
+
+ bool visible;
+ if(camMatrix)
+ visible = TheCamera.IsSphereVisible(pos, 2.0f, camMatrix);
+ else
+ visible = TheCamera.IsSphereVisible(pos, 2.0f);
+ if(!visible){
+ minDist = minDistOffScreen;
+ maxDist = maxDistOffScreen;
+ }
+ if(minDist < dist && dist < maxDist){
+ *pNode1 = node1;
+ *pNode2 = node2;
+ *pPosition = pos;
+
+ bool found;
+ float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z+2.0f, &found);
+ if(!found)
+ return false;
+ if(Abs(groundZ - pos.z) > 3.0f)
+ return false;
+ pPosition->z = groundZ;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+CTreadable*
+CPathFind::FindRoadObjectClosestToCoors(CVector coors, uint8 type)
+{
+ int i, j, k;
+ int node1, node2;
+ CTreadable *closestMapObj = nil;
+ float closestDist = 10000.0f;
+
+ for(i = 0; i < m_numMapObjects; i++){
+ CTreadable *mapObj = m_mapObjects[i];
+ if(mapObj->m_nodeIndices[type][0] < 0)
+ continue;
+ CVector vDist = mapObj->GetPosition() - coors;
+ float fDist = Abs(vDist.x) + Abs(vDist.y) + Abs(vDist.z);
+ if(fDist < 200.0f || fDist < closestDist)
+ for(j = 0; j < 12; j++){
+ node1 = mapObj->m_nodeIndices[type][j];
+ if(node1 < 0)
+ break;
+ // FIX: game uses ThePaths here explicitly
+ for(k = 0; k < m_pathNodes[node1].numLinks; k++){
+ node2 = m_connections[m_pathNodes[node1].firstLink + k];
+ float lineDist = CCollision::DistToLine(&m_pathNodes[node1].pos, &m_pathNodes[node2].pos, &coors);
+ if(lineDist < closestDist){
+ closestDist = lineDist;
+ if((coors - m_pathNodes[node1].pos).MagnitudeSqr() < (coors - m_pathNodes[node2].pos).MagnitudeSqr())
+ closestMapObj = m_mapObjects[m_pathNodes[node1].objectIndex];
+ else
+ closestMapObj = m_mapObjects[m_pathNodes[node2].objectIndex];
+ }
+ }
+ }
+ }
+ return closestMapObj;
}
-WRAPPER void CPathFind::SetLinksBridgeLights(float, float, float, float, bool) { EAXJMP(0x42E3B0); }
+void
+CPathFind::FindNextNodeWandering(uint8 type, CVector coors, CPathNode **lastNode, CPathNode **nextNode, uint8 curDir, uint8 *nextDir)
+{
+ int i;
+ CPathNode *node;
+
+ if(lastNode == nil || (node = *lastNode) == nil || (coors - (*lastNode)->pos).MagnitudeSqr() > 7.0f){
+ // need to find the node we're coming from
+ node = nil;
+ CTreadable *obj = FindRoadObjectClosestToCoors(coors, type);
+ float nodeDist = 1000000000.0f;
+ for(i = 0; i < 12; i++){
+ if(obj->m_nodeIndices[i] < 0)
+ break;
+ float dist = (coors - m_pathNodes[obj->m_nodeIndices[type][i]].pos).Magnitude2D();
+ if(dist < nodeDist){
+ nodeDist = dist;
+ node = &m_pathNodes[obj->m_nodeIndices[type][i]];
+ }
+ }
+ }
+
+ CVector2D vCurDir(Cos(curDir*PI/4.0f), Sin(curDir*PI/4.0f));
+ *nextNode = 0;
+ float bestDot = -999999.0f;
+ for(i = 0; i < node->numLinks; i++){
+ int next = m_connections[node->firstLink+i];
+ if(node->bDisabled || m_pathNodes[next].bDisabled)
+ continue;
+ CVector pedCoors = coors;
+ pedCoors.z += 1.0f;
+ CVector nodeCoors = m_pathNodes[next].pos;
+ nodeCoors.z += 1.0f;
+ if(!CWorld::GetIsLineOfSightClear(pedCoors, nodeCoors, true, false, false, false, false, false))
+ continue;
+ CVector2D nodeDir = m_pathNodes[next].pos - node->pos;
+ nodeDir /= nodeDir.Magnitude();
+ float dot = DotProduct2D(nodeDir, vCurDir);
+ if(dot > bestDot){
+ *nextNode = &m_pathNodes[next];
+ bestDot = dot;
+
+ // direction is 0, 2, 4, 6 for north, east, south, west
+ // this could be sone simpler...
+ if(nodeDir.x < 0.0f){
+ if(2.0f*Abs(nodeDir.y) < -nodeDir.x)
+ *nextDir = 6; // west
+ else if(-2.0f*nodeDir.x < nodeDir.y)
+ *nextDir = 0; // north
+ else if(2.0f*nodeDir.x > nodeDir.y)
+ *nextDir = 4; // south
+ else if(nodeDir.y > 0.0f)
+ *nextDir = 7; // north west
+ else
+ *nextDir = 5; // south west`
+ }else{
+ if(2.0f*Abs(nodeDir.y) < nodeDir.x)
+ *nextDir = 2; // east
+ else if(2.0f*nodeDir.x < nodeDir.y)
+ *nextDir = 0; // north
+ else if(-2.0f*nodeDir.x > nodeDir.y)
+ *nextDir = 4; // south
+ else if(nodeDir.y > 0.0f)
+ *nextDir = 1; // north east
+ else
+ *nextDir = 3; // south east`
+ }
+ }
+ }
+ if(*nextNode == nil){
+ *nextDir = 0;
+ *nextNode = node;
+ }
+}
+
+static CPathNode *apNodesToBeCleared[4995];
+
+void
+CPathFind::DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *pNumNodes, int16 maxNumNodes, CVehicle *vehicle, float *pDist, float distLimit, int32 forcedTargetNode)
+{
+ int i, j;
+
+ // Find target
+ int targetNode;
+ if(forcedTargetNode < 0)
+ targetNode = FindNodeClosestToCoors(target, type, distLimit);
+ else
+ targetNode = forcedTargetNode;
+ if(targetNode < 0)
+ goto fail;
+
+ // Find start
+ int numPathsToTry;
+ CTreadable *startObj;
+ if(startNodeId < 0){
+ if(vehicle == nil || (startObj = vehicle->m_treadable[type]) == nil)
+ startObj = FindRoadObjectClosestToCoors(start, type);
+ numPathsToTry = 0;
+ for(i = 0; i < 12; i++){
+ if(startObj->m_nodeIndices[type][i] < 0)
+ break;
+ if(m_pathNodes[startObj->m_nodeIndices[type][i]].group == m_pathNodes[targetNode].group)
+ numPathsToTry++;
+ }
+ }else{
+ numPathsToTry = 1;
+ startObj = m_mapObjects[m_pathNodes[startNodeId].objectIndex];
+ }
+ if(numPathsToTry == 0)
+ goto fail;
+
+ if(startNodeId < 0){
+ // why only check node 0?
+ if(m_pathNodes[startObj->m_nodeIndices[type][0]].group != m_pathNodes[targetNode].group)
+ goto fail;
+ }else{
+ if(m_pathNodes[startNodeId].group != m_pathNodes[targetNode].group)
+ goto fail;
+ }
+
+
+ for(i = 0; i < 512; i++)
+ m_searchNodes[i].next = nil;
+ AddNodeToList(&m_pathNodes[targetNode], 0);
+ int numNodesToBeCleared = 0;
+ apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[targetNode];
+
+ // Dijkstra's algorithm
+ // Find distances
+ int numPathsFound = 0;
+ if(startNodeId < 0 && m_mapObjects[m_pathNodes[targetNode].objectIndex] == startObj)
+ numPathsFound++;
+ for(i = 0; numPathsFound < numPathsToTry; i = (i+1) & 0x1FF){
+ CPathNode *node;
+ for(node = m_searchNodes[i].next; node; node = node->next){
+ if(m_mapObjects[node->objectIndex] == startObj &&
+ (startNodeId < 0 || node == &m_pathNodes[startNodeId]))
+ numPathsFound++;
+
+ for(j = 0; j < node->numLinks; j++){
+ int next = m_connections[node->firstLink + j];
+ int dist = node->distance + m_distances[node->firstLink + j];
+ if(dist < m_pathNodes[next].distance){
+ if(m_pathNodes[next].distance != MAX_DIST)
+ RemoveNodeFromList(&m_pathNodes[next]);
+ if(m_pathNodes[next].distance == MAX_DIST)
+ apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[next];
+ AddNodeToList(&m_pathNodes[next], dist);
+ }
+ }
+
+ RemoveNodeFromList(node);
+ }
+ }
+
+ // Find out whence to start tracing back
+ CPathNode *curNode;
+ if(startNodeId < 0){
+ int minDist = MAX_DIST;
+ *pNumNodes = 1;
+ for(i = 0; i < 12; i++){
+ if(startObj->m_nodeIndices[type][i] < 0)
+ break;
+ int dist = (m_pathNodes[startObj->m_nodeIndices[type][i]].pos - start).Magnitude();
+ if(m_pathNodes[startObj->m_nodeIndices[type][i]].distance + dist < minDist){
+ minDist = m_pathNodes[startObj->m_nodeIndices[type][i]].distance + dist;
+ curNode = &m_pathNodes[startObj->m_nodeIndices[type][i]];
+ }
+ }
+ if(maxNumNodes == 0){
+ *pNumNodes = 0;
+ }else{
+ nodes[0] = curNode;
+ *pNumNodes = 1;
+ }
+ if(pDist)
+ *pDist = minDist;
+ }else{
+ curNode = &m_pathNodes[startNodeId];
+ *pNumNodes = 0;
+ if(pDist)
+ *pDist = m_pathNodes[startNodeId].distance;
+ }
+
+ // Trace back to target and update list of nodes
+ while(*pNumNodes < maxNumNodes && curNode != &m_pathNodes[targetNode])
+ for(i = 0; i < curNode->numLinks; i++){
+ int next = m_connections[curNode->firstLink + i];
+ if(curNode->distance - m_distances[curNode->firstLink + i] == m_pathNodes[next].distance){
+ curNode = &m_pathNodes[next];
+ nodes[(*pNumNodes)++] = curNode;
+ i = 29030; // could have used a break...
+ }
+ }
+
+ for(i = 0; i < numNodesToBeCleared; i++)
+ apNodesToBeCleared[i]->distance = MAX_DIST;
+ return;
+
+fail:
+ *pNumNodes = 0;
+ if(pDist)
+ *pDist = 100000.0f;
+}
+
+static CPathNode *pNodeList[32];
+static int16 DummyResult;
+static int16 DummyResult2;
+
+bool
+CPathFind::TestCoorsCloseness(CVector target, uint8 type, CVector start)
+{
+ float dist;
+ if(type == PATH_CAR)
+ DoPathSearch(type, start, -1, target, pNodeList, &DummyResult, 32, nil, &dist, 999999.88f, -1);
+ else
+ DoPathSearch(type, start, -1, target, nil, &DummyResult2, 0, nil, &dist, 50.0f, -1);
+ if(type == PATH_CAR)
+ return dist < 160.0f;
+ else
+ return dist < 100.0f;
+}
+
+void
+CPathFind::Save(uint8 *buffer, uint32 *length)
+{
+ int i;
+ int n = m_numPathNodes/8 + 1;
+
+ *length = 2*n;
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(m_pathNodes[i].bDisabled)
+ buffer[i/8] |= 1 << i%8;
+ else
+ buffer[i/8] &= ~(1 << i%8);
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(m_pathNodes[i].bBetweenLevels)
+ buffer[i/8 + n] |= 1 << i%8;
+ else
+ buffer[i/8 + n] &= ~(1 << i%8);
+}
+
+void
+CPathFind::Load(uint8 *buffer, uint32 length)
+{
+ int i;
+ int n = m_numPathNodes/8 + 1;
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(buffer[i/8] & (1 << i%8))
+ m_pathNodes[i].bDisabled = true;
+ else
+ m_pathNodes[i].bDisabled = false;
+
+ for(i = 0; i < m_numPathNodes; i++)
+ if(buffer[i/8 + n] & (1 << i%8))
+ m_pathNodes[i].bBetweenLevels = true;
+ else
+ m_pathNodes[i].bBetweenLevels = false;
+}
STARTPATCHES
+ InjectHook(0x4294A0, &CPathFind::Init, PATCH_JUMP);
+ InjectHook(0x42D580, &CPathFind::AllocatePathFindInfoMem, PATCH_JUMP);
+ InjectHook(0x429540, &CPathFind::RegisterMapObject, PATCH_JUMP);
+ InjectHook(0x42D7E0, &CPathFind::StoreNodeInfoPed, PATCH_JUMP);
+ InjectHook(0x42D690, &CPathFind::StoreNodeInfoCar, PATCH_JUMP);
InjectHook(0x429610, &CPathFind::PreparePathData, PATCH_JUMP);
- InjectHook(0x429C20, &CPathFind::PreparePathDataForType, PATCH_JUMP);
InjectHook(0x42B810, &CPathFind::CountFloodFillGroups, PATCH_JUMP);
+ InjectHook(0x429C20, &CPathFind::PreparePathDataForType, PATCH_JUMP);
+
+ InjectHook(0x42C990, &CPathFind::CalcRoadDensity, PATCH_JUMP);
+ InjectHook(0x42E1B0, &CPathFind::TestForPedTrafficLight, PATCH_JUMP);
+ InjectHook(0x42E340, &CPathFind::TestCrossesRoad, PATCH_JUMP);
+ InjectHook(0x42CBE0, &CPathFind::AddNodeToList, PATCH_JUMP);
+ InjectHook(0x42CBB0, &CPathFind::RemoveNodeFromList, PATCH_JUMP);
+ InjectHook(0x42B790, &CPathFind::RemoveBadStartNode, PATCH_JUMP);
+ InjectHook(0x42E3B0, &CPathFind::SetLinksBridgeLights, PATCH_JUMP);
+ InjectHook(0x42DED0, &CPathFind::SwitchOffNodeAndNeighbours, PATCH_JUMP);
+ InjectHook(0x42D960, &CPathFind::SwitchRoadsOffInArea, PATCH_JUMP);
+ InjectHook(0x42DA50, &CPathFind::SwitchPedRoadsOffInArea, PATCH_JUMP);
+ InjectHook(0x42DB50, &CPathFind::SwitchRoadsInAngledArea, PATCH_JUMP);
+ InjectHook(0x42E140, &CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours, PATCH_JUMP);
+ InjectHook(0x42DF50, &CPathFind::MarkRoadsBetweenLevelsInArea, PATCH_JUMP);
+ InjectHook(0x42E040, &CPathFind::MarkPedRoadsBetweenLevelsInArea, PATCH_JUMP);
+ InjectHook(0x42CC30, &CPathFind::FindNodeClosestToCoors, PATCH_JUMP);
+ InjectHook(0x42CDC0, &CPathFind::FindNodeClosestToCoorsFavourDirection, PATCH_JUMP);
+ InjectHook(0x42CFC0, &CPathFind::FindNodeOrientationForCarPlacement, PATCH_JUMP);
+ InjectHook(0x42D060, &CPathFind::FindNodeOrientationForCarPlacementFacingDestination, PATCH_JUMP);
+ InjectHook(0x42BF10, &CPathFind::NewGenerateCarCreationCoors, PATCH_JUMP);
+ InjectHook(0x42C1E0, &CPathFind::GeneratePedCreationCoors, PATCH_JUMP);
+ InjectHook(0x42D2A0, &CPathFind::FindRoadObjectClosestToCoors, PATCH_JUMP);
+ InjectHook(0x42B9F0, &CPathFind::FindNextNodeWandering, PATCH_JUMP);
+ InjectHook(0x42B040, &CPathFind::DoPathSearch, PATCH_JUMP);
+ InjectHook(0x42C8C0, &CPathFind::TestCoorsCloseness, PATCH_JUMP);
+ InjectHook(0x42E450, &CPathFind::Save, PATCH_JUMP);
+ InjectHook(0x42E550, &CPathFind::Load, PATCH_JUMP);
ENDPATCHES
diff --git a/src/control/PathFind.h b/src/control/PathFind.h
index 9d97de3f..d3f89154 100644
--- a/src/control/PathFind.h
+++ b/src/control/PathFind.h
@@ -2,24 +2,37 @@
#include "Treadable.h"
+class CVehicle;
+
+enum
+{
+ PATH_CAR = 0,
+ PATH_PED = 1,
+};
+
struct CPathNode
{
CVector pos;
- CPathNode *prev; //?
+ CPathNode *prev;
CPathNode *next;
- int16 unknown;
+ int16 distance; // in path search
int16 objectIndex;
int16 firstLink;
uint8 numLinks;
- uint8 flags;
+
+ uint8 unkBits : 2;
+ uint8 bDeadEnd : 1;
+ uint8 bDisabled : 1;
+ uint8 bBetweenLevels : 1;
+
uint8 group;
-/* VC:
- int16 unk1;
+/* For reference VC:
+ int16 prevIndex;
int16 nextIndex;
int16 x;
int16 y;
int16 z;
- int16 unknown;
+ int16 distance;
int16 firstLink;
int8 width;
int8 group;
@@ -40,6 +53,15 @@ struct CPathNode
*/
};
+union CConnectionFlags
+{
+ uint8 flags;
+ struct {
+ uint8 bCrossesRoad : 1;
+ uint8 bTrafficLight : 1;
+ };
+};
+
struct CCarPathLink
{
float posX;
@@ -50,10 +72,9 @@ struct CCarPathLink
int8 numLeftLanes;
int8 numRightLanes;
int8 trafficLightType;
- int8 field15;
- // probably only padding
- int8 field16;
- int8 field17;
+
+ uint8 bBridgeLights : 1;
+ // more?
};
struct CPathInfoForObject
@@ -80,8 +101,6 @@ struct CTempNode
int8 numLeftLanes;
int8 numRightLanes;
int8 linkState;
- // probably padding
- int8 field1B;
};
struct CTempDetachedNode // unused
@@ -102,39 +121,65 @@ public:
uint8 m_distances[20400];
int16 m_carPathConnections[20400];
*/
- CPathNode m_pathNodes[4930];
- CCarPathLink m_carPathLinks[2076];
- CTreadable *m_mapObjects[1250];
- uint8 m_objectFlags[1250];
- int16 m_connections[10260];
- int16 m_distances[10260];
- uint8 m_connectionFlags[10260];
- int16 m_carPathConnections[10260];
+ CPathNode m_pathNodes[NUM_PATHNODES];
+ CCarPathLink m_carPathLinks[NUM_CARPATHLINKS];
+ CTreadable *m_mapObjects[NUM_MAPOBJECTS];
+ uint8 m_objectFlags[NUM_MAPOBJECTS];
+ int16 m_connections[NUM_PATHCONNECTIONS];
+ int16 m_distances[NUM_PATHCONNECTIONS];
+ CConnectionFlags m_connectionFlags[NUM_PATHCONNECTIONS];
+ int16 m_carPathConnections[NUM_PATHCONNECTIONS];
int32 m_numPathNodes;
int32 m_numCarPathNodes;
int32 m_numPedPathNodes;
int16 m_numMapObjects;
int16 m_numConnections;
int32 m_numCarPathLinks;
- int32 h;
+ int32 unk;
uint8 m_numGroups[2];
- CPathNode m_aExtraPaths[872];
+ CPathNode m_searchNodes[512];
+ void Init(void);
+ void AllocatePathFindInfoMem(int16 numPathGroups);
+ void RegisterMapObject(CTreadable *mapObject);
+ void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing);
+ void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight);
+ void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out);
+ bool LoadPathFindData(void);
void PreparePathData(void);
void CountFloodFillGroups(uint8 type);
void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float unk, CTempDetachedNode *detachednodes, int unused);
- void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out);
- void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing);
- void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight);
- void RegisterMapObject(CTreadable *mapObject);
- int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool disabled, bool betweenLevels);
- CPathNode** FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*);
bool IsPathObject(int id) { return id < PATHNODESIZE && (InfoForTileCars[id*12].type != 0 || InfoForTilePeds[id*12].type != 0); }
+ float CalcRoadDensity(float x, float y);
+ bool TestForPedTrafficLight(CPathNode *n1, CPathNode *n2);
+ bool TestCrossesRoad(CPathNode *n1, CPathNode *n2);
+ void AddNodeToList(CPathNode *node, int32 listId);
+ void RemoveNodeFromList(CPathNode *node);
+ void RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n);
void SetLinksBridgeLights(float, float, float, float, bool);
+ void SwitchOffNodeAndNeighbours(int32 nodeId, bool disable);
+ void SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable);
+ void SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable);
+ void SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable);
+ void MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId);
+ void MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2);
+ void MarkPedRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2);
+ int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled = false, bool ignoreBetweenLevels = false);
+ int32 FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY);
+ float FindNodeOrientationForCarPlacement(int32 nodeId);
+ float FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards);
+ bool NewGenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled = false);
+ bool GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix);
+ CTreadable *FindRoadObjectClosestToCoors(CVector coors, uint8 type);
+ void FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*);
+ void DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *numNodes, int16 maxNumNodes, CVehicle *vehicle, float *dist, float distLimit, int32 forcedTargetNode);
+ bool TestCoorsCloseness(CVector target, uint8 type, CVector start);
+ void Save(uint8 *buffer, uint32 *length);
+ void Load(uint8 *buffer, uint32 length);
};
-static_assert(sizeof(CPathFind) == 0x4c8f4, "CPathFind: error");
+static_assert(sizeof(CPathFind) == 0x49bf4, "CPathFind: error");
extern CPathFind &ThePaths;
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index 2770a948..81642a85 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -5,7 +5,8 @@
CPickup(&CPickups::aPickUps)[NUMPICKUPS] = *(CPickup(*)[NUMPICKUPS])*(uintptr*)0x878C98;
// 20 ?! Some Miami leftover? (Originally at 0x5ED8D4)
-uint16 CPickups::ms_maxAmmosForWeapons[20] = { 0, 1, 45, 125, 25, 150, 300, 25, 5, 250, 5, 5, 0, 500, 0, 100, 0, 0, 0, 0 };
+uint16 AmmoForWeapon[20] = { 0, 1, 45, 125, 25, 150, 300, 25, 5, 250, 5, 5, 0, 500, 0, 100, 0, 0, 0, 0 };
+uint16 AmmoForWeapon_OnStreet[20] = { 0, 1, 9, 25, 5, 30, 60, 5, 1, 50, 1, 1, 0, 200, 0, 100, 0, 0, 0, 0 };
WRAPPER void CPickups::RenderPickUpText(void) { EAXJMP(0x432440); }
WRAPPER void CPickups::DoCollectableEffects(CEntity *ent) { EAXJMP(0x431C30); }
diff --git a/src/control/Pickups.h b/src/control/Pickups.h
index 20a779a8..8c2014d6 100644
--- a/src/control/Pickups.h
+++ b/src/control/Pickups.h
@@ -47,9 +47,11 @@ public:
static int32 GenerateNewOne_WeaponType(CVector, eWeaponType, uint8, uint32);
static CPickup (&aPickUps)[NUMPICKUPS];
- static uint16 ms_maxAmmosForWeapons[20];
};
+extern uint16 AmmoForWeapon[20];
+extern uint16 AmmoForWeapon_OnStreet[20];
+
class CPacManPickups
{
public:
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.cpp b/src/control/Restart.cpp
new file mode 100644
index 00000000..c5c46b51
--- /dev/null
+++ b/src/control/Restart.cpp
@@ -0,0 +1,7 @@
+#include "common.h"
+#include "patcher.h"
+#include "Restart.h"
+
+WRAPPER void CRestart::AddHospitalRestartPoint(const CVector&, float) { EAXJMP(0x436100); }
+WRAPPER void CRestart::AddPoliceRestartPoint(const CVector&, float) { EAXJMP(0x436150); }
+WRAPPER void CRestart::OverrideNextRestart(const CVector&, float) { EAXJMP(0x4366C0); } \ No newline at end of file
diff --git a/src/control/Restart.h b/src/control/Restart.h
new file mode 100644
index 00000000..f49ed79c
--- /dev/null
+++ b/src/control/Restart.h
@@ -0,0 +1,9 @@
+#pragma once
+
+class CRestart
+{
+public:
+ static void AddPoliceRestartPoint(const CVector&, float);
+ static void AddHospitalRestartPoint(const CVector&, float);
+ static void OverrideNextRestart(const CVector&, float);
+};
diff --git a/src/control/RoadBlocks.cpp b/src/control/RoadBlocks.cpp
new file mode 100644
index 00000000..3683ff28
--- /dev/null
+++ b/src/control/RoadBlocks.cpp
@@ -0,0 +1,5 @@
+#include "common.h"
+#include "patcher.h"
+#include "RoadBlocks.h"
+
+WRAPPER void CRoadBlocks::GenerateRoadBlockCopsForCar(CVehicle*, int32, int16) { EAXJMP(0x4376A0); }
diff --git a/src/control/RoadBlocks.h b/src/control/RoadBlocks.h
new file mode 100644
index 00000000..0d965e48
--- /dev/null
+++ b/src/control/RoadBlocks.h
@@ -0,0 +1,10 @@
+#pragma once
+#include "common.h"
+
+class CVehicle;
+
+class CRoadBlocks
+{
+public:
+ static void GenerateRoadBlockCopsForCar(CVehicle*, int32, int16);
+};
diff --git a/src/control/Script.cpp b/src/control/Script.cpp
index b61a466b..c3c3a154 100644
--- a/src/control/Script.cpp
+++ b/src/control/Script.cpp
@@ -27,7 +27,9 @@
#include "Pools.h"
#include "Population.h"
#include "Remote.h"
+#include "Restart.h"
#include "Replay.h"
+#include "Shadows.h"
#include "Streaming.h"
#include "Text.h"
#include "User.h"
@@ -1384,20 +1386,20 @@ void CRunningScript::UpdateCompareFlag(bool flag)
{
if (m_bNotFlag)
flag = !flag;
- if (m_nAndOrState == 0){
+ if (m_nAndOrState == ANDOR_NONE){
m_bCondResult = flag;
return;
}
- if (m_nAndOrState >= 1 && m_nAndOrState <= 8) { /* Maybe enums?*/
+ if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8){
m_bCondResult &= flag;
- if (m_nAndOrState == 1){
- m_nAndOrState = 0;
+ if (m_nAndOrState == ANDS_1){
+ m_nAndOrState = ANDOR_NONE;
return;
}
- }else if (m_nAndOrState >= 21 && m_nAndOrState <= 28){
+ }else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8){
m_bCondResult |= flag;
- if (m_nAndOrState == 21) {
- m_nAndOrState = 0;
+ if (m_nAndOrState == ORS_1) {
+ m_nAndOrState = ANDOR_NONE;
return;
}
}else{
@@ -1922,7 +1924,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command)
boat->m_status = STATUS_ABANDONED;
boat->bIsLocked = true;
boat->AutoPilot.m_nCarMission = MISSION_NONE;
- boat->AutoPilot.m_nAnimationId = TEMPACT_NONE; /* Animation ID? */
+ boat->AutoPilot.m_nTempAction = TEMPACT_NONE; /* Animation ID? */
boat->AutoPilot.m_nCruiseSpeed = boat->AutoPilot.m_fMaxTrafficSpeed = 20.0f;
CWorld::Add(boat);
handle = CPools::GetVehiclePool()->GetIndex(boat);
@@ -1941,10 +1943,10 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command)
car->bIsLocked = true;
CCarCtrl::JoinCarWithRoadSystem(car);
car->AutoPilot.m_nCarMission = MISSION_NONE;
- car->AutoPilot.m_nAnimationId = TEMPACT_NONE; /* Animation ID? */
+ car->AutoPilot.m_nTempAction = TEMPACT_NONE; /* Animation ID? */
car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f;
- car->AutoPilot.m_nPreviousLane = car->AutoPilot.m_nCurrentLane = 0;
+ car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0;
car->bEngineOn = false;
car->m_level = CTheZones::GetLevelFromPosition(pos);
car->bHasBeenOwnedByPlayer = true;
@@ -2803,7 +2805,7 @@ int8 CRunningScript::ProcessCommandsFrom200To299(int32 command)
return -1;
}
-#if 1
+#if 0
WRAPPER int8 CRunningScript::ProcessCommandsFrom300To399(int32 command) { EAXJMP(0x43ED30); }
#else
int8 CRunningScript::ProcessCommandsFrom300To399(int32 command)
@@ -2846,6 +2848,7 @@ int8 CRunningScript::ProcessCommandsFrom300To399(int32 command)
CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
assert(pVehicle);
UpdateCompareFlag(pVehicle->GetModelIndex() == ScriptParams[1]);
+ return 0;
}
/* Not implemented.
case COMMAND_IS_CAR_REMAP:
@@ -3010,67 +3013,526 @@ int8 CRunningScript::ProcessCommandsFrom300To399(int32 command)
{
char label[12];
CTheScripts::ReadTextLabelFromScript(&m_nIp, label);
+ int16 zone = CTheZones::FindZoneByLabelAndReturnIndex(label);
m_nIp += 8;
-
+ CollectParameters(&m_nIp, 2);
+ if (zone < 0) {
+ debug("Couldn't find zone - %s\n", label);
+ return 0;
+ }
+ CTheZones::SetCarDensity(zone, ScriptParams[0], ScriptParams[1]);
+ return 0;
}
case COMMAND_SET_PED_DENSITY:
+ {
+ char label[12];
+ CTheScripts::ReadTextLabelFromScript(&m_nIp, label);
+ int16 zone = CTheZones::FindZoneByLabelAndReturnIndex(label);
+ m_nIp += 8;
+ CollectParameters(&m_nIp, 2);
+ if (zone < 0) {
+ debug("Couldn't find zone - %s\n", label);
+ return 0;
+ }
+ CTheZones::SetPedDensity(zone, ScriptParams[0], ScriptParams[1]);
+ return 0;
+ }
case COMMAND_POINT_CAMERA_AT_PLAYER:
+ {
+ CollectParameters(&m_nIp, 3);
+ // ScriptParams[0] is unused.
+ TheCamera.TakeControl(nil, ScriptParams[1], ScriptParams[2], 1);
+ return 0;
+ }
case COMMAND_POINT_CAMERA_AT_CAR:
+ {
+ CollectParameters(&m_nIp, 3);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ TheCamera.TakeControl(pVehicle, ScriptParams[1], ScriptParams[2], 1);
+ return 0;
+ }
case COMMAND_POINT_CAMERA_AT_CHAR:
+ {
+ CollectParameters(&m_nIp, 3);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ TheCamera.TakeControl(pPed, ScriptParams[1], ScriptParams[2], 1);
+ return 0;
+ }
case COMMAND_RESTORE_CAMERA:
+ TheCamera.Restore();
+ return 0;
case COMMAND_SHAKE_PAD:
+ CPad::GetPad(ScriptParams[0])->StartShake(ScriptParams[1], ScriptParams[2]);
+ return 0;
case COMMAND_SET_ZONE_PED_INFO:
+ {
+ char label[12];
+ CTheScripts::ReadTextLabelFromScript(&m_nIp, label);
+ m_nIp += 8;
+ CollectParameters(&m_nIp, 10);
+ int16 zone = CTheZones::FindZoneByLabelAndReturnIndex(label);
+ if (zone < 0) {
+ debug("Couldn't find zone - %s\n", label);
+ return 0;
+ }
+ CTheZones::SetZonePedInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3],
+ ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8], 0, 0, ScriptParams[9]);
+ return 0;
+ }
case COMMAND_SET_TIME_SCALE:
+ CollectParameters(&m_nIp, 1);
+ CTimer::SetTimeScale(*(float*)&ScriptParams[0]);
+ return 0;
case COMMAND_IS_CAR_IN_AIR:
+ {
+ CollectParameters(&m_nIp, 1);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle && pVehicle->IsCar());
+ CAutomobile* pCar = (CAutomobile*)pVehicle;
+ UpdateCompareFlag(pCar->GetAllWheelsOffGround());
+ return 0;
+ }
case COMMAND_SET_FIXED_CAMERA_POSITION:
+ {
+ CollectParameters(&m_nIp, 6);
+ TheCamera.SetCamPositionForFixedMode(
+ CVector(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]),
+ CVector(*(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5]));
+ return 0;
+ }
case COMMAND_POINT_CAMERA_AT_POINT:
+ {
+ CollectParameters(&m_nIp, 4);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ TheCamera.TakeControlNoEntity(pos, ScriptParams[3], 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_CAR_OLD:
+ {
+ CollectParameters(&m_nIp, 3);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]);
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_CHAR_OLD:
+ {
+ CollectParameters(&m_nIp, 3);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]);
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_OBJECT_OLD:
+ {
+ CollectParameters(&m_nIp, 3);
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]);
+ assert(pObject);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]);
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_REMOVE_BLIP:
+ CollectParameters(&m_nIp, 1);
+ CRadar::ClearBlip(ScriptParams[0]);
+ return 0;
case COMMAND_CHANGE_BLIP_COLOUR:
+ CollectParameters(&m_nIp, 2);
+ CRadar::ChangeBlipColour(ScriptParams[0], ScriptParams[1]);
+ return 0;
case COMMAND_DIM_BLIP:
+ CollectParameters(&m_nIp, 2);
+ CRadar::ChangeBlipBrightness(ScriptParams[0], ScriptParams[1]);
+ return 0;
case COMMAND_ADD_BLIP_FOR_COORD_OLD:
+ {
+ CollectParameters(&m_nIp, 5);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ // Useless call
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ ScriptParams[0] = CRadar::SetCoordBlip(BLIP_COORD, pos, ScriptParams[3], (eBlipDisplay)ScriptParams[4]);
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_CHANGE_BLIP_SCALE:
+ CollectParameters(&m_nIp, 2);
+ CRadar::ChangeBlipScale(ScriptParams[0], ScriptParams[1]);
+ return 0;
case COMMAND_SET_FADING_COLOUR:
+ CollectParameters(&m_nIp, 3);
+ TheCamera.SetFadeColour(ScriptParams[0], ScriptParams[1], ScriptParams[2]);
+ return 0;
case COMMAND_DO_FADE:
+ CollectParameters(&m_nIp, 2);
+ TheCamera.Fade(ScriptParams[0] / 1000.0f, ScriptParams[1]);
+ return 0;
case COMMAND_GET_FADING_STATUS:
+ UpdateCompareFlag(TheCamera.GetFading());
+ return 0;
case COMMAND_ADD_HOSPITAL_RESTART:
+ {
+ CollectParameters(&m_nIp, 4);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ float angle = *(float*)&ScriptParams[3];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ CRestart::AddHospitalRestartPoint(pos, angle);
+ return 0;
+ }
case COMMAND_ADD_POLICE_RESTART:
+ {
+ CollectParameters(&m_nIp, 4);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ float angle = *(float*)&ScriptParams[3];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ CRestart::AddPoliceRestartPoint(pos, angle);
+ return 0;
+ }
case COMMAND_OVERRIDE_NEXT_RESTART:
+ {
+ CollectParameters(&m_nIp, 4);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ float angle = *(float*)&ScriptParams[3];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ CRestart::OverrideNextRestart(pos, angle);
+ return 0;
+ }
case COMMAND_DRAW_SHADOW:
+ {
+ CollectParameters(&m_nIp, 10);
+ CVector pos = *(CVector*)&ScriptParams[1];
+ float angle = *(float*)&ScriptParams[4];
+ float length = *(float*)&ScriptParams[5];
+ float x, y;
+ if (angle != 0.0f){
+ y = cos(angle) * length;
+ x = sin(angle) * length;
+ }else{
+ y = length;
+ x = 0.0f;
+ }
+ float frontX = -x;
+ float frontY = y;
+ float sideX = y;
+ float sideY = x;
+ /* Not very nicely named intermediate variables. */
+ CShadows::StoreShadowToBeRendered(ScriptParams[0], &pos, frontX, frontY, sideX, sideY,
+ ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9]);
+ return 0;
+ }
case COMMAND_GET_PLAYER_HEADING:
+ {
+ CollectParameters(&m_nIp, 1);
+ CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed;
+ float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading();
+ *(float*)&ScriptParams[0] = CGeneral::LimitAngle(RADTODEG(angle));
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_SET_PLAYER_HEADING:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed;
+ if (pPed->bInVehicle){
+ // Is assertion required?
+ return 0;
+ }
+ pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]);
+ pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1]));
+ return 0;
+ }
case COMMAND_GET_CHAR_HEADING:
+ {
+ CollectParameters(&m_nIp, 1);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading();
+ *(float*)&ScriptParams[0] = CGeneral::LimitAngle(RADTODEG(angle));
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_SET_CHAR_HEADING:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ if (pPed->bInVehicle) {
+ // Is assertion required?
+ return 0;
+ }
+ pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]);
+ pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1]));
+ return 0;
+ }
case COMMAND_GET_CAR_HEADING:
+ {
+ CollectParameters(&m_nIp, 1);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ float angle = pVehicle->GetForward().Heading();
+ *(float*)&ScriptParams[0] = CGeneral::LimitAngle(RADTODEG(angle));
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_SET_CAR_HEADING:
+ {
+ CollectParameters(&m_nIp, 2);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ pVehicle->SetHeading(DEGTORAD(*(float*)&ScriptParams[1]));
+ return 0;
+ }
case COMMAND_GET_OBJECT_HEADING:
+ {
+ CollectParameters(&m_nIp, 1);
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]);
+ assert(pObject);
+ float angle = pObject->GetForward().Heading();
+ *(float*)&ScriptParams[0] = CGeneral::LimitAngle(RADTODEG(angle));
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_SET_OBJECT_HEADING:
+ {
+ CollectParameters(&m_nIp, 2);
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]);
+ assert(pObject);
+ CWorld::Remove(pObject);
+ pObject->SetHeading(DEGTORAD(*(float*)&ScriptParams[1]));
+ pObject->GetMatrix().UpdateRW();
+ pObject->UpdateRwFrame();
+ CWorld::Add(pObject);
+ return 0;
+ }
case COMMAND_IS_PLAYER_TOUCHING_OBJECT:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed;
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]);
+ assert(pObject);
+ CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed;
+ UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject));
+ return 0;
+ }
case COMMAND_IS_CHAR_TOUCHING_OBJECT:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]);
+ assert(pObject);
+ CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed;
+ UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject));
+ return 0;
+ }
case COMMAND_SET_PLAYER_AMMO:
+ {
+ CollectParameters(&m_nIp, 3);
+ CWorld::Players[0].m_pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]);
+ return 0;
+ }
case COMMAND_SET_CHAR_AMMO:
+ {
+ CollectParameters(&m_nIp, 3);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]);
+ return 0;
+ }
+ /* Not implemented.
case COMMAND_SET_CAR_AMMO:
case COMMAND_LOAD_CAMERA_SPLINE:
case COMMAND_MOVE_CAMERA_ALONG_SPLINE:
case COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE:
+ */
case COMMAND_DECLARE_MISSION_FLAG:
+ CTheScripts::OnAMissionFlag = CTheScripts::Read2BytesFromScript(&++m_nIp);
+ return 0;
case COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT:
+ CollectParameters(&m_nIp, 1);
+ CTheScripts::OnAMissionForContactFlag[ScriptParams[0]] = CTheScripts::Read2BytesFromScript(&++m_nIp);
+ return 0;
case COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT:
+ CollectParameters(&m_nIp, 2);
+ CTheScripts::BaseBriefIdForContact[ScriptParams[0]] = ScriptParams[1];
+ return 0;
case COMMAND_IS_PLAYER_HEALTH_GREATER:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed;
+ UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]);
+ return 0;
+ }
case COMMAND_IS_CHAR_HEALTH_GREATER:
+ {
+ CollectParameters(&m_nIp, 2);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]);
+ return 0;
+ }
case COMMAND_IS_CAR_HEALTH_GREATER:
+ {
+ CollectParameters(&m_nIp, 2);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ UpdateCompareFlag(pVehicle->m_fHealth > ScriptParams[1]);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_CAR:
+ {
+ CollectParameters(&m_nIp, 1);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ int handle = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH);
+ CRadar::ChangeBlipScale(handle, 3);
+ ScriptParams[0] = handle;
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_CHAR:
+ {
+ CollectParameters(&m_nIp, 1);
+ CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]);
+ assert(pPed);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ int handle = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH);
+ CRadar::ChangeBlipScale(handle, 3);
+ ScriptParams[0] = handle;
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_OBJECT:
+ {
+ CollectParameters(&m_nIp, 1);
+ CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]);
+ assert(pObject);
+ // Useless call.
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ int handle = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], 0, BLIP_DISPLAY_BOTH);
+ CRadar::ChangeBlipScale(handle, 3);
+ ScriptParams[0] = handle;
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_CONTACT_POINT:
+ {
+ CollectParameters(&m_nIp, 3);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ // Useless call
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ int handle = CRadar::SetCoordBlip(BLIP_COORD, pos, 2, BLIP_DISPLAY_BOTH);
+ CRadar::ChangeBlipScale(handle, 3);
+ ScriptParams[0] = handle;
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_ADD_BLIP_FOR_COORD:
+ {
+ CollectParameters(&m_nIp, 3);
+ CVector pos = *(CVector*)&ScriptParams[0];
+ if (pos.z <= -100.0f)
+ pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
+ // Useless call
+ CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp));
+ int handle = CRadar::SetCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH);
+ CRadar::ChangeBlipScale(handle, 3);
+ ScriptParams[0] = handle;
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_CHANGE_BLIP_DISPLAY:
+ CollectParameters(&m_nIp, 2);
+ CRadar::ChangeBlipDisplay(ScriptParams[0], (eBlipDisplay)ScriptParams[1]);
+ return 0;
case COMMAND_ADD_ONE_OFF_SOUND:
+ {
+ CollectParameters(&m_nIp, 4);
+ switch (ScriptParams[3]) {
+ case SCRIPT_SOUND_EVIDENCE_PICKUP:
+ DMAudio.PlayFrontEndSound(SOUND_EVIDENCE_PICKUP, 0);
+ return 0;
+ case SCRIPT_SOUND_UNLOAD_GOLD:
+ DMAudio.PlayFrontEndSound(SOUND_UNLOAD_GOLD, 0);
+ return 0;
+ case SCRIPT_SOUND_PART_MISSION_COMPLETE:
+ DMAudio.PlayFrontEndSound(SOUND_PART_MISSION_COMPLETE, 0);
+ return 0;
+ case SCRIPT_SOUND_RACE_START_3:
+ DMAudio.PlayFrontEndSound(SOUND_RACE_START_3, 0);
+ return 0;
+ case SCRIPT_SOUND_RACE_START_2:
+ DMAudio.PlayFrontEndSound(SOUND_RACE_START_2, 0);
+ return 0;
+ case SCRIPT_SOUND_RACE_START_1:
+ DMAudio.PlayFrontEndSound(SOUND_RACE_START_1, 0);
+ return 0;
+ case SCRIPT_SOUND_RACE_START_GO:
+ DMAudio.PlayFrontEndSound(SOUND_RACE_START_GO, 0);
+ return 0;
+ default:
+ break;
+ }
+ cAudioScriptObject* obj = new cAudioScriptObject();
+ obj->m_vecPos = *(CVector*)&ScriptParams[0];
+ obj->m_wSound = ScriptParams[3];
+ obj->m_nAudioEntityId = -5;
+ /* BUG: if audio is not initialized, this object will not be freed. */
+ /* Issue needs to be addressed in CreateOneShotScriptObject. */
+ DMAudio.CreateOneShotScriptObject(obj);
+ return 0;
+ }
case COMMAND_ADD_CONTINUOUS_SOUND:
+ {
+ CollectParameters(&m_nIp, 4);
+ cAudioScriptObject* obj = new cAudioScriptObject();
+ obj->m_vecPos = *(CVector*)&ScriptParams[0];
+ obj->m_wSound = ScriptParams[3];
+ obj->m_nAudioEntityId = DMAudio.CreateLoopingScriptObject(obj);
+ ScriptParams[0] = CPools::GetAudioScriptObjectPool()->GetIndex(obj);
+ StoreParameters(&m_nIp, 1);
+ return 0;
+ }
case COMMAND_REMOVE_SOUND:
+ {
+ CollectParameters(&m_nIp, 1);
+ cAudioScriptObject* obj = CPools::GetAudioScriptObjectPool()->GetAt(ScriptParams[0]);
+ if (!obj){
+ debug("REMOVE_SOUND - Sound doesn't exist\n");
+ return 0;
+ }
+ DMAudio.DestroyLoopingScriptObject(obj->m_nAudioEntityId);
+ delete obj;
+ return 0;
+ }
case COMMAND_IS_CAR_STUCK_ON_ROOF:
+ {
+ CollectParameters(&m_nIp, 1);
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+ assert(pVehicle);
+ UpdateCompareFlag(CTheScripts::UpsideDownCars.HasCarBeenUpsideDownForAWhile(ScriptParams[0]));
+ return 0;
+ }
default:
assert(0);
}
diff --git a/src/control/TrafficLights.cpp b/src/control/TrafficLights.cpp
index 73ff6118..61c941b8 100644
--- a/src/control/TrafficLights.cpp
+++ b/src/control/TrafficLights.cpp
@@ -1,5 +1,22 @@
#include "common.h"
#include "patcher.h"
#include "TrafficLights.h"
+#include "Timer.h"
+#include "Vehicle.h"
WRAPPER void CTrafficLights::DisplayActualLight(CEntity *ent) { EAXJMP(0x455800); }
+WRAPPER bool CTrafficLights::ShouldCarStopForLight(CVehicle*, bool) { EAXJMP(0x455350); }
+WRAPPER bool CTrafficLights::ShouldCarStopForBridge(CVehicle*) { EAXJMP(0x456460); }
+
+uint8
+CTrafficLights::LightForPeds(void)
+{
+ uint32 period = CTimer::GetTimeInMilliseconds() & 0x3FFF; // Equals to % 16384
+
+ if (period >= 15384)
+ return PED_LIGHTS_WALK_BLINK;
+ else if (period >= 12000)
+ return PED_LIGHTS_WALK;
+ else
+ return PED_LIGHTS_DONT_WALK;
+} \ No newline at end of file
diff --git a/src/control/TrafficLights.h b/src/control/TrafficLights.h
index eec3e1e3..f0d0248d 100644
--- a/src/control/TrafficLights.h
+++ b/src/control/TrafficLights.h
@@ -1,9 +1,19 @@
#pragma once
class CEntity;
+class CVehicle;
+
+enum {
+ PED_LIGHTS_WALK,
+ PED_LIGHTS_WALK_BLINK,
+ PED_LIGHTS_DONT_WALK,
+};
class CTrafficLights
{
public:
static void DisplayActualLight(CEntity *ent);
+ static uint8 LightForPeds(void);
+ static bool ShouldCarStopForLight(CVehicle*, bool);
+ static bool ShouldCarStopForBridge(CVehicle*);
};