diff options
author | Fire_Head <Fire-Head@users.noreply.github.com> | 2019-08-15 04:06:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-15 04:06:52 +0200 |
commit | 6909fa283a25410a6d5f2fc93259b71770512ca2 (patch) | |
tree | cc187ba965eb71352fae028d3eb7f9f8db463153 /src/control | |
parent | CParticleObject done, cDMAudio done (diff) | |
parent | Merge pull request #189 from Nick007J/master (diff) | |
download | re3-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.cpp | 5 | ||||
-rw-r--r-- | src/control/AutoPilot.h | 33 | ||||
-rw-r--r-- | src/control/CarAI.cpp | 16 | ||||
-rw-r--r-- | src/control/CarAI.h | 6 | ||||
-rw-r--r-- | src/control/CarCtrl.cpp | 864 | ||||
-rw-r--r-- | src/control/CarCtrl.h | 68 | ||||
-rw-r--r-- | src/control/Cranes.cpp | 5 | ||||
-rw-r--r-- | src/control/Cranes.h | 10 | ||||
-rw-r--r-- | src/control/Curves.cpp | 6 | ||||
-rw-r--r-- | src/control/Curves.h | 9 | ||||
-rw-r--r-- | src/control/Gangs.cpp | 2 | ||||
-rw-r--r-- | src/control/Gangs.h | 3 | ||||
-rw-r--r-- | src/control/Garages.cpp | 1 | ||||
-rw-r--r-- | src/control/Garages.h | 1 | ||||
-rw-r--r-- | src/control/PathFind.cpp | 1012 | ||||
-rw-r--r-- | src/control/PathFind.h | 103 | ||||
-rw-r--r-- | src/control/Pickups.cpp | 3 | ||||
-rw-r--r-- | src/control/Pickups.h | 4 | ||||
-rw-r--r-- | src/control/Population.cpp | 1 | ||||
-rw-r--r-- | src/control/Population.h | 1 | ||||
-rw-r--r-- | src/control/Restart.cpp | 7 | ||||
-rw-r--r-- | src/control/Restart.h | 9 | ||||
-rw-r--r-- | src/control/RoadBlocks.cpp | 5 | ||||
-rw-r--r-- | src/control/RoadBlocks.h | 10 | ||||
-rw-r--r-- | src/control/Script.cpp | 486 | ||||
-rw-r--r-- | src/control/TrafficLights.cpp | 17 | ||||
-rw-r--r-- | src/control/TrafficLights.h | 10 |
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*); }; |