summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFire_Head <Fire-Head@users.noreply.github.com>2019-08-15 04:06:52 +0200
committerGitHub <noreply@github.com>2019-08-15 04:06:52 +0200
commit6909fa283a25410a6d5f2fc93259b71770512ca2 (patch)
treecc187ba965eb71352fae028d3eb7f9f8db463153
parentCParticleObject done, cDMAudio done (diff)
parentMerge pull request #189 from Nick007J/master (diff)
downloadre3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.gz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.bz2
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.lz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.xz
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.tar.zst
re3-6909fa283a25410a6d5f2fc93259b71770512ca2.zip
-rw-r--r--src/audio/DMAudio.cpp3
-rw-r--r--src/audio/DMAudio.h2
-rw-r--r--src/control/AutoPilot.cpp5
-rw-r--r--src/control/AutoPilot.h33
-rw-r--r--src/control/CarAI.cpp16
-rw-r--r--src/control/CarAI.h6
-rw-r--r--src/control/CarCtrl.cpp864
-rw-r--r--src/control/CarCtrl.h68
-rw-r--r--src/control/Cranes.cpp5
-rw-r--r--src/control/Cranes.h10
-rw-r--r--src/control/Curves.cpp6
-rw-r--r--src/control/Curves.h9
-rw-r--r--src/control/Gangs.cpp2
-rw-r--r--src/control/Gangs.h3
-rw-r--r--src/control/Garages.cpp1
-rw-r--r--src/control/Garages.h1
-rw-r--r--src/control/PathFind.cpp1012
-rw-r--r--src/control/PathFind.h103
-rw-r--r--src/control/Pickups.cpp3
-rw-r--r--src/control/Pickups.h4
-rw-r--r--src/control/Population.cpp1
-rw-r--r--src/control/Population.h1
-rw-r--r--src/control/Restart.cpp7
-rw-r--r--src/control/Restart.h9
-rw-r--r--src/control/RoadBlocks.cpp5
-rw-r--r--src/control/RoadBlocks.h10
-rw-r--r--src/control/Script.cpp486
-rw-r--r--src/control/TrafficLights.cpp17
-rw-r--r--src/control/TrafficLights.h10
-rw-r--r--src/core/Camera.cpp10
-rw-r--r--src/core/Camera.h5
-rw-r--r--src/core/Collision.cpp2
-rw-r--r--src/core/General.h18
-rw-r--r--src/core/IniFile.cpp28
-rw-r--r--src/core/IniFile.h10
-rw-r--r--src/core/Radar.cpp16
-rw-r--r--src/core/Radar.h8
-rw-r--r--src/core/Stats.cpp4
-rw-r--r--src/core/Stats.h2
-rw-r--r--src/core/Wanted.h2
-rw-r--r--src/core/World.cpp2
-rw-r--r--src/core/World.h4
-rw-r--r--src/core/common.h2
-rw-r--r--src/core/config.h6
-rw-r--r--src/core/re3.cpp54
-rw-r--r--src/entities/Physical.cpp27
-rw-r--r--src/entities/Physical.h6
-rw-r--r--src/entities/Treadable.h3
-rw-r--r--src/math/Vector2D.h51
-rw-r--r--src/modelinfo/VehicleModelInfo.cpp15
-rw-r--r--src/peds/Ped.cpp2262
-rw-r--r--src/peds/Ped.h92
-rw-r--r--src/peds/PedRoutes.cpp6
-rw-r--r--src/peds/PedRoutes.h7
-rw-r--r--src/render/Renderer.cpp4
-rw-r--r--src/vehicles/Automobile.cpp4
-rw-r--r--src/vehicles/Automobile.h18
-rw-r--r--src/vehicles/Heli.cpp1042
-rw-r--r--src/vehicles/Heli.h87
-rw-r--r--src/vehicles/Plane.cpp11
-rw-r--r--src/vehicles/Train.cpp2
-rw-r--r--src/vehicles/Train.h5
-rw-r--r--src/vehicles/Vehicle.cpp10
-rw-r--r--src/vehicles/Vehicle.h11
64 files changed, 6131 insertions, 407 deletions
diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp
index ce160677..6fb6d1e5 100644
--- a/src/audio/DMAudio.cpp
+++ b/src/audio/DMAudio.cpp
@@ -8,7 +8,6 @@
cDMAudio &DMAudio = *(cDMAudio*)0x95CDBE;
-
void
cDMAudio::Initialise(void)
{
@@ -372,4 +371,4 @@ STARTPATCHES
//InjectHook(0x57CEB0, `global constructor keyed to'dmaudio.cpp, PATCH_JUMP);
//InjectHook(0x57CED0, cDMAudio::~cDMAudio, PATCH_JUMP);
-ENDPATCHES \ No newline at end of file
+ENDPATCHES
diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h
index 5220bbd3..318b9947 100644
--- a/src/audio/DMAudio.h
+++ b/src/audio/DMAudio.h
@@ -246,7 +246,7 @@ public:
void PlayLoadedMissionAudio(void);
bool IsMissionAudioSampleFinished(void);
void ClearMissionAudio(void);
-
+
int32 GetRadioInCar(void);
void SetRadioInCar(uint32 radio);
void SetRadioChannel(int8 radio, int32 pos);
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*);
};
diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp
index b5ba76db..f3582c67 100644
--- a/src/core/Camera.cpp
+++ b/src/core/Camera.cpp
@@ -24,6 +24,16 @@ WRAPPER void CCamera::CalculateDerivedValues(void) { EAXJMP(0x46EEA0); }
WRAPPER void CCamera::Restore(void) { EAXJMP(0x46F990); }
WRAPPER void CCamera::SetWidescreenOff(void) { EAXJMP(0x46FF10); }
WRAPPER void CamShakeNoPos(CCamera*, float) { EAXJMP(0x46B100); }
+WRAPPER void CCamera::TakeControl(CEntity*, int16, int16, int32) { EAXJMP(0x471500); }
+WRAPPER void CCamera::TakeControlNoEntity(const CVector&, int16, int32) { EAXJMP(0x4715B0); }
+WRAPPER void CCamera::SetCamPositionForFixedMode(const CVector&, const CVector&) { EAXJMP(0x46FCC0); }
+
+
+bool
+CCamera::GetFading()
+{
+ return m_bFading;
+}
bool
CCamera::IsSphereVisible(const CVector &center, float radius, const CMatrix *mat)
diff --git a/src/core/Camera.h b/src/core/Camera.h
index 3ce0d9a6..97ed79f4 100644
--- a/src/core/Camera.h
+++ b/src/core/Camera.h
@@ -471,6 +471,11 @@ int m_iModeObbeCamIsInForCar;
float Find3rdPersonQuickAimPitch(void);
+ void TakeControl(CEntity*, int16, int16, int32);
+ void TakeControlNoEntity(const CVector&, int16, int32);
+ void SetCamPositionForFixedMode(const CVector&, const CVector&);
+ bool GetFading();
+
void dtor(void) { this->CCamera::~CCamera(); }
};
static_assert(offsetof(CCamera, m_WideScreenOn) == 0x70, "CCamera: error");
diff --git a/src/core/Collision.cpp b/src/core/Collision.cpp
index df1dcd63..66b29d9f 100644
--- a/src/core/Collision.cpp
+++ b/src/core/Collision.cpp
@@ -144,7 +144,7 @@ CCollision::LoadCollisionWhenINeedIt(bool forceChange)
if(veh && veh->IsTrain()){
if(((CTrain*)veh)->m_nDoorState != TRAIN_DOOR_OPEN)
return;
- }else if(playerCoors.z < 4.0f && !CCullZones::DoINeedToLoadCollision())
+ }else if(playerCoors.z < -4.0f && !CCullZones::DoINeedToLoadCollision())
return;
// Figure out whose level's collisions we're most likely to be interested in
diff --git a/src/core/General.h b/src/core/General.h
index 7c0c9562..8f9aa044 100644
--- a/src/core/General.h
+++ b/src/core/General.h
@@ -74,6 +74,7 @@ public:
return result;
}
+ // Returns an angle such that x2/y2 looks at x1/y1 with its forward vector if rotated by that angle
static float GetRadianAngleBetweenPoints(float x1, float y1, float x2, float y2)
{
float x = x2 - x1;
@@ -95,9 +96,26 @@ public:
}
}
+ // should return direction in 0-8 range. fits perfectly to peds' path directions.
+ static int CGeneral::GetNodeHeadingFromVector(float x, float y)
+ {
+ float angle = CGeneral::GetRadianAngleBetweenPoints(x, y, 0.0f, 0.0f);
+ if (angle < 0.0f)
+ angle += TWOPI;
+
+ angle = DEGTORAD(22.5f) + TWOPI - angle;
+
+ if (angle >= TWOPI)
+ angle -= TWOPI;
+
+ return (int)floorf(angle / DEGTORAD(45.0f));
+ }
+
// not too sure about all these...
static uint16 GetRandomNumber(void)
{ return myrand() & MYRAND_MAX; }
+ static bool GetRandomTrueFalse(void)
+ { return GetRandomNumber() < MYRAND_MAX / 2; }
// Probably don't want to ever reach high
static float GetRandomNumberInRange(float low, float high)
{ return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); }
diff --git a/src/core/IniFile.cpp b/src/core/IniFile.cpp
new file mode 100644
index 00000000..08b30876
--- /dev/null
+++ b/src/core/IniFile.cpp
@@ -0,0 +1,28 @@
+#include "common.h"
+#include "patcher.h"
+#include "IniFile.h"
+
+#include "CarCtrl.h"
+#include "FileMgr.h"
+#include "main.h"
+#include "Population.h"
+
+float &CIniFile::PedNumberMultiplier = *(float*)0x6182F4;
+float &CIniFile::CarNumberMultiplier = *(float*)0x6182F8;
+
+void CIniFile::LoadIniFile()
+{
+ CFileMgr::SetDir("");
+ int f = CFileMgr::OpenFile("gta3.ini", "r");
+ if (f){
+ CFileMgr::ReadLine(f, gString, 200);
+ sscanf(gString, "%f", &PedNumberMultiplier);
+ PedNumberMultiplier = min(3.0f, max(0.5f, PedNumberMultiplier));
+ CFileMgr::ReadLine(f, gString, 200);
+ sscanf(gString, "%f", &CarNumberMultiplier);
+ CarNumberMultiplier = min(3.0f, max(0.5f, CarNumberMultiplier));
+ CFileMgr::CloseFile(f);
+ }
+ CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier;
+ CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier;
+} \ No newline at end of file
diff --git a/src/core/IniFile.h b/src/core/IniFile.h
new file mode 100644
index 00000000..9a98151b
--- /dev/null
+++ b/src/core/IniFile.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class CIniFile
+{
+public:
+ static void LoadIniFile();
+
+ static float& PedNumberMultiplier;
+ static float& CarNumberMultiplier;
+};
diff --git a/src/core/Radar.cpp b/src/core/Radar.cpp
index ea7a7ffa..f04e14d1 100644
--- a/src/core/Radar.cpp
+++ b/src/core/Radar.cpp
@@ -100,9 +100,9 @@ void CRadar::ChangeBlipBrightness(int32 i, int32 bright)
#endif
#if 1
-WRAPPER void CRadar::ChangeBlipColour(int32) { EAXJMP(0x4A5770); }
+WRAPPER void CRadar::ChangeBlipColour(int32, int32) { EAXJMP(0x4A5770); }
#else
-void CRadar::ChangeBlipColour(int32 i)
+void CRadar::ChangeBlipColour(int32 i, int32)
{
}
@@ -571,9 +571,9 @@ void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float
#endif
#if 1
-WRAPPER int32 CRadar::GetActualBlipArray(int32) { EAXJMP(0x4A41C0); }
+WRAPPER int32 CRadar::GetActualBlipArrayIndex(int32) { EAXJMP(0x4A41C0); }
#else
-int32 CRadar::GetActualBlipArray(int32 i)
+int32 CRadar::GetActualBlipArrayIndex(int32 i)
{
return int32();
}
@@ -737,18 +737,18 @@ void CRadar::SetBlipSprite(int32 i, int32 icon)
#endif
#if 1
-WRAPPER int CRadar::SetCoordBlip(int32, CVector, int32) { EAXJMP(0x4A5590); }
+WRAPPER int32 CRadar::SetCoordBlip(eBlipType, CVector, int32, eBlipDisplay) { EAXJMP(0x4A5590); }
#else
-int CRadar::SetCoordBlip(int32 type, CVector pos, int32 flag)
+int CRadar::SetCoordBlip(eBlipType type, CVector pos, int32 flag, eBlipDisplay)
{
return 0;
}
#endif
#if 1
-WRAPPER int CRadar::SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag) { EAXJMP(0x4A5640); }
+WRAPPER int CRadar::SetEntityBlip(eBlipType type, int32, int32, eBlipDisplay) { EAXJMP(0x4A5640); }
#else
-int CRadar::SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag)
+int CRadar::SetEntityBlip(eBlipType type, int32, int32, eBlipDisplay)
{
return 0;
}
diff --git a/src/core/Radar.h b/src/core/Radar.h
index e5396a50..1ec28070 100644
--- a/src/core/Radar.h
+++ b/src/core/Radar.h
@@ -98,7 +98,7 @@ public:
public:
static int CalculateBlipAlpha(float dist);
static void ChangeBlipBrightness(int32 i, int32 bright);
- static void ChangeBlipColour(int32 i);
+ static void ChangeBlipColour(int32 i, int32);
static void ChangeBlipDisplay(int32 i, int16 flag);
static void ChangeBlipScale(int32 i, int16 scale);
static void ClearBlip(int32 i);
@@ -113,7 +113,7 @@ public:
static void DrawRadarSection(int32 x, int32 y);
static void DrawRadarSprite(int32 sprite, float x, float y, int32 alpha);
static void DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha);
- static int32 GetActualBlipArray(int32 i);
+ static int32 GetActualBlipArrayIndex(int32 i);
static int32 GetNewUniqueBlipIndex(int32 i);
static int32 GetRadarTraceColour(int32 color, bool bright);
static void Initialise();
@@ -125,8 +125,8 @@ public:
static void RequestMapSection(int32 x, int32 y);
static void SaveAllRadarBlips(int32);
static void SetBlipSprite(int32 i, int32 icon);
- static int SetCoordBlip(int32 type, CVector pos, int32 flag);
- static int SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag);
+ static int32 SetCoordBlip(eBlipType type, CVector pos, int32, eBlipDisplay flag);
+ static int32 SetEntityBlip(eBlipType type, int32, int32, eBlipDisplay);
static void SetRadarMarkerState(int32 i, int32 flag);
static void ShowRadarMarker(CVector pos, int16 color, float radius);
static void ShowRadarTrace(float x, float y, uint32 size, uint32 red, uint32 green, uint32 blue, uint32 alpha);
diff --git a/src/core/Stats.cpp b/src/core/Stats.cpp
index 01bbf82e..9d0e7df1 100644
--- a/src/core/Stats.cpp
+++ b/src/core/Stats.cpp
@@ -7,8 +7,10 @@ bool& CStats::CommercialPassed = *(bool*)0x8F4334;
bool& CStats::IndustrialPassed = *(bool*)0x8E2A68;
int32 &CStats::NumberKillFrenziesPassed = *(int32*)0x8E287C;
int32 &CStats::PeopleKilledByOthers = *(int32*)0x8E2C50;
+int32 &CStats::HelisDestroyed = *(int32*)0x8E2A64;
+int32 *CStats::PedsKilledOfThisType = (int32*)0x880DBC;
void CStats::AnotherKillFrenzyPassed()
{
++NumberKillFrenziesPassed;
-} \ No newline at end of file
+}
diff --git a/src/core/Stats.h b/src/core/Stats.h
index c536465f..90db25e8 100644
--- a/src/core/Stats.h
+++ b/src/core/Stats.h
@@ -9,6 +9,8 @@ public:
static bool& IndustrialPassed;
static int32 &NumberKillFrenziesPassed;
static int32 &PeopleKilledByOthers;
+ static int32 &HelisDestroyed;
+ static int32 *PedsKilledOfThisType; //[NUM_PEDTYPES]
public:
static void AnotherKillFrenzyPassed();
diff --git a/src/core/Wanted.h b/src/core/Wanted.h
index 7cd89b7e..34a4b58d 100644
--- a/src/core/Wanted.h
+++ b/src/core/Wanted.h
@@ -77,6 +77,8 @@ public:
void ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare);
void UpdateWantedLevel();
+ bool IsIgnored(void) { return m_bIgnoredByCops || m_bIgnoredByEveryone; }
+
static int32 WorkOutPolicePresence(CVector posn, float radius);
static void SetMaximumWantedLevel(int32 level);
};
diff --git a/src/core/World.cpp b/src/core/World.cpp
index 0440a951..c6eb831c 100644
--- a/src/core/World.cpp
+++ b/src/core/World.cpp
@@ -646,7 +646,7 @@ CWorld::FindObjectsInRange(CVector &centre, float distance, bool ignoreZ, short
}
CEntity*
-CWorld::TestSphereAgainstWorld(CVector centre, float distance, CEntity* entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects)
+CWorld::TestSphereAgainstWorld(CVector centre, float distance, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects)
{
CEntity* foundE = nil;
diff --git a/src/core/World.h b/src/core/World.h
index e4f46589..6c52da5a 100644
--- a/src/core/World.h
+++ b/src/core/World.h
@@ -95,8 +95,8 @@ public:
static bool GetIsLineOfSightSectorClear(CSector &sector, const CColLine &line, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false);
static bool GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, bool ignoreSomeObjects = false);
- static CEntity* TestSphereAgainstWorld(CVector, float, CEntity*, bool, bool, bool, bool, bool, bool);
- static CEntity* TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool);
+ static CEntity *TestSphereAgainstWorld(CVector centre, float distance, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects);
+ static CEntity *TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool);
static void FindObjectsInRangeSectorList(CPtrList&, CVector&, float, bool, short*, short, CEntity**);
static void FindObjectsInRange(CVector&, float, bool, short*, short, CEntity**, bool, bool, bool, bool, bool);
static float FindGroundZForCoord(float x, float y);
diff --git a/src/core/common.h b/src/core/common.h
index 62cb2baa..36f67bfa 100644
--- a/src/core/common.h
+++ b/src/core/common.h
@@ -197,7 +197,7 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define ABS(a) (((a) < 0) ? (-a) : (a))
+#define ABS(a) (((a) < 0) ? (-(a)) : (a))
#define norm(value, min, max) (((value) < (min)) ? 0 : (((value) > (max)) ? 1 : (((value) - (min)) / ((max) - (min)))))
diff --git a/src/core/config.h b/src/core/config.h
index 52d1dab8..8eda6187 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -33,6 +33,12 @@ enum Config {
NUMTEMPOBJECTS = 30,
+ // Path data
+ NUM_PATHNODES = 4930,
+ NUM_CARPATHLINKS = 2076,
+ NUM_MAPOBJECTS = 1250,
+ NUM_PATHCONNECTIONS = 10260,
+
// Link list lengths
// TODO: alpha list
NUMCOLCACHELINKS = 200,
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 35b3cfa4..dc501075 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -14,7 +14,9 @@
#include "Streaming.h"
#include "PathFind.h"
#include "Boat.h"
+#include "Heli.h"
#include "Automobile.h"
+#include "Ped.h"
#include "debugmenu_public.h"
#include <vector>
@@ -138,6 +140,20 @@ SpawnCar(int id)
}
static void
+LetThemFollowYou(void) {
+ CPed* player = (CPed*) FindPlayerPed();
+ for (int i = 0; i < player->m_numNearPeds; i++) {
+
+ CPed* nearPed = player->m_nearPeds[i];
+ if (nearPed && !nearPed->IsPlayer()) {
+ nearPed->SetObjective(OBJECTIVE_FOLLOW_PED_IN_FORMATION, (void*)player);
+ nearPed->m_pedFormation = rand() & 7;
+ nearPed->bScriptObjectiveCompleted = false;
+ }
+ }
+}
+
+static void
FixCar(void)
{
CVehicle *veh = FindPlayerVehicle();
@@ -318,6 +334,12 @@ DebugMenuPopulate(void)
DebugMenuAddCmd("Debug", "Toggle Comedy Controls", ToggleComedy);
DebugMenuAddCmd("Debug", "Place Car on Road", PlaceOnRoad);
+ DebugMenuAddVarBool8("Debug", "Catalina Heli On", (int8*)&CHeli::CatalinaHeliOn, nil);
+ DebugMenuAddCmd("Debug", "Catalina Fly By", CHeli::StartCatalinaFlyBy);
+ DebugMenuAddCmd("Debug", "Catalina Take Off", CHeli::CatalinaTakeOff);
+ DebugMenuAddCmd("Debug", "Catalina Fly Away", CHeli::MakeCatalinaHeliFlyAway);
+ DebugMenuAddVarBool8("Debug", "Script Heli On", (int8*)0x95CD43, nil);
+
DebugMenuAddVarBool8("Debug", "Show Ped Road Groups", (int8*)&gbShowPedRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Car Road Groups", (int8*)&gbShowCarRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Collision Lines", (int8*)&gbShowCollisionLines, nil);
@@ -328,6 +350,11 @@ DebugMenuPopulate(void)
DebugMenuAddVarBool8("Debug", "Don't render Vehicles", (int8*)&gbDontRenderVehicles, nil);
DebugMenuAddVarBool8("Debug", "Don't render Objects", (int8*)&gbDontRenderObjects, nil);
+ DebugMenuAddCmd("Debug", "Make peds around you follow you", LetThemFollowYou);
+#ifndef FINAL
+ DebugMenuAddVarBool8("Debug", "Toggle unused fight feature", (int8*)&CPed::bUnusedFightThingOnPlayer, nil);
+#endif
+
DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start);
DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop);
@@ -347,29 +374,6 @@ delayedPatches10(int a, int b)
}
*/
-void __declspec(naked) HeadlightsFix()
-{
- static const float fMinusOne = -1.0f;
- _asm
- {
- fld [esp+708h-690h]
- fcomp fMinusOne
- fnstsw ax
- and ah, 5
- cmp ah, 1
- jnz HeadlightsFix_DontLimit
- fld fMinusOne
- fstp [esp+708h-690h]
-
-HeadlightsFix_DontLimit:
- fld [esp+708h-690h]
- fabs
- fld st
- push 0x5382F2
- retn
- }
-}
-
const int re3_buffsize = 1024;
static char re3_buff[re3_buffsize];
@@ -454,10 +458,6 @@ patch()
InjectHook(0x475E00, printf, PATCH_JUMP); // _Error
- // stolen from silentpatch (sorry)
- Patch<WORD>(0x5382BF, 0x0EEB);
- InjectHook(0x5382EC, HeadlightsFix, PATCH_JUMP);
-
// InterceptCall(&open_script_orig, open_script, 0x438869);
// InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E);
diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp
index 55ed5380..fbd1322d 100644
--- a/src/entities/Physical.cpp
+++ b/src/entities/Physical.cpp
@@ -11,6 +11,7 @@
#include "ParticleObject.h"
#include "Particle.h"
#include "SurfaceTable.h"
+#include "PathFind.h"
#include "CarCtrl.h"
#include "DMAudio.h"
#include "Automobile.h"
@@ -56,13 +57,13 @@ CPhysical::CPhysical(void)
m_phy_flagA80 = false;
m_fDistanceTravelled = 0.0f;
- m_pedTreadable = nil;
- m_carTreadable = nil;
+ m_treadable[PATH_CAR] = nil;
+ m_treadable[PATH_PED] = nil;
m_phy_flagA10 = false;
m_phy_flagA20 = false;
- m_nSurfaceTouched = SURFACE_DEFAULT;
+ m_nZoneLevel = 0;
}
CPhysical::~CPhysical(void)
@@ -267,16 +268,16 @@ CPhysical::AddCollisionRecord_Treadable(CEntity *ent)
{
if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){
CTreadable *t = (CTreadable*)ent;
- if(t->m_nodeIndicesPeds[0] >= 0 ||
- t->m_nodeIndicesPeds[1] >= 0 ||
- t->m_nodeIndicesPeds[2] >= 0 ||
- t->m_nodeIndicesPeds[3] >= 0)
- m_pedTreadable = t;
- if(t->m_nodeIndicesCars[0] >= 0 ||
- t->m_nodeIndicesCars[1] >= 0 ||
- t->m_nodeIndicesCars[2] >= 0 ||
- t->m_nodeIndicesCars[3] >= 0)
- m_carTreadable = t;
+ if(t->m_nodeIndices[PATH_PED][0] >= 0 ||
+ t->m_nodeIndices[PATH_PED][1] >= 0 ||
+ t->m_nodeIndices[PATH_PED][2] >= 0 ||
+ t->m_nodeIndices[PATH_PED][3] >= 0)
+ m_treadable[PATH_PED] = t;
+ if(t->m_nodeIndices[PATH_CAR][0] >= 0 ||
+ t->m_nodeIndices[PATH_CAR][1] >= 0 ||
+ t->m_nodeIndices[PATH_CAR][2] >= 0 ||
+ t->m_nodeIndices[PATH_CAR][3] >= 0)
+ m_treadable[PATH_CAR] = t;
}
}
diff --git a/src/entities/Physical.h b/src/entities/Physical.h
index 26ef0086..2786a7de 100644
--- a/src/entities/Physical.h
+++ b/src/entities/Physical.h
@@ -19,8 +19,7 @@ public:
int32 m_audioEntityId;
float unk1;
- CTreadable *m_carTreadable;
- CTreadable *m_pedTreadable;
+ CTreadable *m_treadable[2]; // car and ped
uint32 m_nLastTimeCollided;
CVector m_vecMoveSpeed; // velocity
CVector m_vecTurnSpeed; // angular velocity
@@ -115,6 +114,9 @@ public:
m_vecMoveSpeed.y = y;
m_vecMoveSpeed.z = z;
}
+ void SetMoveSpeed(const CVector& speed) {
+ m_vecMoveSpeed = speed;
+ }
const CVector &GetTurnSpeed() { return m_vecTurnSpeed; }
void SetTurnSpeed(float x, float y, float z) {
m_vecTurnSpeed.x = x;
diff --git a/src/entities/Treadable.h b/src/entities/Treadable.h
index d82ff52b..9e4de59a 100644
--- a/src/entities/Treadable.h
+++ b/src/entities/Treadable.h
@@ -8,8 +8,7 @@ public:
static void *operator new(size_t);
static void operator delete(void*, size_t);
- int16 m_nodeIndicesCars[12];
- int16 m_nodeIndicesPeds[12];
+ int16 m_nodeIndices[2][12]; // first car, then ped
bool GetIsATreadable(void) { return true; }
};
diff --git a/src/math/Vector2D.h b/src/math/Vector2D.h
index c8835ec0..a090155c 100644
--- a/src/math/Vector2D.h
+++ b/src/math/Vector2D.h
@@ -18,7 +18,30 @@ public:
x *= invsqrt;
y *= invsqrt;
}else
- x = 0.0f;
+ x = 1.0f;
+ }
+ const CVector2D &operator+=(CVector2D const &right) {
+ x += right.x;
+ y += right.y;
+ return *this;
+ }
+
+ const CVector2D &operator-=(CVector2D const &right) {
+ x -= right.x;
+ y -= right.y;
+ return *this;
+ }
+
+ const CVector2D &operator*=(float right) {
+ x *= right;
+ y *= right;
+ return *this;
+ }
+
+ const CVector2D &operator/=(float right) {
+ x /= right;
+ y /= right;
+ return *this;
}
CVector2D operator-(const CVector2D &rhs) const {
return CVector2D(x-rhs.x, y-rhs.y);
@@ -29,6 +52,9 @@ public:
CVector2D operator*(float t) const {
return CVector2D(x*t, y*t);
}
+ CVector2D operator/(float t) const {
+ return CVector2D(x/t, y/t);
+ }
};
inline float
@@ -42,3 +68,26 @@ CrossProduct2D(const CVector2D &v1, const CVector2D &v2)
{
return v1.x*v2.y - v1.y*v2.x;
}
+
+inline float
+Distance2D(const CVector2D &v, float x, float y)
+{
+ return Sqrt((v.x-x)*(v.x-x) + (v.y-y)*(v.y-y));
+}
+
+inline float
+DistanceSqr2D(const CVector2D &v, float x, float y)
+{
+ return (v.x-x)*(v.x-x) + (v.y-y)*(v.y-y);
+}
+
+inline void
+NormalizeXY(float &x, float &y)
+{
+ float l = Sqrt(x*x + y*y);
+ if(l != 0.0f){
+ x /= l;
+ y /= l;
+ }else
+ x = 1.0f;
+}
diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp
index c031b76e..19100d84 100644
--- a/src/modelinfo/VehicleModelInfo.cpp
+++ b/src/modelinfo/VehicleModelInfo.cpp
@@ -14,6 +14,7 @@
#include "Automobile.h"
#include "Train.h"
#include "Plane.h"
+#include "Heli.h"
#include "ModelIndices.h"
#include "ModelInfo.h"
@@ -98,13 +99,13 @@ RwObjectNameIdAssocation trainIds[] = {
};
RwObjectNameIdAssocation heliIds[] = {
- { "chassis_dummy", 1, VEHICLE_FLAG_COLLAPSE },
- { "toprotor", 2, 0 },
- { "backrotor", 3, 0 },
- { "tail", 4, 0 },
- { "topknot", 5, 0 },
- { "skid_left", 6, 0 },
- { "skid_right", 7, 0 },
+ { "chassis_dummy", HELI_CHASSIS, VEHICLE_FLAG_COLLAPSE },
+ { "toprotor", HELI_TOPROTOR, 0 },
+ { "backrotor", HELI_BACKROTOR, 0 },
+ { "tail", HELI_TAIL, 0 },
+ { "topknot", HELI_TOPKNOT, 0 },
+ { "skid_left", HELI_SKID_LEFT, 0 },
+ { "skid_right", HELI_SKID_RIGHT, 0 },
{ nil, 0, 0 }
};
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index 077cb89d..d0ceedff 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -31,6 +31,9 @@
#include "FileMgr.h"
#include "TempColModels.h"
#include "Pickups.h"
+#include "Train.h"
+#include "TrafficLights.h"
+#include "PedRoutes.h"
WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC430); }
WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); }
@@ -43,31 +46,44 @@ WRAPPER void CPed::SetMoveAnim(void) { EAXJMP(0x4C5A40); }
WRAPPER void CPed::SetFollowRoute(int16, int16) { EAXJMP(0x4DD690); }
WRAPPER void CPed::SetDuck(uint32) { EAXJMP(0x4E4920); }
WRAPPER void CPed::RegisterThreatWithGangPeds(CEntity*) { EAXJMP(0x4E3870); }
-WRAPPER bool CPed::Seek(void) { EAXJMP(0x4D1640); }
WRAPPER void CPed::SetFollowPath(CVector) { EAXJMP(0x4D2EA0); }
WRAPPER void CPed::RemoveInCarAnims(void) { EAXJMP(0x4E4E20); }
WRAPPER void CPed::StartFightDefend(uint8, uint8, uint8) { EAXJMP(0x4E7780); }
WRAPPER void CPed::SetDirectionToWalkAroundObject(CEntity*) { EAXJMP(0x4CCEB0); }
WRAPPER void CPed::SetRadioStation(void) { EAXJMP(0x4D7BC0); }
WRAPPER void CPed::MakeTyresMuddySectorList(CPtrList&) { EAXJMP(0x53CFD0); }
+WRAPPER void CPed::ProcessObjective(void) { EAXJMP(0x4D94E0); }
+
+CPed *gapTempPedList[50];
+uint16 gnNumTempPedList;
bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44;
bool &CPed::bPedCheat2 = *(bool*)0x95CD5A;
bool &CPed::bPedCheat3 = *(bool*)0x95CD59;
-CColPoint &CPed::ms_tempColPoint = *(CColPoint*)0x62DB14;
+CColPoint &aTempPedColPts = *(CColPoint*)0x62DB14;
+
+// TODO: CommentWaitTime should be hardcoded into exe, and it isn't reversed yet.
+CPedAudioData (&CPed::CommentWaitTime)[38] = *(CPedAudioData(*)[38]) * (uintptr*)0x5F94C4;
-// TODO: PedAudioData should be hardcoded into exe, and it isn't reversed yet.
-CPedAudioData (&CPed::PedAudioData)[38] = *(CPedAudioData(*)[38]) * (uintptr*)0x5F94C4;
+uint16 nPlayerInComboMove;
-uint16 &CPed::unknownFightThing = *(uint16*)0x95CC58;
-FightMove (&CPed::ms_fightMoves)[24] = * (FightMove(*)[24]) * (uintptr*)0x5F9844;
+FightMove (&tFightMoves)[24] = * (FightMove(*)[24]) * (uintptr*)0x5F9844;
-uint16 &CPed::distanceMultToCountPedNear = *(uint16*)0x5F8C98;
+uint16 &CPed::nThreatReactionRangeMultiplier = *(uint16*)0x5F8C98;
-CVector &CPed::offsetToOpenRegularCarDoor = *(CVector*)0x62E030;
-CVector &CPed::offsetToOpenLowCarDoor = *(CVector*)0x62E03C;
-CVector &CPed::offsetToOpenVanDoor = *(CVector*)0x62E048;
+CVector vecPedCarDoorAnimOffset;
+CVector vecPedCarDoorLoAnimOffset;
+CVector vecPedVanRearDoorAnimOffset;
+CVector &vecPedQuickDraggedOutCarAnimOffset = *(CVector*)0x62E06C;
+CVector &vecPedDraggedOutCarAnimOffset = *(CVector*)0x62E060;
+CVector &vecPedTrainDoorAnimOffset = *(CVector*)0x62E054;
+
+CVector2D &CPed::ms_vec2DFleePosition = *(CVector2D*)0x6EDF70;
+
+#ifndef FINAL
+bool CPed::bUnusedFightThingOnPlayer = false;
+#endif
void *CPed::operator new(size_t sz) { return CPools::GetPedPool()->New(); }
void *CPed::operator new(size_t sz, int handle) { return CPools::GetPedPool()->New(handle); }
@@ -283,7 +299,6 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_type = ENTITY_TYPE_PED;
bPedPhysics = true;
bUseCollisionRecords = true;
-// m_status = STATUS_SIMPLE;
m_vecAnimMoveDelta.x = 0.0f;
m_vecAnimMoveDelta.y = 0.0f;
@@ -307,7 +322,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_vecOffsetSeek.y = 0.0f;
m_vecOffsetSeek.z = 0.0f;
m_pedFormation = 0;
- m_lastThreatTimer = 0;
+ m_collidingThingTimer = 0;
m_nPedStateTimer = 0;
m_actionX = 0;
m_actionY = 0;
@@ -331,9 +346,9 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_pCurrentPhysSurface = nil;
m_vecOffsetFromPhysSurface = CVector(0.0f, 0.0f, 0.0f);
m_pSeekTarget = nil;
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
m_wepSkills = 0;
- field_318 = 1.0f;
+ m_distanceToCountSeekDone = 1.0f;
bRunningToPhone = false;
m_phoneId = -1;
m_lastAccident = 0;
@@ -359,11 +374,11 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_nPathNodes = 0;
m_nCurPathNode = 0;
m_nPathState = 0;
- m_pNextPathNode = nil;
m_pLastPathNode = nil;
+ m_pNextPathNode = nil;
m_routeLastPoint = -1;
- m_routePoints = 0;
- m_routePos = 0;
+ m_routeStartPoint = 0;
+ m_routePointsPassed = 0;
m_routeType = 0;
m_bodyPartBleeding = -1;
@@ -374,7 +389,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
bIsStanding = false;
m_ped_flagA2 = false;
- m_ped_flagA4 = false;
+ bIsAttacking = false;
bIsPointingGunAt = false;
bIsLooking = false;
m_ped_flagA20 = false;
@@ -386,7 +401,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
bIsTalking = false;
bIsInTheAir = false;
bIsLanding = false;
- m_ped_flagB20 = false;
+ bIsRunning = false;
m_ped_flagB40 = false;
m_ped_flagB80 = false;
@@ -531,7 +546,7 @@ RemoveAllModelCB(RwObject *object, void *data)
}
static PedOnGroundState
-CheckForPedsOnGroundToAttack(CPlayerPed *player, CPed **pedOnGround)
+CheckForPedsOnGroundToAttack(CPed *attacker, CPed **pedOnGround)
{
PedOnGroundState stateToReturn;
float angleToFace;
@@ -549,22 +564,22 @@ CheckForPedsOnGroundToAttack(CPlayerPed *player, CPed **pedOnGround)
if (!CGame::nastyGame)
return NO_PED;
- for (int currentPedId = 0; currentPedId < player->m_numNearPeds; currentPedId++) {
+ for (int currentPedId = 0; currentPedId < attacker->m_numNearPeds; currentPedId++) {
- currentPed = player->m_nearPeds[currentPedId];
+ currentPed = attacker->m_nearPeds[currentPedId];
- CVector posDifference = currentPed->GetPosition() - player->GetPosition();
+ CVector posDifference = currentPed->GetPosition() - attacker->GetPosition();
distance = posDifference.Magnitude();
if (distance < 2.0f) {
angleToFace = CGeneral::GetRadianAngleBetweenPoints(
currentPed->GetPosition().x, currentPed->GetPosition().y,
- player->GetPosition().x, player->GetPosition().y);
+ attacker->GetPosition().x, attacker->GetPosition().y);
angleToFace = CGeneral::LimitRadianAngle(angleToFace);
- player->m_fRotationCur = CGeneral::LimitRadianAngle(player->m_fRotationCur);
+ attacker->m_fRotationCur = CGeneral::LimitRadianAngle(attacker->m_fRotationCur);
- angleDiff = Abs(angleToFace - player->m_fRotationCur);
+ angleDiff = Abs(angleToFace - attacker->m_fRotationCur);
if (angleDiff > PI)
angleDiff = 2 * PI - angleDiff;
@@ -583,20 +598,21 @@ CheckForPedsOnGroundToAttack(CPlayerPed *player, CPed **pedOnGround)
pedOnTheFloor = currentPed;
}
}
- } else if ((distance >= 0.8f || angleDiff >= DEGTORAD(75.0f))
- && (distance >= 1.3f || angleDiff >= DEGTORAD(55.0f))
- && (distance >= 1.7f || angleDiff >= DEGTORAD(35.0f))
- && (distance >= 2.0f || angleDiff >= DEGTORAD(30.0f))) {
+ } else if ((distance < 0.8f && angleDiff < DEGTORAD(75.0f))
+ || (distance < 1.3f && angleDiff < DEGTORAD(55.0f))
+ || (distance < 1.7f && angleDiff < DEGTORAD(35.0f))
+ || (distance < 2.0f && angleDiff < DEGTORAD(30.0f))) {
+ // Either this condition or below one was probably returning 4 early in development. See Fight().
+ foundBelow = 1;
+ pedBelow = currentPed;
+ break;
+ } else {
if (angleDiff < DEGTORAD(75.0f)) {
foundBelow = 1;
if (!pedBelow)
pedBelow = currentPed;
}
- } else {
- foundBelow = 1;
- pedBelow = currentPed;
- break;
}
}
}
@@ -609,7 +625,7 @@ CheckForPedsOnGroundToAttack(CPlayerPed *player, CPed **pedOnGround)
stateToReturn = PED_DEAD_ON_THE_FLOOR;
} else if (foundBelow) {
currentPed = pedBelow;
- stateToReturn = PED_BELOW_PLAYER;
+ stateToReturn = PED_IN_FRONT_OF_ATTACKER;
} else {
currentPed = nil;
stateToReturn = NO_PED;
@@ -992,7 +1008,7 @@ CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg)
}
}
- if (!ped->m_ped_flagA4)
+ if (!ped->bIsAttacking)
ped->ClearAttack();
}
@@ -1018,7 +1034,7 @@ CPed::Attack(void)
ourWeapon = CWeaponInfo::GetWeaponInfo(ourWeaponType);
ourWeaponFire = ourWeapon->m_eWeaponFire;
weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ourWeapon->m_AnimToPlay);
- lastReloadWasInFuture = m_ped_flagA4;
+ lastReloadWasInFuture = bIsAttacking;
reloadAnimAssoc = nil;
reloadAnim = NUM_ANIMS;
delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire;
@@ -1059,7 +1075,7 @@ CPed::Attack(void)
if (!weaponAnimAssoc) {
if (lastReloadWasInFuture) {
if (ourWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->field_1380) {
- if (!CGame::nastyGame || ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), nil) < PED_ON_THE_FLOOR) {
+ if (!CGame::nastyGame || ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) {
weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f);
}
else {
@@ -1131,7 +1147,7 @@ CPed::Attack(void)
CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, reloadAnim, 8.0f);
ClearLookFlag();
ClearAimFlag();
- m_ped_flagA4 = false;
+ bIsAttacking = false;
bIsPointingGunAt = false;
m_lastHitTime = CTimer::GetTimeInMilliseconds();
return;
@@ -1145,8 +1161,7 @@ CPed::Attack(void)
weaponAnimAssoc->speed = 0.5f;
- // BUG: We currently don't know any situation this cond. could be true.
- if (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime) {
+ if (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_lastHitTime) {
weaponAnimAssoc->callbackType = 0;
}
}
@@ -1187,11 +1202,11 @@ CPed::Attack(void)
if (weaponAnimTime > animLoopEnd || !weaponAnimAssoc->IsRunning() && ourWeaponFire != WEAPON_FIRE_PROJECTILE) {
if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd
- && (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime)
+ && (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_lastHitTime)
&& GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) {
weaponAnim = weaponAnimAssoc->animId;
- if (ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), nil) < PED_ON_THE_FLOOR) {
+ if (ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) {
if (weaponAnim != ourWeapon->m_Anim2ToPlay || weaponAnim == ANIM_RBLOCK_CSHOOT) {
weaponAnimAssoc->Start(ourWeapon->m_fAnimLoopStart);
} else {
@@ -1234,7 +1249,7 @@ CPed::Attack(void)
if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire)
lastReloadWasInFuture = false;
- m_ped_flagA4 = lastReloadWasInFuture;
+ bIsAttacking = lastReloadWasInFuture;
}
void
@@ -1470,13 +1485,13 @@ CPed::GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatP
vehModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(veh->m_modelIndex);
if (veh->bIsVan && (component == CAR_DOOR_LR || component == CAR_DOOR_RR)) {
seatOffset = 0.0f;
- vehDoorOffset = offsetToOpenVanDoor;
+ vehDoorOffset = vecPedVanRearDoorAnimOffset;
} else {
seatOffset = veh->pHandling->fSeatOffsetDistance * seatPosMult;
if (veh->bLowVehicle) {
- vehDoorOffset = offsetToOpenLowCarDoor;
+ vehDoorOffset = vecPedCarDoorLoAnimOffset;
} else {
- vehDoorOffset = offsetToOpenRegularCarDoor;
+ vehDoorOffset = vecPedCarDoorAnimOffset;
}
}
@@ -1537,12 +1552,12 @@ CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component)
CVector localVehDoorOffset;
if (veh->bIsVan && (component == VEHICLE_ENTER_REAR_LEFT || component == VEHICLE_ENTER_REAR_RIGHT)) {
- localVehDoorOffset = offsetToOpenVanDoor;
+ localVehDoorOffset = vecPedVanRearDoorAnimOffset;
} else {
if (veh->bIsLow) {
- localVehDoorOffset = offsetToOpenLowCarDoor;
+ localVehDoorOffset = vecPedCarDoorLoAnimOffset;
} else {
- localVehDoorOffset = offsetToOpenRegularCarDoor;
+ localVehDoorOffset = vecPedCarDoorAnimOffset;
}
}
@@ -1885,7 +1900,7 @@ CPed::PlayFootSteps(void)
CVector2D top(forward * 0.26f);
CVector2D right(GetRight() * 0.14f);
- CShadows::AddPermanentShadow(1, gpBloodPoolTex, &footPos,
+ CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &footPos,
top.x, top.y,
right.x, right.y,
255, 255, 0, 0, 4.0f, 3000.0f, 1.0f);
@@ -1986,9 +2001,6 @@ CPed::SortPeds(CPed **list, int min, int max)
void
CPed::BuildPedLists(void)
{
- static CPed *unsortedNearPeds[10];
- uint16 nextNearPedSlot = 0;
-
if ((CTimer::GetFrameCounter() + (m_randomSeed % 256)) % 16) {
for(int i = 0; i < 10; ) {
@@ -2019,6 +2031,7 @@ CPed::BuildPedLists(void)
(centre.y - 20.0f) * 0.025f + 50.0f,
(centre.x + 20.0f) * 0.025f + 50.0f,
(centre.y + 20.0f) * 0.025f + 50.0f);
+ gnNumTempPedList = 0;
for(int y = rect.top; y <= rect.bottom; y++) {
for(int x = rect.left; x <= rect.right; x++) {
@@ -2026,19 +2039,18 @@ CPed::BuildPedLists(void)
CPed *ped = (CPed*)pedPtrNode->item;
if (ped != this && !ped->bInVehicle) {
float dist = (ped->GetPosition() - GetPosition()).Magnitude2D();
- if (distanceMultToCountPedNear * 30.0f > dist)
- {
- unsortedNearPeds[nextNearPedSlot] = ped;
- nextNearPedSlot++;
+ if (nThreatReactionRangeMultiplier * 30.0f > dist) {
+ gapTempPedList[gnNumTempPedList] = ped;
+ gnNumTempPedList++;
}
}
}
}
}
- unsortedNearPeds[nextNearPedSlot] = nil;
- SortPeds(unsortedNearPeds, 0, nextNearPedSlot - 1);
+ gapTempPedList[gnNumTempPedList] = nil;
+ SortPeds(gapTempPedList, 0, gnNumTempPedList - 1);
for (m_numNearPeds = 0; m_numNearPeds < 10; m_numNearPeds++) {
- CPed *ped = unsortedNearPeds[m_numNearPeds];
+ CPed *ped = gapTempPedList[m_numNearPeds];
if (!ped)
break;
@@ -2482,9 +2494,9 @@ CPed::SetObjective(eObjective newObj, void *entity)
case OBJECTIVE_KILL_CHAR_ON_FOOT:
case OBJECTIVE_KILL_CHAR_ANY_MEANS:
case OBJECTIVE_MUG_CHAR:
- m_pLastPathNode = nil;
+ m_pNextPathNode = nil;
bIsFleeing = false;
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
m_pedInObjective = (CPed*)entity;
m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective);
m_pLookTarget = (CEntity*)entity;
@@ -2494,7 +2506,7 @@ CPed::SetObjective(eObjective newObj, void *entity)
case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS:
case OBJECTIVE_GOTO_CHAR_ON_FOOT:
case OBJECTIVE_FIGHT_CHAR:
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
m_pedInObjective = (CPed*)entity;
m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective);
break;
@@ -2535,7 +2547,7 @@ CPed::SetObjective(eObjective newObj, void *entity)
m_carInObjective->RegisterReference((CEntity**)&m_carInObjective);
m_pSeekTarget = m_carInObjective;
m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget);
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
if (newObj == OBJECTIVE_SOLICIT) {
m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000;
} else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == MISSION_CHAR &&
@@ -3124,7 +3136,7 @@ CPed::ClearAll(void)
m_nPedState = PED_NONE;
m_nMoveState = PEDMOVE_NONE;
m_pSeekTarget = nil;
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
m_fleeFromPosX = 0.0f;
m_fleeFromPosY = 0.0f;
m_fleeFrom = nil;
@@ -3708,7 +3720,7 @@ CPed::SetGetUp(void)
&& ((CTimer::GetFrameCounter() + m_randomSeed % 256 + 5) % 8
|| CCollision::ProcessColModels(GetMatrix(), *CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(),
collidingVeh->GetMatrix(), *CModelInfo::GetModelInfo(collidingVeh->m_modelIndex)->GetColModel(),
- &ms_tempColPoint, nil, nil) > 0)) {
+ &aTempPedColPts, nil, nil) > 0)) {
bGetUpAnimStarted = false;
if (IsPlayer())
@@ -3849,11 +3861,11 @@ CPed::SetWanderPath(int8 pathStateDest)
if (pathStateDest == 0)
pathStateDest = CGeneral::GetRandomNumberInRange(1, 7);
- ThePaths.FindNextNodeWandering(1, GetPosition(), &m_pNextPathNode, &m_pLastPathNode,
+ ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode,
m_nPathState, &nextPathState);
// Circular loop until we find a node for current m_nPathState
- while (!m_pLastPathNode) {
+ while (!m_pNextPathNode) {
m_nPathState = (m_nPathState+1) % 8;
// We're at where we started and couldn't find any node
@@ -3862,7 +3874,7 @@ CPed::SetWanderPath(int8 pathStateDest)
SetIdle();
return false;
}
- ThePaths.FindNextNodeWandering(1, GetPosition(), &m_pNextPathNode, &m_pLastPathNode,
+ ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode,
m_nPathState, &nextPathState);
}
@@ -3870,7 +3882,7 @@ CPed::SetWanderPath(int8 pathStateDest)
m_nPathState = nextPathState;
m_nPedState = PED_WANDER_PATH;
SetMoveState(PEDMOVE_WALK);
- m_ped_flagB20 = false;
+ bIsRunning = false;
return true;
}
} else {
@@ -3955,10 +3967,10 @@ CPed::RestorePreviousState(void)
break;
case PED_WANDER_PATH:
m_nPedState = PED_WANDER_PATH;
- m_ped_flagB20 = false;
+ bIsRunning = false;
if (!m_ped_flagC80) {
- if (m_pLastPathNode) {
- CVector diff = m_pLastPathNode->pos - GetPosition();
+ if (m_pNextPathNode) {
+ CVector diff = m_pNextPathNode->pos - GetPosition();
if (diff.MagnitudeSqr() < 49.0f) {
SetMoveState(PEDMOVE_WALK);
break;
@@ -4058,8 +4070,7 @@ CPed::GrantAmmo(eWeaponType weaponType, uint32 ammo)
{
if (HasWeapon(weaponType)) {
GetWeapon(weaponType).m_nAmmoTotal += ammo;
- }
- else {
+ } else {
GetWeapon(weaponType).Initialise(weaponType, ammo);
m_maxWeaponTypeAllowed++;
}
@@ -4270,15 +4281,15 @@ CPed::SetAttack(CEntity* victim)
return;
if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_HGUN_RELOAD)) {
- m_ped_flagA4 = false;
+ bIsAttacking = false;
return;
}
if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_AK_RELOAD)) {
if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->field_1380)
- m_ped_flagA4 = false;
+ bIsAttacking = false;
else
- m_ped_flagA4 = true;
+ bIsAttacking = true;
return;
}
@@ -4295,7 +4306,7 @@ CPed::SetAttack(CEntity* victim)
if (m_nPedState != PED_ATTACK) {
m_nPedState = PED_ATTACK;
- m_ped_flagA4 = false;
+ bIsAttacking = false;
animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, 8.0f);
animAssoc->SetRun();
if (animAssoc->currentTime != animAssoc->hierarchy->totalLength)
@@ -4334,7 +4345,7 @@ CPed::SetAttack(CEntity* victim)
}
}
if (m_nPedState == PED_ATTACK) {
- m_ped_flagA4 = true;
+ bIsAttacking = true;
return;
}
@@ -4370,7 +4381,7 @@ CPed::SetAttack(CEntity* victim)
animDelta = 1000.0f;
if (GetWeapon()->m_eWeaponType != WEAPONTYPE_BASEBALLBAT
- || CheckForPedsOnGroundToAttack((CPlayerPed*)this, nil) < PED_ON_THE_FLOOR) {
+ || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) {
animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, animDelta);
} else {
animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, animDelta);
@@ -4378,7 +4389,7 @@ CPed::SetAttack(CEntity* victim)
}
animAssoc->SetRun();
- if (animAssoc->currentTime != animAssoc->hierarchy->totalLength)
+ if (animAssoc->currentTime == animAssoc->hierarchy->totalLength)
animAssoc->SetCurrentTime(0.0f);
animAssoc->SetFinishCallback(FinishedAttackCB, this);
@@ -4438,7 +4449,7 @@ CPed::StartFightAttack(uint8 buttonPressure)
CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE)->blendAmount = 1.0f;
CPed *pedOnGround = nil;
- if (IsPlayer() && CheckForPedsOnGroundToAttack((CPlayerPed*)this, &pedOnGround) > PED_BELOW_PLAYER) {
+ if (IsPlayer() && CheckForPedsOnGroundToAttack(this, &pedOnGround) > PED_IN_FRONT_OF_ATTACKER) {
m_lastFightMove = FIGHTMOVE_GROUNDKICK;
} else if (m_pedStats->m_flags & STAT_SHOPPING_BAGS) {
m_lastFightMove = FIGHTMOVE_ROUNDHOUSE;
@@ -4457,14 +4468,18 @@ CPed::StartFightAttack(uint8 buttonPressure)
SetLookFlag(pedOnGround, 1);
SetLookTimer(1500);
}
- animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ms_fightMoves[m_lastFightMove].animId, 4.0f);
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_lastFightMove].animId, 4.0f);
animAssoc->SetFinishCallback(FinishFightMoveCB, this);
m_fightUnk2 = 0;
- m_fightUnk1 = 0;
- m_ped_flagA4 = true;
+ m_takeAStepAfterAttack = false;
+#ifndef FINAL
+ m_takeAStepAfterAttack = IsPlayer() && bUnusedFightThingOnPlayer;
+#endif
+
+ bIsAttacking = true;
if (IsPlayer())
- unknownFightThing = 0;
+ nPlayerInComboMove = 0;
}
void
@@ -4513,28 +4528,28 @@ CPed::LoadFightData(void)
if (strncmp(moveName, "ENDWEAPONDATA", 13) == 0)
return;
- ms_fightMoves[moveId].startFireTime = startFireTime / 30.0f;
- ms_fightMoves[moveId].endFireTime = endFireTime / 30.0f;
- ms_fightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f;
- ms_fightMoves[moveId].strikeRadius = strikeRadius;
- ms_fightMoves[moveId].damage = damage;
- ms_fightMoves[moveId].flags = flags;
+ tFightMoves[moveId].startFireTime = startFireTime / 30.0f;
+ tFightMoves[moveId].endFireTime = endFireTime / 30.0f;
+ tFightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f;
+ tFightMoves[moveId].strikeRadius = strikeRadius;
+ tFightMoves[moveId].damage = damage;
+ tFightMoves[moveId].flags = flags;
switch (hitLevel) {
case 'G':
- ms_fightMoves[moveId].hitLevel = 1;
+ tFightMoves[moveId].hitLevel = 1;
break;
case 'H':
- ms_fightMoves[moveId].hitLevel = 4;
+ tFightMoves[moveId].hitLevel = 4;
break;
case 'L':
- ms_fightMoves[moveId].hitLevel = 2;
+ tFightMoves[moveId].hitLevel = 2;
break;
case 'M':
- ms_fightMoves[moveId].hitLevel = 3;
+ tFightMoves[moveId].hitLevel = 3;
break;
case 'N':
- ms_fightMoves[moveId].hitLevel = 0;
+ tFightMoves[moveId].hitLevel = 0;
break;
default:
break;
@@ -4542,9 +4557,9 @@ CPed::LoadFightData(void)
if (strncmp(animName, "null", 4) != 0) {
animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName);
- ms_fightMoves[moveId].animId = (AnimationId)animAssoc->animId;
+ tFightMoves[moveId].animId = (AnimationId)animAssoc->animId;
} else {
- ms_fightMoves[moveId].animId = ANIM_WALK;
+ tFightMoves[moveId].animId = ANIM_WALK;
}
moveId++;
}
@@ -4584,9 +4599,9 @@ CPed::FightStrike(CVector &touchedNodePos)
for (int i = 0; i < m_numNearPeds; i++) {
nearPed = m_nearPeds[i];
if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED)
- maxDistanceToBeBeaten = nearPed->GetBoundRadius() + ms_fightMoves[m_lastFightMove].strikeRadius + 0.1f;
+ maxDistanceToBeBeaten = nearPed->GetBoundRadius() + tFightMoves[m_lastFightMove].strikeRadius + 0.1f;
else
- maxDistanceToBeBeaten = nearPed->GetBoundRadius() + ms_fightMoves[m_lastFightMove].strikeRadius;
+ maxDistanceToBeBeaten = nearPed->GetBoundRadius() + tFightMoves[m_lastFightMove].strikeRadius;
if (nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) {
CVector nearPedCentre;
@@ -4605,7 +4620,7 @@ CPed::FightStrike(CVector &touchedNodePos)
attackDistance = nearPed->GetPosition() + ourCol->spheres[j].center;
attackDistance -= touchedNodePos;
CColSphere *ourPieces = ourCol->spheres;
- float maxDistanceToBeat = ourPieces[j].radius + ms_fightMoves[m_lastFightMove].strikeRadius;
+ float maxDistanceToBeat = ourPieces[j].radius + tFightMoves[m_lastFightMove].strikeRadius;
// We can beat him too
if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) {
@@ -4626,7 +4641,7 @@ CPed::FightStrike(CVector &touchedNodePos)
float oldVictimHealth = nearPed->m_fHealth;
CVector bloodPos = 0.5f * attackDistance + touchedNodePos;
- int damageMult = ms_fightMoves[m_lastFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1;
+ int damageMult = tFightMoves[m_lastFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1;
CVector2D diff (GetPosition() - nearPed->GetPosition());
int direction = nearPed->GetLocalDirection(diff);
@@ -4654,16 +4669,16 @@ CPed::FightStrike(CVector &touchedNodePos)
else
unk2 = damageMult;
- nearPed->StartFightDefend(direction, ms_fightMoves[m_lastFightMove].hitLevel, unk2);
+ nearPed->StartFightDefend(direction, tFightMoves[m_lastFightMove].hitLevel, unk2);
PlayHitSound(nearPed);
m_fightUnk2 = -1;
- RpAnimBlendClumpGetAssociation(GetClump(), ms_fightMoves[m_lastFightMove].animId)->speed = 0.6f;
+ RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_lastFightMove].animId)->speed = 0.6f;
if (nearPed->m_nPedState != PED_DIE && nearPed->m_nPedState != PED_DEAD) {
nearPed->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, closestPedPiece, direction);
}
if (CGame::nastyGame
- && ms_fightMoves[m_lastFightMove].hitLevel > 3
+ && tFightMoves[m_lastFightMove].hitLevel > 3
&& nearPed->m_nPedState == PED_DIE
&& nearPed->GetIsOnScreen()) {
@@ -4803,7 +4818,7 @@ CPed::SetFlee(CVector2D &from, int time)
}
bIsFleeing = true;
- m_pLastPathNode = nil;
+ m_pNextPathNode = nil;
m_fleeTimer = CTimer::GetTimeInMilliseconds() + time;
float angleToFace = CGeneral::GetRadianAngleBetweenPoints(
@@ -4869,6 +4884,7 @@ CPed::SetWaitState(eWaitState state, void *time)
if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) {
ClearObjective();
RestorePreviousState();
+ m_hitRecoverTimer = CTimer::GetTimeInMilliseconds() + 30000;
}
break;
case WAITSTATE_TURN180:
@@ -4894,6 +4910,7 @@ CPed::SetWaitState(eWaitState state, void *time)
if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) {
ClearObjective();
RestorePreviousState();
+ m_hitRecoverTimer = CTimer::GetTimeInMilliseconds() + 30000;
}
break;
case WAITSTATE_LOOK_ABOUT:
@@ -5060,9 +5077,9 @@ CPed::Say(uint16 audio)
if (audioToPlay < m_queuedSound) {
if (audioToPlay != m_lastQueuedSound || audioToPlay == SOUND_PED_DEATH
- || PedAudioData[audioToPlay - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime
+ || CommentWaitTime[audioToPlay - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime
+ m_lastSoundStart
- + (uint32) CGeneral::GetRandomNumberInRange(0, PedAudioData[audioToPlay - SOUND_PED_DEATH].m_nMaxRandomDelayTime) <= CTimer::GetTimeInMilliseconds()) {
+ + (uint32) CGeneral::GetRandomNumberInRange(0, CommentWaitTime[audioToPlay - SOUND_PED_DEATH].m_nMaxRandomDelayTime) <= CTimer::GetTimeInMilliseconds()) {
m_queuedSound = audioToPlay;
}
}
@@ -5098,10 +5115,10 @@ CPed::CollideWithPed(CPed *collideWith)
if (collideWith->m_nMoveState != PEDMOVE_STILL
&& (!collideWith->IsPlayer() || collideWith->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled())) {
- float weAndCarDist = (GetPosition() - m_vecSeekVehicle).MagnitudeSqr2D();
- float heAndCarDist = (collideWith->GetPosition() - m_vecSeekVehicle).MagnitudeSqr2D();
+ float seekPosDist = (GetPosition() - m_vecSeekPos).MagnitudeSqr2D();
+ float heAndSeekPosDist = (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D();
- if (weAndCarDist <= heAndCarDist) {
+ if (seekPosDist <= heAndSeekPosDist) {
waitTime = 1000;
collideWith->SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime);
collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime;
@@ -5273,7 +5290,7 @@ CPed::CollideWithPed(CPed *collideWith)
SetFlee(collideWith, 5000);
bIsFleeing = true;
- m_pLastPathNode = nil;
+ m_pNextPathNode = nil;
if (!doWeRun)
SetMoveState(PEDMOVE_WALK);
}
@@ -5349,12 +5366,12 @@ CPed::CreateDeadPedWeaponPickups(void)
// otherwise try another position (but disregard second check apparently)
angleToPed += 3.14f;
pickupPos = GetPosition();
- pickupPos.x = 1.5f * Sin(angleToPed);
- pickupPos.y = 1.5f * Cos(angleToPed);
+ pickupPos.x += 1.5f * Sin(angleToPed);
+ pickupPos.y += 1.5f * Cos(angleToPed);
pickupPos.z = CWorld::FindGroundZFor3DCoord(pickupPos.x, pickupPos.y, pickupPos.z, &found) + 0.5f;
}
if (found)
- CPickups::GenerateNewOne_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, min(weaponAmmo, CPickups::ms_maxAmmosForWeapons[weapon]));
+ CPickups::GenerateNewOne_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, min(weaponAmmo, AmmoForWeapon_OnStreet[weapon]));
}
ClearWeapons();
}
@@ -5464,7 +5481,7 @@ CPed::SetDead(void)
}
void
-CPed::SetSeek(CEntity *seeking, float unk)
+CPed::SetSeek(CEntity *seeking, float distanceToCountDone)
{
if (!IsPedInControl())
return;
@@ -5479,17 +5496,17 @@ CPed::SetSeek(CEntity *seeking, float unk)
SetStoredState();
m_nPedState = PED_SEEK_ENTITY;
- field_318 = unk;
+ m_distanceToCountSeekDone = distanceToCountDone;
m_pSeekTarget = seeking;
m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget);
SetMoveState(PEDMOVE_STILL);
}
void
-CPed::SetSeek(CVector pos, float unk)
+CPed::SetSeek(CVector pos, float distanceToCountDone)
{
if (!IsPedInControl()
- || (m_nPedState == PED_SEEK_POS && m_vecSeekVehicle.x != pos.x && m_vecSeekVehicle.y != pos.y))
+ || (m_nPedState == PED_SEEK_POS && m_vecSeekPos.x == pos.x && m_vecSeekPos.y == pos.y))
return;
if (GetWeapon()->m_eWeaponType == WEAPONTYPE_M16
@@ -5504,8 +5521,8 @@ CPed::SetSeek(CVector pos, float unk)
SetStoredState();
m_nPedState = PED_SEEK_POS;
- field_318 = unk;
- m_vecSeekVehicle = pos;
+ m_distanceToCountSeekDone = distanceToCountDone;
+ m_vecSeekPos = pos;
}
void
@@ -5649,7 +5666,7 @@ CPed::DuckAndCover(void)
&& CWorld::GetIsLineOfSightClear(GetPosition(), duckPos, 1, 0, 0, 1, 0, 0, 0)) {
SetSeek(duckPos, 1.0f);
m_headingRate = 15.0f;
- m_ped_flagB20 = true;
+ bIsRunning = true;
bDuckAndCover = true;
justDucked = true;
m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500;
@@ -5676,7 +5693,7 @@ CPed::DuckAndCover(void)
bKindaStayInSamePlace = true;
bDuckAndCover = false;
- m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f);
if (m_pSeekTarget && m_pSeekTarget->IsVehicle())
((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover++;
@@ -5692,13 +5709,1962 @@ CPed::DuckAndCover(void)
return false;
}
+void
+CPed::EndFight(uint8 endType)
+{
+ if (m_nPedState != PED_FIGHT)
+ return;
+
+ m_lastFightMove = FIGHTMOVE_NULL;
+ RestorePreviousState();
+ CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FIGHT_IDLE);
+ if (animAssoc)
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+
+ switch (endType) {
+ case ENDFIGHT_NORMAL:
+ CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 8.0f);
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT2_IDLE, 8.0f);
+ break;
+ case ENDFIGHT_WITH_A_STEP:
+ CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 1.0f);
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_WALK_START, 8.0f);
+ break;
+ case ENDFIGHT_FAST:
+ CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 8.0f);
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT2_IDLE, 8.0f)->speed = 2.0f;
+ break;
+ default:
+ break;
+ }
+ m_nWaitTimer = 0;
+}
+
+void
+CPed::EnterCar(void)
+{
+ if (m_pMyVehicle && m_pMyVehicle->m_status != STATUS_WRECKED && m_fHealth > 0.0f) {
+ CVehicle* veh = (CVehicle*)m_pSeekTarget;
+
+ // Not used.
+ // CVector posForDoor = GetPositionToOpenCarDoor(veh, m_vehEnterType);
+
+ if (veh->CanPedOpenLocks(this)) {
+ if (m_vehEnterType) {
+ CAnimBlendAssociation *enterAssoc = m_pVehicleAnim;
+ if (enterAssoc)
+ veh->ProcessOpenDoor(m_vehEnterType, enterAssoc->animId, enterAssoc->currentTime);
+ }
+ }
+ bIsInTheAir = false;
+ LineUpPedWithCar(LINE_UP_TO_CAR_START);
+ } else {
+ QuitEnteringCar();
+ SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f);
+ }
+}
+
+uint8
+CPed::GetNearestTrainPedPosition(CVehicle *train, CVector &enterPos)
+{
+ CVector enterStepOffset;
+ CVehicleModelInfo *trainModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(train->m_modelIndex);
+ CMatrix trainMat = CMatrix(train->GetMatrix());
+ CVector leftEntryPos, rightEntryPos, midEntryPos;
+ float distLeftEntry, distRightEntry, distMidEntry;
+
+ // enterStepOffset = vecPedCarDoorAnimOffset;
+ enterStepOffset = CVector(1.5f, 0.0f, 0.0f);
+
+ if (train->pPassengers[TRAIN_POS_LEFT_ENTRY]) {
+ distLeftEntry = 999.0f;
+ } else {
+ leftEntryPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterStepOffset;
+ leftEntryPos = Multiply3x3(trainMat, leftEntryPos);
+ leftEntryPos += train->GetPosition();
+ distLeftEntry = (leftEntryPos - GetPosition()).Magnitude();
+ }
+
+ if (train->pPassengers[TRAIN_POS_MID_ENTRY]) {
+ distMidEntry = 999.0f;
+ } else {
+ midEntryPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterStepOffset;
+ midEntryPos = Multiply3x3(trainMat, midEntryPos);
+ midEntryPos += train->GetPosition();
+ distMidEntry = (midEntryPos - GetPosition()).Magnitude();
+ }
+
+ if (train->pPassengers[TRAIN_POS_RIGHT_ENTRY]) {
+ distRightEntry = 999.0f;
+ } else {
+ rightEntryPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterStepOffset;
+ rightEntryPos = Multiply3x3(trainMat, rightEntryPos);
+ rightEntryPos += train->GetPosition();
+ distRightEntry = (rightEntryPos - GetPosition()).Magnitude();
+ }
+
+ if (distMidEntry < distLeftEntry) {
+ if (distMidEntry < distRightEntry) {
+ enterPos = midEntryPos;
+ m_vehEnterType = TRAIN_POS_MID_ENTRY;
+ } else {
+ enterPos = rightEntryPos;
+ m_vehEnterType = TRAIN_POS_RIGHT_ENTRY;
+ }
+ } else if (distRightEntry < distLeftEntry) {
+ enterPos = rightEntryPos;
+ m_vehEnterType = TRAIN_POS_RIGHT_ENTRY;
+ } else {
+ enterPos = leftEntryPos;
+ m_vehEnterType = TRAIN_POS_LEFT_ENTRY;
+ }
+
+ return 1;
+}
+
+uint8
+CPed::GetNearestTrainDoor(CVehicle *train, CVector &doorPos)
+{
+ GetNearestTrainPedPosition(train, doorPos);
+/*
+ // Not used.
+ CVehicleModelInfo* trainModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(train->m_modelIndex);
+ CMatrix trainMat = CMatrix(train->GetMatrix());
+
+ doorPos = trainModel->m_positions[m_vehEnterType];
+ doorPos.x -= 1.5f;
+ doorPos = Multiply3x3(trainMat, doorPos);
+ doorPos += train->GetPosition();
+*/
+ return 1;
+}
+
+void
+CPed::LineUpPedWithTrain(void)
+{
+ CVector lineUpPos;
+ CVehicleModelInfo* trainModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_pMyVehicle->m_modelIndex);
+ CVector enterOffset(1.5f, 0.0f, -0.2f);
+
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_fRotationCur = m_pMyVehicle->GetForward().Heading() - HALFPI;
+ m_fRotationDest = m_fRotationCur;
+
+ if (!bInVehicle) {
+ GetNearestTrainDoor(m_pMyVehicle, lineUpPos);
+ lineUpPos.z += 0.2f;
+ } else {
+ if (m_pMyVehicle->pPassengers[TRAIN_POS_LEFT_ENTRY] == this) {
+
+ lineUpPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterOffset;
+
+ } else if (m_pMyVehicle->pPassengers[TRAIN_POS_MID_ENTRY] == this) {
+
+ lineUpPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterOffset;
+
+ } else if (m_pMyVehicle->pPassengers[TRAIN_POS_RIGHT_ENTRY] == this) {
+
+ lineUpPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterOffset;
+ }
+ lineUpPos = Multiply3x3(m_pMyVehicle->GetMatrix(), lineUpPos);
+ lineUpPos += m_pMyVehicle->GetPosition();
+ }
+
+ if (m_pVehicleAnim) {
+ float percentageLeft = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength;
+ lineUpPos += (GetPosition() - lineUpPos) * percentageLeft;
+ }
+
+ GetPosition() = lineUpPos;
+// CVector pedPos = GetPosition();
+ GetMatrix().SetRotate(0.0f, 0.0f, m_fRotationCur);
+ GetPosition() += lineUpPos;
+}
+
+void
+CPed::EnterTrain(void)
+{
+ LineUpPedWithTrain();
+}
+
+void
+CPed::ExitTrain(void)
+{
+ LineUpPedWithTrain();
+}
+
+void
+CPed::ExitCar(void)
+{
+ if (!m_pVehicleAnim)
+ return;
+
+ AnimationId exitAnim = (AnimationId) m_pVehicleAnim->animId;
+ float animTime = m_pVehicleAnim->currentTime;
+
+ m_pMyVehicle->ProcessOpenDoor(m_vehEnterType, exitAnim, animTime);
+
+ if (m_pSeekTarget) {
+ // Car is upside down
+ if (m_pMyVehicle->GetUp().z > -0.8f) {
+ if (exitAnim != ANIM_CAR_CLOSE_RHS && exitAnim != ANIM_CAR_CLOSE_LHS && animTime <= 0.3f)
+ LineUpPedWithCar((m_pMyVehicle->m_modelIndex == MI_DODO ? LINE_UP_TO_CAR_END : LINE_UP_TO_CAR_START));
+ else
+ LineUpPedWithCar(LINE_UP_TO_CAR_END);
+ } else {
+ LineUpPedWithCar(LINE_UP_TO_CAR_END);
+ }
+ }
+
+ // If there is someone in front of the door, make him fall while we exit.
+ if (m_nPedState == PED_EXIT_CAR) {
+ CPed *foundPed = nil;
+ for (int i = 0; i < m_numNearPeds; i++) {
+ if ((m_nearPeds[i]->GetPosition() - GetPosition()).MagnitudeSqr2D() < 0.04f) {
+ foundPed = m_nearPeds[i];
+ break;
+ }
+ }
+ if (foundPed && animTime > 0.4f && foundPed->IsPedInControl())
+ foundPed->SetFall(1000, ANIM_KO_SKID_FRONT, 1);
+ }
+}
+
+void
+CPed::Fall(void)
+{
+ if (m_getUpTimer != -1 && CTimer::GetTimeInMilliseconds() > m_getUpTimer)
+ ClearFall();
+}
+
+void
+CPed::Fight(void)
+{
+ CAnimBlendAssociation *currentAssoc = nil, *animAssoc;
+ bool hasShoppingBags, punchOnly, canKick, canKneeHead, canRoundhouse;
+ float angleToFace, nextAngle;
+ bool goForward = false;
+ int nextFightMove;
+
+ switch (m_lastFightMove) {
+ case FIGHTMOVE_NULL:
+ return;
+ case FIGHTMOVE_IDLE2NORM:
+ m_lastFightMove = FIGHTMOVE_NULL;
+ RestorePreviousState();
+ break;
+ case FIGHTMOVE_IDLE:
+ // currentAssoc = nil;
+ break;
+ default:
+ currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_lastFightMove].animId);
+ break;
+ }
+
+ if (!bIsAttacking && IsPlayer()) {
+ if (currentAssoc) {
+ currentAssoc->blendDelta = -1000.0f;
+ currentAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ currentAssoc->flags &= ~ASSOC_RUNNING;
+ }
+ if (m_takeAStepAfterAttack)
+ EndFight(ENDFIGHT_WITH_A_STEP);
+ else
+ EndFight(ENDFIGHT_FAST);
+
+ } else if (currentAssoc && m_fightUnk2 > -2) {
+ float animTime = currentAssoc->currentTime;
+ FightMove &curMove = tFightMoves[m_lastFightMove];
+ if (curMove.hitLevel != 0 && animTime > curMove.startFireTime && animTime <= curMove.endFireTime && m_fightUnk2 >= 0) {
+
+ CVector touchingNodePos(0.0f, 0.0f, 0.0f);
+ RwFrame *touchingFrame = nil;
+
+ switch (m_lastFightMove) {
+ case FIGHTMOVE_STDPUNCH:
+ case FIGHTMOVE_PUNCHHOOK:
+ case FIGHTMOVE_BODYBLOW:
+ touchingFrame = GetNodeFrame(PED_HANDR);
+ break;
+ case FIGHTMOVE_IDLE:
+ case FIGHTMOVE_SHUFFLE_F:
+ break;
+ case FIGHTMOVE_KNEE:
+ touchingFrame = GetNodeFrame(PED_LOWERLEGR);
+ break;
+ case FIGHTMOVE_HEADBUTT:
+ touchingFrame = GetNodeFrame(PED_HEAD);
+ break;
+ case FIGHTMOVE_PUNCHJAB:
+ touchingFrame = GetNodeFrame(PED_HANDL);
+ break;
+ case FIGHTMOVE_KICK:
+ case FIGHTMOVE_LONGKICK:
+ case FIGHTMOVE_ROUNDHOUSE:
+ case FIGHTMOVE_GROUNDKICK:
+ touchingFrame = GetNodeFrame(PED_FOOTR);
+ break;
+ }
+ while (touchingFrame) {
+ RwV3dTransformPoints(touchingNodePos, touchingNodePos, 1, RwFrameGetMatrix(touchingFrame));
+ touchingFrame = RwFrameGetParent(touchingFrame);
+ }
+
+ if (m_lastFightMove == FIGHTMOVE_PUNCHJAB) {
+ touchingNodePos += 0.1f * GetForward();
+ } else if (m_lastFightMove == FIGHTMOVE_PUNCHHOOK) {
+ touchingNodePos += 0.22f * GetForward();
+ }
+ FightStrike(touchingNodePos);
+ m_fightButtonPressure = 0;
+ return;
+ }
+
+ if (curMove.hitLevel != 0) {
+ if (animTime > curMove.endFireTime) {
+ if (IsPlayer())
+ currentAssoc->speed = 1.0f;
+ else
+ currentAssoc->speed = 0.8f;
+ }
+
+ if (IsPlayer() && !nPlayerInComboMove) {
+ if (curMove.comboFollowOnTime > 0.0f && m_fightButtonPressure != 0 && animTime > curMove.comboFollowOnTime) {
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curMove.animId, 8.0f);
+ animAssoc->SetFinishCallback(FinishFightMoveCB, this);
+ animAssoc->SetCurrentTime(0.1f * animAssoc->hierarchy->totalLength);
+ m_fightButtonPressure = 0;
+ nPlayerInComboMove = 1;
+ }
+ }
+ } else {
+ if (curMove.startFireTime > 0.0f && m_lastFightMove != FIGHTMOVE_SHUFFLE_F && animTime > curMove.startFireTime) {
+ if (IsPlayer())
+ currentAssoc->speed = 1.3f;
+ else
+ currentAssoc->speed = 0.8f;
+ }
+ }
+ } else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) {
+ EndFight(ENDFIGHT_FAST);
+
+ } else if (m_fightButtonPressure != 0) {
+ bool canAffectMultiplePeople = true;
+ nextAngle = m_fRotationCur;
+ bool kickGround = false;
+ float angleForGroundKick = 0.0f;
+ CPed *pedOnGround = nil;
+
+ Say(SOUND_PED_ATTACK);
+
+ if (IsPlayer()) {
+ canRoundhouse = false;
+ punchOnly = false;
+ canKick = true;
+ nextFightMove = (m_fightButtonPressure > 190 ? FIGHTMOVE_HEADBUTT : FIGHTMOVE_KNEE);
+ hasShoppingBags = false;
+ canKneeHead = true;
+ nPlayerInComboMove = 0;
+ } else {
+ nextFightMove = (m_fightButtonPressure > 120 ? FIGHTMOVE_HEADBUTT : FIGHTMOVE_KNEE);
+ uint16 pedFeatures = m_pedStats->m_flags;
+ punchOnly = pedFeatures & STAT_PUNCH_ONLY;
+ canRoundhouse = pedFeatures & STAT_CAN_ROUNDHOUSE;
+ canKneeHead = pedFeatures & STAT_CAN_KNEE_HEAD;
+ canKick = pedFeatures & STAT_CAN_KICK;
+ hasShoppingBags = pedFeatures & STAT_SHOPPING_BAGS;
+ }
+
+ // Attack isn't scripted, find the victim
+ if (IsPlayer() || !m_pedInObjective) {
+
+ for (int i = 0; i < m_numNearPeds; i++) {
+
+ CPed *nearPed = m_nearPeds[i];
+ float nearPedDist = (nearPed->GetPosition() - GetPosition()).Magnitude();
+ if (nearPedDist < 3.0f) {
+ float angleToFace = CGeneral::GetRadianAngleBetweenPoints(
+ nearPed->GetPosition().x, nearPed->GetPosition().y,
+ GetPosition().x, GetPosition().y);
+
+ nextAngle = CGeneral::LimitRadianAngle(angleToFace);
+ m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur);
+
+ float neededTurn = Abs(nextAngle - m_fRotationCur);
+ if (neededTurn > PI)
+ neededTurn = TWOPI - neededTurn;
+
+ PedState nearPedState = nearPed->m_nPedState;
+ if (nearPedState != PED_FALL && nearPedState != PED_GETUP && nearPedState != PED_DIE && nearPedState != PED_DEAD) {
+
+ if (nearPedDist < 0.8f && neededTurn < DEGTORAD(75.0f) && canKneeHead) {
+ canAffectMultiplePeople = false;
+ } else if (nearPedDist >= 1.3f || neededTurn >= DEGTORAD(55.0f) || hasShoppingBags) {
+
+ if (nearPedDist < 1.7f
+ && neededTurn < DEGTORAD(35.0f)
+ && (canKick || hasShoppingBags)) {
+
+ nextFightMove = FIGHTMOVE_KICK;
+ if (hasShoppingBags) {
+ nextFightMove = FIGHTMOVE_ROUNDHOUSE;
+ } else if (canRoundhouse && CGeneral::GetRandomNumber() & 1) {
+ nextFightMove = FIGHTMOVE_ROUNDHOUSE;
+ }
+ canAffectMultiplePeople = false;
+ } else if (nearPedDist < 2.0f && neededTurn < DEGTORAD(30.0f) && canKick) {
+ canAffectMultiplePeople = false;
+ nextFightMove = FIGHTMOVE_LONGKICK;
+ } else if (neededTurn < DEGTORAD(30.0f)) {
+ goForward = true;
+ }
+ } else {
+ nextFightMove += 2; // Makes it 6 or 7
+ if (punchOnly)
+ nextFightMove = FIGHTMOVE_PUNCHJAB;
+
+ canAffectMultiplePeople = false;
+ }
+ } else if (!CGame::nastyGame
+ || nearPedDist >= 1.3f
+ || neededTurn >= DEGTORAD(55.0f)
+ || punchOnly) {
+
+ if (nearPedDist > 0.8f
+ && nearPedDist < 3.0f
+ && neededTurn < DEGTORAD(30.0f)) {
+ goForward = true;
+ }
+
+ } else if (nearPedState != PED_DEAD || pedOnGround) {
+ if (!nearPed->IsPedHeadAbovePos(-0.3f)) {
+ canAffectMultiplePeople = false;
+ nextFightMove = FIGHTMOVE_GROUNDKICK;
+ }
+
+ } else {
+ pedOnGround = nearPed;
+ kickGround = true;
+ angleForGroundKick = nextAngle;
+ }
+ }
+
+ if (!canAffectMultiplePeople) {
+ m_fRotationDest = nextAngle;
+ if (IsPlayer()) {
+ m_fRotationCur = m_fRotationDest;
+ m_lookTimer = 0;
+ SetLookFlag(nearPed, 1);
+ SetLookTimer(1500);
+ }
+ break;
+ }
+ }
+ } else {
+ // Because we're in a scripted fight with some particular ped.
+ canAffectMultiplePeople = false;
+
+ float fightingPedDist = (m_pedInObjective->GetPosition() - GetPosition()).Magnitude();
+ if (hasShoppingBags) {
+ if (fightingPedDist >= 1.7f)
+ nextFightMove = FIGHTMOVE_SHUFFLE_F;
+ else
+ nextFightMove = FIGHTMOVE_ROUNDHOUSE;
+
+ } else if (punchOnly) {
+ if (fightingPedDist >= 1.3f)
+ nextFightMove = FIGHTMOVE_SHUFFLE_F;
+ else
+ nextFightMove = FIGHTMOVE_PUNCHJAB;
+
+ } else if (fightingPedDist >= 3.0f) {
+ nextFightMove = FIGHTMOVE_STDPUNCH;
+
+ } else {
+ angleToFace = CGeneral::GetRadianAngleBetweenPoints(
+ m_pedInObjective->GetPosition().x,
+ m_pedInObjective->GetPosition().y,
+ GetPosition().x,
+ GetPosition().y);
+
+ nextAngle = CGeneral::LimitRadianAngle(angleToFace);
+ m_fRotationDest = nextAngle;
+ m_fRotationCur = m_fRotationDest;
+ PedState fightingPedState = m_pedInObjective->m_nPedState;
+ if (fightingPedState != PED_FALL && fightingPedState != PED_GETUP && fightingPedState != PED_DIE && fightingPedState != PED_DEAD) {
+
+ if (fightingPedDist >= 0.8f || !canKneeHead) {
+
+ if (fightingPedDist >= 1.3f) {
+
+ if (fightingPedDist < 1.7f && canKick) {
+ nextFightMove = FIGHTMOVE_KICK;
+ if (canRoundhouse && CGeneral::GetRandomNumber() & 1)
+ nextFightMove = FIGHTMOVE_ROUNDHOUSE;
+
+ } else if (fightingPedDist < 2.0f && canKick) {
+ nextFightMove += 5; // Makes it 9 or 10
+
+ } else {
+ nextFightMove = FIGHTMOVE_SHUFFLE_F;
+
+ }
+ } else {
+ nextFightMove += 2; // Makes it 6 or 7
+ }
+ }
+ } else if (!CGame::nastyGame
+ || fightingPedDist >= 1.3f
+ || m_pedInObjective->IsPlayer()
+ || m_pedInObjective->m_nPedState != PED_DEAD && m_pedInObjective->IsPedHeadAbovePos(-0.3f)) {
+ nextFightMove = FIGHTMOVE_IDLE;
+ } else {
+ nextFightMove = FIGHTMOVE_GROUNDKICK;
+ }
+ }
+ }
+
+ if (canAffectMultiplePeople) {
+ if (kickGround && IsPlayer()) {
+ m_fRotationDest = angleForGroundKick;
+ nextFightMove = FIGHTMOVE_GROUNDKICK;
+ m_fRotationCur = m_fRotationDest;
+ m_lookTimer = 0;
+ SetLookFlag(pedOnGround, 1);
+ SetLookTimer(1500);
+ } else if (goForward) {
+ nextFightMove = FIGHTMOVE_SHUFFLE_F;
+ } else {
+ nextFightMove = FIGHTMOVE_STDPUNCH;
+ }
+ }
+
+ if (nextFightMove != FIGHTMOVE_IDLE) {
+ m_lastFightMove = (PedFightMoves) nextFightMove;
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_lastFightMove].animId, 4.0f);
+
+ animAssoc->SetFinishCallback(FinishFightMoveCB, this);
+ if (m_fightUnk2 == -2 && animAssoc->currentTime != 0.0f) {
+ animAssoc->SetCurrentTime(0.0f);
+ animAssoc->SetRun();
+ }
+ m_fightButtonPressure = 0;
+ }
+ m_fightUnk2 = 0;
+ } else if (m_takeAStepAfterAttack && m_lastFightMove != FIGHTMOVE_SHUFFLE_F
+#ifndef FIX_BUGS
+ && CheckForPedsOnGroundToAttack(this, nil) == 4) {
+#else
+ && CheckForPedsOnGroundToAttack(this, nil) == PED_IN_FRONT_OF_ATTACKER) {
+#endif
+ m_lastFightMove = FIGHTMOVE_SHUFFLE_F;
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_lastFightMove].animId);
+
+ if (animAssoc) {
+ animAssoc->SetCurrentTime(0.0f);
+ animAssoc->blendDelta = 4.0f;
+ animAssoc->SetRun();
+ } else {
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_lastFightMove].animId, 32.0f);
+ }
+ animAssoc->SetFinishCallback(FinishFightMoveCB, this);
+ m_fightUnk2 = 0;
+ m_fightButtonPressure = 0;
+ m_takeAStepAfterAttack = false;
+
+ } else if (m_takeAStepAfterAttack) {
+ EndFight(ENDFIGHT_FAST);
+
+ } else if (m_lastFightMove == FIGHTMOVE_IDLE) {
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ EndFight(ENDFIGHT_NORMAL);
+ }
+
+ } else {
+ m_lastFightMove = FIGHTMOVE_IDLE;
+ if (IsPlayer())
+ m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500;
+ else
+ m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000;
+ }
+}
+
+// Some helper function which doesn't exist in og game.
+inline void
+SelectClosestNodeForSeek(CPed *ped, CPathNode *node, CVector2D closeDist, CVector2D farDist, CPathNode *closeNode, CPathNode *closeNode2, int runCount = 3)
+{
+ for (int i = 0; i < node->numLinks; i++) {
+
+ CPathNode *testNode = &ThePaths.m_pathNodes[ThePaths.m_connections[i + node->firstLink]];
+
+ if (testNode && testNode != closeNode && testNode != closeNode2) {
+ CVector2D posDiff(ped->m_vecSeekPos - testNode->pos);
+ float dist = posDiff.MagnitudeSqr();
+
+ if (farDist.MagnitudeSqr() > dist) {
+
+ if (closeDist.MagnitudeSqr() <= dist) {
+ ped->m_pNextPathNode = closeNode;
+ closeDist = posDiff;
+ } else {
+ ped->m_pNextPathNode = (closeNode2 ? closeNode2 : testNode);
+ farDist = posDiff;
+ }
+ }
+
+ if (--runCount > 0)
+ SelectClosestNodeForSeek(ped, testNode, closeDist, farDist, closeNode, (closeNode2 ? closeNode2 : testNode), runCount);
+ }
+ }
+}
+
+bool
+CPed::FindBestCoordsFromNodes(CVector unused, CVector *bestCoords)
+{
+ if (m_pNextPathNode || !bIsFleeing)
+ return false;
+
+ CVector ourPos = GetPosition();
+
+ int closestNodeId = ThePaths.FindNodeClosestToCoors(GetPosition(), 1, 999999.9f, false, false);
+
+ CVector seekObjPos = m_vecSeekPos;
+ seekObjPos.z += 1.0f;
+
+ if (CWorld::GetIsLineOfSightClear(ourPos, seekObjPos, true, false, false, true, false, false, false))
+ return false;
+
+ m_pNextPathNode = nil;
+
+ CVector2D seekPosDist (m_vecSeekPos - ourPos);
+
+ CPathNode *closestNode = &ThePaths.m_pathNodes[closestNodeId];
+ CVector2D closeDist(m_vecSeekPos - closestNode->pos);
+
+ SelectClosestNodeForSeek(this, closestNode, closeDist, seekPosDist, closestNode, nil);
+
+ // Above function decided that going to the next node is more logical than seeking the object.
+ if (m_pNextPathNode) {
+
+ CVector pathToNextNode = m_pNextPathNode->pos - ourPos;
+ if (pathToNextNode.MagnitudeSqr2D() < seekPosDist.MagnitudeSqr()) {
+ *bestCoords = m_pNextPathNode->pos;
+ return true;
+ }
+ m_pNextPathNode = nil;
+ }
+
+ return false;
+}
+
+void
+CPed::FinishDieAnimCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed *ped = (CPed*)arg;
+
+ if (ped->bIsPedDieAnimPlaying)
+ ped->bIsPedDieAnimPlaying = false;
+}
+
+void
+CPed::FinishFightMoveCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed *ped = (CPed*)arg;
+
+ if (tFightMoves[ped->m_lastFightMove].animId == animAssoc->animId) {
+ ped->m_fightUnk2 = -2;
+ animAssoc->blendDelta = -1000.0f;
+ }
+}
+
+void
+CPed::FinishHitHeadCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed *ped = (CPed*)arg;
+
+ if (animAssoc) {
+ animAssoc->blendDelta = -4.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+
+ if (ped->m_nPedState == PED_JUMP)
+ ped->RestorePreviousState();
+
+ ped->bIsLanding = false;
+}
+
+void
+CPed::FinishJumpCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed* ped = (CPed*)arg;
+
+ ped->m_ped_flagG4 = true;
+ ped->bIsLanding = false;
+
+ animAssoc->blendDelta = -1000.0f;
+}
+
+void
+CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed *ped = (CPed*)arg;
+
+ if (ped->m_nPedState != PED_JUMP)
+ return;
+
+ CVector forward(0.15f * ped->GetForward() + ped->GetPosition());
+ forward.z += CModelInfo::GetModelInfo(ped->m_modelIndex)->GetColModel()->spheres->center.z + 0.25f;
+
+ CEntity *foundEnt = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false);
+ if (!foundEnt) {
+ // Forward of forward
+ forward += 0.15f * ped->GetForward();
+ forward.z += 0.15f;
+ foundEnt = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false);
+ }
+
+ if (foundEnt) {
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ CAnimBlendAssociation *handsCoverAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_HANDSCOWER, 8.0f);
+ handsCoverAssoc->flags &= ~ASSOC_FADEOUTWHENDONE;
+ handsCoverAssoc->SetFinishCallback(FinishHitHeadCB, ped);
+ ped->bIsLanding = true;
+ return;
+ }
+
+ float velocityFromAnim = 0.1f;
+ CAnimBlendAssociation *sprintAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_SPRINT);
+
+ if (sprintAssoc) {
+ velocityFromAnim = 0.05f * sprintAssoc->blendAmount + 0.17f;
+ } else {
+ CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_RUN);
+ if (runAssoc) {
+ velocityFromAnim = 0.07f * runAssoc->blendAmount + 0.1f;
+ }
+ }
+
+ if (ped->IsPlayer())
+ ped->ApplyMoveForce(0.0f, 0.0f, 8.5f);
+ else
+ ped->ApplyMoveForce(0.0f, 0.0f, 4.5f);
+
+ if (sq(velocityFromAnim) > ped->m_vecMoveSpeed.MagnitudeSqr2D()) {
+
+ if (TheCamera.Cams[0].Using3rdPersonMouseCam()) {
+ float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur);
+ ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(fpsAngle);
+ ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(fpsAngle);
+ } else {
+ ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(ped->m_fRotationCur);
+ ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(ped->m_fRotationCur);
+ }
+ }
+
+ ped->bIsStanding = false;
+ ped->bIsInTheAir = true;
+ animAssoc->blendDelta = -1000.0f;
+ CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_JUMP_GLIDE);
+
+ if (ped->bDoBloodyFootprints) {
+ CVector bloodPos(0.0f, 0.0f, 0.0f);
+ for (RwFrame *i = ped->GetNodeFrame(PED_FOOTL); i; i = RwFrameGetParent(i))
+ RwV3dTransformPoints(bloodPos, bloodPos, 1, RwFrameGetMatrix(i));
+
+ bloodPos.z -= 0.1f;
+ bloodPos += 0.2f * ped->GetForward();
+
+ CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos,
+ 0.26f * ped->GetForward().x,
+ 0.26f * ped->GetForward().y,
+ 0.14f * ped->GetRight().x,
+ 0.14f * ped->GetRight().y,
+ 255, 255, 0, 0, 4.0f, 3000, 1.0f);
+
+ bloodPos = CVector(0.0f, 0.0f, 0.0f);
+ for (RwFrame* j = ped->GetNodeFrame(PED_FOOTR); j; j = RwFrameGetParent(j))
+ RwV3dTransformPoints(bloodPos, bloodPos, 1, RwFrameGetMatrix(j));
+
+ bloodPos.z -= 0.1f;
+ bloodPos += 0.2f * ped->GetForward();
+ CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos,
+ 0.26f * ped->GetForward().x,
+ 0.26f * ped->GetForward().y,
+ 0.14f * ped->GetRight().x,
+ 0.14f * ped->GetRight().y,
+ 255, 255, 0, 0, 4.0f, 3000, 1.0f);
+
+ if (ped->m_bloodyFootprintCount <= 40) {
+ ped->m_bloodyFootprintCount = 0;
+ ped->bDoBloodyFootprints = false;
+ } else {
+ ped->m_bloodyFootprintCount -= 40;
+ }
+ }
+}
+
+void
+CPed::FinishedWaitCB(CAnimBlendAssociation *animAssoc, void *arg)
+{
+ CPed* ped = (CPed*)arg;
+
+ ped->m_nWaitTimer = 0;
+ ped->RestoreHeadingRate();
+ ped->Wait();
+}
+
+void
+CPed::Wait(void)
+{
+ AnimationId mustHaveAnim = NUM_ANIMS;
+ CAnimBlendAssociation *animAssoc;
+ CPed *pedWeLook;
+
+ if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) {
+ m_nWaitState = WAITSTATE_FALSE;
+ RestoreHeadingRate();
+ return;
+ }
+
+ switch (m_nWaitState) {
+
+ case WAITSTATE_TRAFFIC_LIGHTS:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ if (CTrafficLights::LightForPeds() == PED_LIGHTS_WALK) {
+ m_nWaitState = WAITSTATE_FALSE;
+ SetMoveState(PEDMOVE_WALK);
+ }
+ }
+ break;
+
+ case WAITSTATE_CROSS_ROAD:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ if (CGeneral::GetRandomNumber() & 1 || !m_nWaitTimer)
+ m_nWaitState = WAITSTATE_FALSE;
+ else
+ SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, nil);
+
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB);
+ if (animAssoc) {
+ animAssoc->blendDelta = -8.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+ }
+ break;
+
+ case WAITSTATE_LOOK_PED:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ m_nWaitState = WAITSTATE_FALSE;
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS);
+ if (animAssoc) {
+ animAssoc->blendDelta = -8.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+ }
+ break;
+
+ case WAITSTATE_DOUBLEBACK:
+ if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) {
+ uint32 timeLeft = m_nWaitTimer - CTimer::GetTimeInMilliseconds();
+ if (timeLeft < 2500 && timeLeft > 2000) {
+ m_nWaitTimer -= 500;
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f);
+ }
+ } else {
+ m_nWaitState = WAITSTATE_FALSE;
+ SetMoveState(PEDMOVE_WALK);
+ }
+ break;
+
+ case WAITSTATE_HITWALL:
+ if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) {
+ if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) {
+ m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500;
+ }
+ } else {
+ m_nWaitState = WAITSTATE_FALSE;
+ }
+ break;
+
+ case WAITSTATE_TURN180:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ m_nWaitState = WAITSTATE_FALSE;
+ SetMoveState(PEDMOVE_WALK);
+ m_fRotationCur = m_fRotationCur + PI;
+ if (m_nPedState == PED_INVESTIGATE)
+ ClearInvestigateEvent();
+ }
+
+ if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) {
+ m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500;
+ }
+ break;
+
+ case WAITSTATE_SURPRISE:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_HIT_WALL)) {
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f);
+ animAssoc->SetFinishCallback(FinishedWaitCB, this);
+ m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000;
+ } else {
+ m_nWaitState = WAITSTATE_FALSE;
+ }
+ }
+ break;
+
+ case WAITSTATE_STUCK:
+ if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer)
+ break;
+
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_TIRED);
+
+ if (!animAssoc)
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_TURN_180);
+ if (!animAssoc)
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH);
+ if (!animAssoc)
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS);
+
+ if (animAssoc) {
+ if (animAssoc->IsPartial()) {
+ animAssoc->blendDelta = -8.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ } else {
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 4.0f);
+ }
+
+ if (animAssoc->animId == ANIM_TURN_180) {
+ m_fRotationCur = CGeneral::LimitRadianAngle(PI + m_fRotationCur);
+ m_nWaitState = WAITSTATE_FALSE;
+ SetMoveState(PEDMOVE_WALK);
+ m_nStoredMoveState = PEDMOVE_NONE;
+ m_panicCounter = 0;
+ return;
+ }
+ }
+
+ AnimationId animToRun;
+
+ switch (CGeneral::GetRandomNumber() & 3) {
+ case 0:
+ animToRun = ANIM_ROAD_CROSS;
+ break;
+ case 1:
+ animToRun = ANIM_IDLE_TIRED;
+ break;
+ case 2:
+ animToRun = ANIM_XPRESS_SCRATCH;
+ break;
+ case 3:
+ animToRun = ANIM_TURN_180;
+ break;
+ default:
+ break;
+ }
+
+ animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToRun, 4.0f);
+
+ if (animToRun == ANIM_TURN_180)
+ animAssoc->SetFinishCallback(FinishedWaitCB, this);
+
+ m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(1500, 5000);
+ break;
+
+ case WAITSTATE_LOOK_ABOUT:
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ m_nWaitState = WAITSTATE_FALSE;
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB);
+ if (animAssoc) {
+ animAssoc->blendDelta = -8.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+ }
+ break;
+
+ case WAITSTATE_PLAYANIM_HANDSUP:
+ mustHaveAnim = ANIM_HANDSUP;
+
+ case WAITSTATE_PLAYANIM_HANDSCOWER:
+ if (mustHaveAnim == NUM_ANIMS)
+ mustHaveAnim = ANIM_HANDSCOWER;
+
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim);
+ pedWeLook = (CPed*) m_pLookTarget;
+
+ if ((!m_pLookTarget || !m_pLookTarget->IsPed() || pedWeLook->m_pPointGunAt)
+ && m_nPedState != PED_FLEE_ENTITY
+ && m_nPedState != PED_ATTACK
+ && CTimer::GetTimeInMilliseconds() <= m_nWaitTimer
+ && animAssoc) {
+
+ TurnBody();
+ } else {
+ m_nWaitState = WAITSTATE_FALSE;
+ m_nWaitTimer = 0;
+ if (m_pLookTarget && m_pLookTarget->IsPed()) {
+
+ if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_ATTACK) {
+
+ if (m_pedStats->m_fear <= 100 - pedWeLook->m_pedStats->m_temper) {
+
+ if (GetWeapon()->IsTypeMelee()) {
+
+ SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget);
+ if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) {
+
+ bIsFleeing = true;
+ m_pNextPathNode = nil;
+ }
+ if (m_nMoveState != PEDMOVE_RUN)
+ SetMoveState(PEDMOVE_WALK);
+
+ if (m_nPedType != PEDTYPE_COP) {
+ ProcessObjective();
+ SetMoveState(PEDMOVE_WALK);
+ }
+ } else {
+ SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_pLookTarget);
+ SetObjectiveTimer(20000);
+ }
+ } else {
+ SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget);
+ if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS)
+ {
+ bIsFleeing = true;
+ m_pNextPathNode = nil;
+ }
+ SetMoveState(PEDMOVE_RUN);
+ Say(SOUND_PED_FLEE_RUN);
+ }
+ }
+ }
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim);
+ if (animAssoc) {
+ animAssoc->blendDelta = -4.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+ }
+ break;
+ case WAITSTATE_PLAYANIM_COWER:
+ mustHaveAnim = ANIM_HANDSCOWER;
+
+ case WAITSTATE_PLAYANIM_DUCK:
+ if (mustHaveAnim == NUM_ANIMS)
+ mustHaveAnim = ANIM_DUCK_DOWN;
+
+ case WAITSTATE_PLAYANIM_TAXI:
+ if (mustHaveAnim == NUM_ANIMS)
+ mustHaveAnim = ANIM_IDLE_TAXI;
+
+ case WAITSTATE_PLAYANIM_CHAT:
+ if (mustHaveAnim == NUM_ANIMS)
+ mustHaveAnim = ANIM_IDLE_CHAT;
+
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim);
+ if (animAssoc) {
+ animAssoc->blendDelta = -4.0f;
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ }
+ m_nWaitState = WAITSTATE_FALSE;
+ }
+ break;
+
+ case WAITSTATE_FINISH_FLEE:
+ animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_TIRED);
+ if (animAssoc) {
+ if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) {
+ animAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 4.0f);
+ int timer = 2000;
+ m_nWaitState = WAITSTATE_FALSE;
+ SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &timer);
+ }
+ } else {
+ m_nWaitState = WAITSTATE_FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(!m_nWaitState)
+ RestoreHeadingRate();
+}
+
+bool
+CPed::Seek(void)
+{
+ float distanceToCountItDone = m_distanceToCountSeekDone;
+ eMoveState nextMove = PEDMOVE_NONE;
+
+ if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) {
+
+ if (m_nPedState != PED_EXIT_TRAIN && m_nPedState != PED_ENTER_TRAIN && m_nPedState != PED_SEEK_IN_BOAT &&
+ m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_SOLICIT && !bDuckAndCover) {
+
+ if ((!m_pedInObjective || !m_pedInObjective->bInVehicle)
+ && !((CTimer::GetFrameCounter() + (m_randomSeed % 256) + 17) & 7)) {
+
+ CEntity *foundEnt = CWorld::TestSphereAgainstWorld(m_vecSeekPos, 0.4f, nil,
+ false, true, false, false, false, false);
+
+ if (foundEnt) {
+ if (!foundEnt->IsVehicle() || ((CVehicle*)foundEnt)->m_vehType == VEHICLE_TYPE_CAR) {
+ distanceToCountItDone = 2.5f;
+ } else {
+ CVehicleModelInfo *vehModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(foundEnt->m_modelIndex);
+ float yLength = vehModel->GetColModel()->boundingBox.max.y
+ - vehModel->GetColModel()->boundingBox.min.y;
+ distanceToCountItDone = yLength * 0.55f;
+ }
+ }
+ }
+ }
+ }
+
+ if (!m_pSeekTarget && m_nPedState == PED_SEEK_ENTITY)
+ ClearSeek();
+
+ float seekPosDist = (m_vecSeekPos - GetPosition()).Magnitude2D();
+ if (seekPosDist < 2.0f || m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT) {
+
+ if (m_objective == OBJECTIVE_FOLLOW_PED_IN_FORMATION) {
+
+ if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL)
+ nextMove = m_pedInObjective->m_nMoveState;
+ } else
+ nextMove = PEDMOVE_WALK;
+
+ } else if (m_objective != OBJECTIVE_FOLLOW_PED_IN_FORMATION) {
+
+ if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || m_objective == OBJECTIVE_RUN_TO_AREA || bIsRunning)
+ nextMove = PEDMOVE_RUN;
+ else
+ nextMove = PEDMOVE_WALK;
+
+ } else if (seekPosDist <= 2.0f) {
+
+ if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL)
+ nextMove = m_pedInObjective->m_nMoveState;
+
+ } else {
+ nextMove = PEDMOVE_RUN;
+ }
+
+ if (m_nPedState == PED_SEEK_ENTITY) {
+ if (m_pSeekTarget->IsPed()) {
+ if (((CPed*)m_pSeekTarget)->bInVehicle)
+ distanceToCountItDone += 2.0f;
+ }
+ }
+
+ if (seekPosDist >= distanceToCountItDone) {
+ if (bIsRunning)
+ nextMove = PEDMOVE_RUN;
+
+ if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) {
+
+ if (m_actionX != 0.0f && m_actionY != 0.0f) {
+
+ m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(
+ m_actionX, m_actionY,
+ GetPosition().x, GetPosition().y);
+
+ float neededTurn = Abs(m_fRotationDest - m_fRotationCur);
+
+ if (neededTurn > PI)
+ neededTurn = TWOPI - neededTurn;
+
+ if (neededTurn > HALFPI) {
+ if (seekPosDist >= 1.0f) {
+ if (seekPosDist < 2.0f) {
+ if (bIsRunning)
+ nextMove = PEDMOVE_RUN;
+ else
+ nextMove = PEDMOVE_WALK;
+ }
+ } else {
+ nextMove = PEDMOVE_STILL;
+ }
+ }
+
+ CVector2D moveDist(GetPosition().x - m_actionX, GetPosition().y - m_actionY);
+ if (moveDist.Magnitude() < 0.5f) {
+ m_nPedStateTimer = 0;
+ m_actionX = 0;
+ m_actionY = 0;
+ }
+ }
+ } else {
+ m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(
+ m_vecSeekPos.x, m_vecSeekPos.y,
+ GetPosition().x, GetPosition().y);
+
+ float neededTurn = Abs(m_fRotationDest - m_fRotationCur);
+
+ if (neededTurn > PI)
+ neededTurn = TWOPI - neededTurn;
+
+ if (neededTurn > HALFPI) {
+ if (seekPosDist >= 1.0 && neededTurn <= DEGTORAD(135.0f)) {
+ if (seekPosDist < 2.0f)
+ nextMove = PEDMOVE_WALK;
+ } else {
+ nextMove = PEDMOVE_STILL;
+ }
+ }
+ }
+
+ if (((m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY) && m_nMoveState < nextMove)
+ || (m_nPedState != PED_FLEE_POS && m_nPedState != PED_FLEE_ENTITY && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT && m_nWaitState == WAITSTATE_FALSE)) {
+
+ SetMoveState(nextMove);
+ }
+
+ SetMoveAnim();
+ return false;
+ }
+
+ if ((m_objective != OBJECTIVE_FOLLOW_PED_IN_FORMATION || m_pedInObjective->m_nMoveState == PEDMOVE_STILL) && m_nMoveState != PEDMOVE_STILL) {
+ m_nPedStateTimer = 0;
+ m_actionX = 0;
+ m_actionY = 0;
+ }
+
+ if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_GOTO_AREA_ANY_MEANS) {
+ if (m_pNextPathNode)
+ m_pNextPathNode = nil;
+ else
+ bScriptObjectiveCompleted = true;
+
+ bIsFleeing = true;
+ }
+
+ if (SeekFollowingPath(nil))
+ m_nCurPathNode++;
+
+ return true;
+}
+
+bool
+CPed::SeekFollowingPath(CVector *unused)
+{
+ return m_nCurPathNode <= m_nPathNodes && m_nPathNodes;
+}
+
+void
+CPed::Flee(void)
+{
+ if (CTimer::GetTimeInMilliseconds() > m_fleeTimer && m_fleeTimer) {
+ bool mayFinishFleeing = true;
+ if (m_nPedState == PED_FLEE_ENTITY) {
+ if ((CVector2D(GetPosition()) - ms_vec2DFleePosition).MagnitudeSqr() < 900.0f)
+ mayFinishFleeing = false;
+ }
+
+ if (mayFinishFleeing) {
+ eMoveState moveState = m_nMoveState;
+ ClearFlee();
+
+ if (m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE || m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS)
+ RestorePreviousObjective();
+
+ if ((m_nPedState == PED_IDLE || m_nPedState == PED_WANDER_PATH) && CGeneral::GetRandomNumber() & 1) {
+ SetWaitState(moveState <= PEDMOVE_WALK ? WAITSTATE_CROSS_ROAD_LOOK : WAITSTATE_FINISH_FLEE, nil);
+ }
+ return;
+ }
+ m_fleeTimer = CTimer::GetTimeInMilliseconds() + 5000;
+ }
+
+ if (bIsFleeing) {
+ CPathNode *realLastNode = nil;
+ uint8 nextDirection = 0;
+ uint8 curDirectionShouldBe = 9; // means not defined yet
+
+ if (m_nPedStateTimer < CTimer::GetTimeInMilliseconds()
+ && m_collidingThingTimer < CTimer::GetTimeInMilliseconds()) {
+
+ if (m_pNextPathNode && CTimer::GetTimeInMilliseconds() > m_standardTimer) {
+
+ curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y);
+ if (m_nPathState < curDirectionShouldBe)
+ m_nPathState += 8;
+
+ int dirDiff = m_nPathState - curDirectionShouldBe;
+ if (dirDiff > 2 && dirDiff < 6) {
+ realLastNode = nil;
+ m_pLastPathNode = m_pNextPathNode;
+ m_pNextPathNode = 0;
+ }
+ }
+
+ if (m_pNextPathNode) {
+ m_vecSeekPos = m_pNextPathNode->pos;
+ if (m_nMoveState == PEDMOVE_RUN)
+ bIsRunning = true;
+
+ eMoveState moveState = m_nMoveState;
+ if (Seek()) {
+ realLastNode = m_pLastPathNode;
+ m_pLastPathNode = m_pNextPathNode;
+ m_pNextPathNode = nil;
+ }
+ bIsRunning = false;
+ SetMoveState(moveState);
+ }
+ }
+
+ if (!m_pNextPathNode) {
+ if (curDirectionShouldBe == 9) {
+ curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y);
+ }
+ ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode,
+ curDirectionShouldBe,
+ &nextDirection);
+
+ if (curDirectionShouldBe < nextDirection)
+ curDirectionShouldBe += 8;
+
+ if (m_pNextPathNode && m_pNextPathNode != realLastNode && m_pNextPathNode != m_pLastPathNode && curDirectionShouldBe - nextDirection != 4) {
+ m_nPathState = nextDirection;
+ m_standardTimer = CTimer::GetTimeInMilliseconds() + 2000;
+ } else {
+ bIsFleeing = false;
+ SetMoveState(PEDMOVE_RUN);
+ Flee();
+ }
+ }
+ return;
+ }
+
+ if ((m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ON_FIRE) && m_nPedStateTimer < CTimer::GetTimeInMilliseconds()) {
+
+ float angleToFleeFromPos = CGeneral::GetRadianAngleBetweenPoints(
+ GetPosition().x,
+ GetPosition().y,
+ ms_vec2DFleePosition.x,
+ ms_vec2DFleePosition.y);
+
+ m_fRotationDest = CGeneral::LimitRadianAngle(angleToFleeFromPos);
+
+ if (m_fRotationCur - PI > m_fRotationDest)
+ m_fRotationDest += TWOPI;
+ else if (PI + m_fRotationCur < m_fRotationDest)
+ m_fRotationDest -= TWOPI;
+ }
+
+ if (CTimer::GetTimeInMilliseconds() & 0x20) {
+ //CVector forwardPos = GetPosition();
+ CMatrix forwardMat(GetMatrix());
+ forwardMat.GetPosition() += Multiply3x3(forwardMat, CVector(0.0f, 4.0f, 0.0f));
+ CVector forwardPos = forwardMat.GetPosition();
+
+ CEntity *foundEnt;
+ CColPoint foundCol;
+ bool found = CWorld::ProcessVerticalLine(forwardPos, forwardMat.GetPosition().z - 100.0f, foundCol, foundEnt, 1, 0, 0, 0, 1, 0, 0);
+
+ if (!found || Abs(forwardPos.z - forwardMat.GetPosition().z) > 1.0f) {
+ m_fRotationDest += DEGTORAD(112.5f);
+ m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 2000;
+ }
+ }
+
+ if (CTimer::GetTimeInMilliseconds() >= m_collidingThingTimer)
+ return;
+
+ if (!m_collidingEntityWhileFleeing)
+ return;
+
+ double damagingThingPriorityMult = (double)(m_collidingThingTimer - CTimer::GetTimeInMilliseconds()) * 2.0 / 2500;
+
+ if (damagingThingPriorityMult <= 1.5) {
+
+ double angleToFleeEntity = CGeneral::GetRadianAngleBetweenPoints(
+ GetPosition().x,
+ GetPosition().y,
+ m_collidingEntityWhileFleeing->GetPosition().x,
+ m_collidingEntityWhileFleeing->GetPosition().y);
+ angleToFleeEntity = CGeneral::LimitRadianAngle(angleToFleeEntity);
+
+ // It includes projectiles, but is everything collides with us included?
+ double angleToFleeDamagingThing = CGeneral::GetRadianAngleBetweenPoints(
+ m_vecDamageNormal.x,
+ m_vecDamageNormal.y,
+ 0.0,
+ 0.0);
+ angleToFleeDamagingThing = CGeneral::LimitRadianAngle(angleToFleeDamagingThing);
+
+ if (angleToFleeEntity - PI > angleToFleeDamagingThing)
+ angleToFleeDamagingThing += TWOPI;
+ else if (PI + angleToFleeEntity < angleToFleeDamagingThing)
+ angleToFleeDamagingThing -= TWOPI;
+
+ if (damagingThingPriorityMult <= 1.0) {
+ // Range [0.0, 1.0]
+
+ double angleToFleeBoth = (angleToFleeDamagingThing + angleToFleeEntity) * 0.5;
+
+ if (m_fRotationDest - PI > angleToFleeBoth)
+ angleToFleeBoth += TWOPI;
+ else if (PI + m_fRotationDest < angleToFleeBoth)
+ angleToFleeBoth -= TWOPI;
+
+ m_fRotationDest = (1.0 - damagingThingPriorityMult) * m_fRotationDest + damagingThingPriorityMult * angleToFleeBoth;
+ } else {
+ // Range (1.0, 1.5]
+
+ double adjustedMult = (damagingThingPriorityMult - 1.0) * 2.0;
+ m_fRotationDest = angleToFleeEntity * (1.0 - adjustedMult) + adjustedMult * angleToFleeDamagingThing;
+ }
+ } else {
+ m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(
+ m_vecDamageNormal.x,
+ m_vecDamageNormal.y,
+ 0.0f,
+ 0.0f);
+ m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest);
+ }
+
+ m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur);
+
+ if (m_fRotationCur - PI > m_fRotationDest)
+ m_fRotationDest += TWOPI;
+ else if (PI + m_fRotationCur < m_fRotationDest)
+ m_fRotationDest -= TWOPI;
+
+}
+
+void
+CPed::FollowPath(void)
+{
+ m_vecSeekPos.x = m_stPathNodeStates[m_nCurPathNode].x;
+ m_vecSeekPos.y = m_stPathNodeStates[m_nCurPathNode].y;
+ m_vecSeekPos.z = GetPosition().z;
+
+ // Mysterious code
+/* int v4 = 0;
+ int maxNodeIndex = m_nPathNodes - 1;
+ if (maxNodeIndex > 0) {
+ if (maxNodeIndex > 8) {
+ while (v4 < maxNodeIndex - 8)
+ v4 += 8;
+ }
+
+ while (v4 < maxNodeIndex)
+ v4++;
+
+ }
+*/
+ if (Seek()) {
+ m_nCurPathNode++;
+ if (m_nCurPathNode == m_nPathNodes)
+ RestorePreviousState();
+ }
+}
+
+CVector
+CPed::GetFormationPosition(void)
+{
+ CPed *referencePed = m_pedInObjective;
+
+ if (referencePed->m_nPedState == PED_DEAD) {
+ CPed *referencePedOfReference = referencePed->m_pedInObjective;
+ if (!referencePedOfReference) {
+ m_pedInObjective = nil;
+ return GetPosition();
+ }
+ m_pedInObjective = referencePed = referencePedOfReference;
+ }
+
+ CVector formationOffset;
+ switch (m_pedFormation) {
+ case 1:
+ formationOffset = CVector(0.0f, -1.5f, 0.0f);
+ break;
+ case 2:
+ formationOffset = CVector(-1.5f, -1.5f, 0.0f);
+ break;
+ case 3:
+ formationOffset = CVector(1.5f, -1.5f, 0.0f);
+ break;
+ case 4:
+ formationOffset = CVector(-1.5f, 1.5f, 0.0f);
+ break;
+ case 5:
+ formationOffset = CVector(1.5f, 1.5f, 0.0f);
+ break;
+ case 6:
+ formationOffset = CVector(-1.5f, 0.0f, 0.0f);
+ break;
+ case 7:
+ formationOffset = CVector(1.5f, 0.0f, 0.0f);
+ break;
+ case 8:
+ formationOffset = CVector(0.0f, 1.5f, 0.0f);
+ break;
+ default:
+ formationOffset = CVector(0.0f, 0.0f, 0.0f);
+ break;
+ }
+ return formationOffset + referencePed->GetPosition();
+}
+
+void
+CPed::GetNearestDoor(CVehicle *veh, CVector &posToOpen)
+{
+ CVector *enterOffset = nil;
+ if (m_vehEnterType == CAR_DOOR_LF && veh->pDriver
+ || m_vehEnterType == CAR_DOOR_RF && veh->pPassengers[0]
+ || m_vehEnterType == CAR_DOOR_LR && veh->pPassengers[1]
+ || m_vehEnterType == CAR_DOOR_RR && veh->pPassengers[2])
+ {
+ enterOffset = &vecPedQuickDraggedOutCarAnimOffset;
+ }
+
+ CVector lfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LF);
+ CVector rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF);
+
+ // Right front door is closer
+ if ((lfPos - GetPosition()).MagnitudeSqr2D() >= (rfPos - GetPosition()).MagnitudeSqr2D()) {
+
+ if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) {
+
+ CPed *rfPassenger = veh->pPassengers[0];
+ if (!rfPassenger
+ || rfPassenger->m_leader != this && !m_ped_flagF4 && (veh->VehicleCreatedBy != MISSION_VEHICLE || m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER)
+ || veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset) == 0) {
+
+ if ((veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) == 0
+ || veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset) == 0) {
+ m_vehEnterType = CAR_DOOR_RF;
+ posToOpen = rfPos;
+ return;
+ }
+ }
+ } else {
+ if (!veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset))
+ return;
+ }
+ m_vehEnterType = CAR_DOOR_LF;
+ posToOpen = lfPos;
+ return;
+ }
+
+ if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) {
+ m_vehEnterType = CAR_DOOR_LF;
+ posToOpen = lfPos;
+ return;
+ }
+
+ if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) {
+ m_vehEnterType = CAR_DOOR_RF;
+ posToOpen = rfPos;
+ }
+}
+
+bool
+CPed::GetNearestPassengerDoor(CVehicle *veh, CVector &posToOpen)
+{
+ CVector rfPos, lrPos, rrPos;
+ bool canEnter = false;
+
+ CVehicleModelInfo *vehModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(veh->m_modelIndex);
+
+ if (veh->m_modelIndex > MI_RHINO || veh->m_modelIndex != MI_BUS) {
+
+ CVector2D rfPosDist(999.0f, 999.0f);
+ CVector2D lrPosDist(999.0f, 999.0f);
+ CVector2D rrPosDist(999.0f, 999.0f);
+
+ if (!veh->pPassengers[0]
+ && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF)
+ && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, nil)) {
+
+ rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF);
+ canEnter = true;
+ rfPosDist = rfPos - GetPosition();
+ }
+ if (vehModel->m_numDoors == 4) {
+ if (!veh->pPassengers[1]
+ && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR)
+ && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) {
+ lrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LR);
+ canEnter = true;
+ lrPosDist = lrPos - GetPosition();
+ }
+ if (!veh->pPassengers[2]
+ && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR)
+ && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) {
+ rrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RR);
+ canEnter = true;
+ rrPosDist = rrPos - GetPosition();
+ }
+
+ // When the door we should enter is blocked by some object.
+ if (!canEnter)
+ veh->ShufflePassengersToMakeSpace();
+ }
+
+ CVector2D nextToCompare = rfPosDist;
+ posToOpen = rfPos;
+ m_vehEnterType = CAR_DOOR_RF;
+ if (lrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) {
+ m_vehEnterType = CAR_DOOR_LR;
+ posToOpen = lrPos;
+ nextToCompare = lrPosDist;
+ }
+
+ if (rrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) {
+ m_vehEnterType = CAR_DOOR_RR;
+ posToOpen = rrPos;
+ }
+ return canEnter;
+
+ } else {
+ m_vehEnterType = CAR_DOOR_RF;
+ posToOpen = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF);
+ return true;
+ }
+}
+
+int
+CPed::GetNextPointOnRoute(void)
+{
+ int16 nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint;
+
+ // Route is complete
+ if (nextPoint < 0 || nextPoint > 200 || m_routeLastPoint != CRouteNode::GetRouteThisPointIsOn(nextPoint)) {
+
+ switch (m_routeType) {
+ case PEDROUTE_STOP_WHEN_DONE:
+ nextPoint = -1;
+ break;
+ case PEDROUTE_GO_BACKWARD_WHEN_DONE:
+ m_routePointsBeingPassed = -m_routePointsBeingPassed;
+ nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint;
+ break;
+ case PEDROUTE_GO_TO_START_WHEN_DONE:
+ m_routePointsPassed = -1;
+ nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint;
+ break;
+ default:
+ break;
+ }
+ }
+ return nextPoint;
+}
+
+// TODO: enum
+uint8
+CPed::GetPedRadioCategory(uint32 modelIndex)
+{
+ switch (modelIndex) {
+ case MI_MALE01:
+ case MI_FEMALE03:
+ case MI_PROSTITUTE2:
+ case MI_WORKER1:
+ case MI_MOD_MAN:
+ case MI_MOD_WOM:
+ case MI_ST_WOM:
+ case MI_FAN_WOM:
+ return 3;
+ case MI_TAXI_D:
+ case MI_PIMP:
+ case MI_MALE02:
+ case MI_FEMALE02:
+ case MI_FATFEMALE01:
+ case MI_FATFEMALE02:
+ case MI_DOCKER1:
+ case MI_WORKER2:
+ case MI_FAN_MAN2:
+ return 9;
+ case MI_GANG01:
+ case MI_GANG02:
+ case MI_SCUM_MAN:
+ case MI_SCUM_WOM:
+ case MI_HOS_WOM:
+ case MI_CONST1:
+ return 1;
+ case MI_GANG03:
+ case MI_GANG04:
+ case MI_GANG07:
+ case MI_GANG08:
+ case MI_CT_MAN2:
+ case MI_CT_WOM2:
+ case MI_B_MAN3:
+ case MI_SHOPPER3:
+ return 4;
+ case MI_GANG05:
+ case MI_GANG06:
+ case MI_GANG11:
+ case MI_GANG12:
+ case MI_CRIMINAL02:
+ case MI_B_WOM2:
+ case MI_ST_MAN:
+ case MI_HOS_MAN:
+ return 5;
+ case MI_FATMALE01:
+ case MI_LI_MAN2:
+ case MI_SHOPPER1:
+ case MI_CAS_MAN:
+ return 6;
+ case MI_PROSTITUTE:
+ case MI_P_WOM2:
+ case MI_LI_WOM2:
+ case MI_B_WOM3:
+ case MI_CAS_WOM:
+ return 2;
+ case MI_P_WOM1:
+ case MI_DOCKER2:
+ case MI_STUD_MAN:
+ return 7;
+ case MI_CT_MAN1:
+ case MI_CT_WOM1:
+ case MI_LI_MAN1:
+ case MI_LI_WOM1:
+ case MI_B_MAN1:
+ case MI_B_MAN2:
+ case MI_B_WOM1:
+ case MI_SHOPPER2:
+ case MI_STUD_WOM:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+// Some kind of VC leftover I think
+int
+CPed::GetWeaponSlot(eWeaponType weaponType)
+{
+ if (HasWeapon(weaponType))
+ return weaponType;
+ else
+ return -1;
+}
+
+void
+CPed::GoToNearestDoor(CVehicle *veh)
+{
+ CVector posToOpen;
+ GetNearestDoor(veh, posToOpen);
+ SetSeek(posToOpen, 0.5f);
+ SetMoveState(PEDMOVE_RUN);
+}
+
+bool
+CPed::HaveReachedNextPointOnRoute(float distToCountReached)
+{
+ if ((m_nextRoutePointPos - GetPosition()).Magnitude2D() >= distToCountReached)
+ return false;
+
+ m_routePointsPassed += m_routePointsBeingPassed;
+ return true;
+}
+
+void
+CPed::Idle(void)
+{
+ CVehicle *veh = m_pMyVehicle;
+ if (veh && veh->m_nGettingOutFlags && m_vehEnterType) {
+
+ if (veh->m_nGettingOutFlags & GetCarDoorFlag(m_vehEnterType)) {
+
+ if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
+
+ CVector doorPos = GetPositionToOpenCarDoor(veh, m_vehEnterType);
+ CVector doorDist = GetPosition() - doorPos;
+
+ if (doorDist.MagnitudeSqr() < 0.25f) {
+ SetMoveState(PEDMOVE_WALK);
+ return;
+ }
+ }
+ }
+ }
+
+ CAnimBlendAssociation *armedIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_ARMED);
+ CAnimBlendAssociation* unarmedIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_STANCE);
+ int waitTime;
+
+ if (m_nMoveState == PEDMOVE_STILL) {
+
+ eWeaponType curWeapon = GetWeapon()->m_eWeaponType;
+ if (!armedIdleAssoc ||
+ CTimer::GetTimeInMilliseconds() <= m_nWaitTimer && curWeapon != WEAPONTYPE_UNARMED && curWeapon != WEAPONTYPE_MOLOTOV && curWeapon != WEAPONTYPE_GRENADE) {
+
+ if ((!GetWeapon()->IsType2Handed() || curWeapon == WEAPONTYPE_SHOTGUN) && curWeapon != WEAPONTYPE_BASEBALLBAT
+ || !unarmedIdleAssoc || unarmedIdleAssoc->blendAmount <= 0.95f || m_nWaitState != WAITSTATE_FALSE || CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) {
+
+ m_moved = CVector2D(0.0f, 0.0f);
+ return;
+ }
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_ARMED, 3.0f);
+ waitTime = CGeneral::GetRandomNumberInRange(4000, 7500);
+ } else {
+ armedIdleAssoc->blendDelta = -2.0f;
+ armedIdleAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ waitTime = CGeneral::GetRandomNumberInRange(3000, 8500);
+ }
+ m_nWaitTimer = CTimer::GetTimeInMilliseconds() + waitTime;
+ } else {
+ if (armedIdleAssoc) {
+ armedIdleAssoc->blendDelta = -8.0f;
+ armedIdleAssoc->flags |= ASSOC_DELETEFADEDOUT;
+ m_nWaitTimer = 0;
+ }
+ if (!IsPlayer())
+ SetMoveState(PEDMOVE_STILL);
+ }
+ m_moved = CVector2D(0.0f, 0.0f);
+}
+
+void
+CPed::InTheAir(void)
+{
+ CColPoint foundCol;
+ CEntity *foundEnt;
+
+ CVector ourPos = GetPosition();
+ CVector bitBelow = GetPosition();
+ bitBelow.z -= 4.04f;
+
+ if (m_vecMoveSpeed.z < 0.0f && !bIsPedDieAnimPlaying) {
+ if (m_nPedState != PED_DIE && m_nPedState != PED_DEAD) {
+ if (CWorld::ProcessLineOfSight(ourPos, bitBelow, foundCol, foundEnt, true, true, false, true, false, false, false)) {
+ if (GetPosition().z - foundCol.point.z < 1.3f)
+ SetLanding();
+ } else {
+ if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FALL_FALL)) {
+ if (m_vecMoveSpeed.z < -0.1f)
+ CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FALL_FALL, 4.0f);
+ }
+ }
+ }
+ }
+}
+
+void
+CPed::SetLanding(void)
+{
+ if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD)
+ return;
+
+ CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FALL_FALL);
+ CAnimBlendAssociation *landAssoc;
+
+ RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f);
+ if (fallAssoc) {
+ landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FALL_COLLAPSE);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_COLLAPSE, 1.0f);
+
+ if (IsPlayer())
+ Say(SOUND_PED_LAND);
+
+ } else {
+ landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FALL_LAND);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_LAND, 1.0f);
+ }
+
+ landAssoc->SetFinishCallback(PedLandCB, this);
+ bIsInTheAir = false;
+ bIsLanding = true;
+}
+
+void
+CPed::Initialise(void)
+{
+ debug("Initialising CPed...\n");
+ CPedType::Initialise();
+ LoadFightData();
+ SetAnimOffsetForEnterOrExitVehicle();
+ debug("CPed ready\n");
+}
+
+void
+CPed::SetAnimOffsetForEnterOrExitVehicle(void)
+{
+ // FIX: If there were no translations on enter anims, there were overflows all over this function.
+
+ CAnimBlendHierarchy *enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_CAR_JACKED_LHS)->hierarchy;
+ CAnimBlendSequence *seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedDraggedOutCarAnimOffset = lastFrame->translation;
+ }
+ }
+
+ enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_CAR_GETIN_LHS)->hierarchy;
+ seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedCarDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedCarDoorAnimOffset = lastFrame->translation;
+ }
+ }
+
+ enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_CAR_GETIN_LOW_LHS)->hierarchy;
+ seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedCarDoorLoAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedCarDoorLoAnimOffset = lastFrame->translation;
+ }
+ }
+
+ enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_CAR_QJACKED)->hierarchy;
+ seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedQuickDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedQuickDraggedOutCarAnimOffset = lastFrame->translation;
+ }
+ }
+
+ enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_VAN_GETIN_L)->hierarchy;
+ seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedVanRearDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedVanRearDoorAnimOffset = lastFrame->translation;
+ }
+ }
+
+ enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_TRAIN_GETOUT)->hierarchy;
+ seq = enterAssoc->sequences;
+ CAnimManager::UncompressAnimation(enterAssoc);
+ if (seq->numFrames > 0) {
+ if (!seq->HasTranslation())
+ vecPedTrainDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f);
+ else {
+ KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1);
+ vecPedTrainDoorAnimOffset = lastFrame->translation;
+ }
+ }
+}
+
WRAPPER void CPed::PedGetupCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE810); }
WRAPPER void CPed::PedStaggerCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8D0); }
WRAPPER void CPed::PedEvadeCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D36E0); }
-WRAPPER void CPed::FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D3950); }
-WRAPPER void CPed::FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D6520); }
-WRAPPER void CPed::FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7490); }
-WRAPPER void CPed::FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A80); }
WRAPPER void CPed::PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEC80); }
WRAPPER void CPed::PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DE500); }
WRAPPER void CPed::PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEAF0); }
@@ -5711,9 +7677,7 @@ WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation* dra
WRAPPER void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation* dragAssoc, void* arg) { EAXJMP(0x4E2920); }
WRAPPER void CPed::PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E3290); }
WRAPPER void CPed::PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E36E0); }
-WRAPPER void CPed::FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E9830); }
WRAPPER void CPed::PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E4B90); }
-WRAPPER void CPed::FinishJumpCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A50); }
WRAPPER void CPed::PedLandCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8A0); }
WRAPPER void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4C6580); }
@@ -5825,7 +7789,6 @@ STARTPATCHES
InjectHook(0x4D09B0, &CPed::SetFall, PATCH_JUMP);
InjectHook(0x4E6220, &CPed::SetAttack, PATCH_JUMP);
InjectHook(0x4E7530, &CPed::StartFightAttack, PATCH_JUMP);
- InjectHook(0x4E9870, &CPed::LoadFightData, PATCH_JUMP);
InjectHook(0x4E8EC0, &CPed::FightStrike, PATCH_JUMP);
InjectHook(0x4CCE20, &CPed::GetLocalDirection, PATCH_JUMP);
InjectHook(0x4E8E20, &CPed::PlayHitSound, PATCH_JUMP);
@@ -5846,4 +7809,35 @@ STARTPATCHES
InjectHook(0x4D15A0, (void (CPed::*)(CEntity*, float)) &CPed::SetSeek, PATCH_JUMP);
InjectHook(0x4EB5C0, &CPed::DoesLOSBulletHitPed, PATCH_JUMP);
InjectHook(0x4E3EC0, &CPed::DuckAndCover, PATCH_JUMP);
-ENDPATCHES
+ InjectHook(0x4E8D30, &CPed::EndFight, PATCH_JUMP);
+ InjectHook(0x4E0D30, &CPed::EnterCar, PATCH_JUMP);
+ InjectHook(0x4E2E50, &CPed::GetNearestTrainPedPosition, PATCH_JUMP);
+ InjectHook(0x4E2D70, &CPed::GetNearestTrainDoor, PATCH_JUMP);
+ InjectHook(0x4E33D0, &CPed::LineUpPedWithTrain, PATCH_JUMP);
+ InjectHook(0x4E18D0, &CPed::ExitCar, PATCH_JUMP);
+ InjectHook(0x4E7EE0, &CPed::Fight, PATCH_JUMP);
+ InjectHook(0x4D3950, &CPed::FinishDieAnimCB, PATCH_JUMP);
+ InjectHook(0x4E9830, &CPed::FinishFightMoveCB, PATCH_JUMP);
+ InjectHook(0x4D7A80, &CPed::FinishHitHeadCB, PATCH_JUMP);
+ InjectHook(0x4D7A50, &CPed::FinishJumpCB, PATCH_JUMP);
+ InjectHook(0x4D7490, &CPed::FinishLaunchCB, PATCH_JUMP);
+ InjectHook(0x4D6520, &CPed::FinishedWaitCB, PATCH_JUMP);
+ InjectHook(0x4D5D80, &CPed::Wait, PATCH_JUMP);
+ InjectHook(0x4E3A90, &CPed::FindBestCoordsFromNodes, PATCH_JUMP);
+ InjectHook(0x4D2E70, &CPed::SeekFollowingPath, PATCH_JUMP);
+ InjectHook(0x4D1640, &CPed::Seek, PATCH_JUMP);
+ InjectHook(0x4D3020, &CPed::FollowPath, PATCH_JUMP);
+ InjectHook(0x4D1ED0, &CPed::Flee, PATCH_JUMP);
+ InjectHook(0x4E1CF0, &CPed::GetNearestDoor, PATCH_JUMP);
+ InjectHook(0x4DF420, &CPed::GetFormationPosition, PATCH_JUMP);
+ InjectHook(0x4E1F30, &CPed::GetNearestPassengerDoor, PATCH_JUMP);
+ InjectHook(0x4D0690, &CPed::Idle, PATCH_JUMP);
+ InjectHook(0x4DD720, &CPed::GetNextPointOnRoute, PATCH_JUMP);
+ InjectHook(0x4D7B50, &CPed::GetPedRadioCategory, PATCH_JUMP);
+ InjectHook(0x4CFA40, &CPed::GetWeaponSlot, PATCH_JUMP);
+ InjectHook(0x4E2220, &CPed::GoToNearestDoor, PATCH_JUMP);
+ InjectHook(0x4DD7B0, &CPed::HaveReachedNextPointOnRoute, PATCH_JUMP);
+ InjectHook(0x4D0D10, &CPed::InTheAir, PATCH_JUMP);
+ InjectHook(0x4C5270, &CPed::Initialise, PATCH_JUMP);
+ InjectHook(0x4D0E40, &CPed::SetLanding, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/peds/Ped.h b/src/peds/Ped.h
index db19619b..264a67a8 100644
--- a/src/peds/Ped.h
+++ b/src/peds/Ped.h
@@ -22,6 +22,20 @@ struct CPedAudioData
int m_nMaxRandomDelayTime;
};
+enum
+{
+ ENDFIGHT_NORMAL,
+ ENDFIGHT_WITH_A_STEP,
+ ENDFIGHT_FAST
+};
+
+enum PedRouteType
+{
+ PEDROUTE_STOP_WHEN_DONE = 1,
+ PEDROUTE_GO_BACKWARD_WHEN_DONE,
+ PEDROUTE_GO_TO_START_WHEN_DONE
+};
+
struct FightMove
{
AnimationId animId;
@@ -152,7 +166,7 @@ enum PedLineUpPhase {
enum PedOnGroundState {
NO_PED,
- PED_BELOW_PLAYER,
+ PED_IN_FRONT_OF_ATTACKER,
PED_ON_THE_FLOOR,
PED_DEAD_ON_THE_FLOOR
};
@@ -241,7 +255,7 @@ public:
// cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPed.h from R*
uint8 bIsStanding : 1;
uint8 m_ped_flagA2 : 1;
- uint8 m_ped_flagA4 : 1; // stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime)
+ uint8 bIsAttacking : 1; // doesn't reset after fist fight, also stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime)
uint8 bIsPointingGunAt : 1;
uint8 bIsLooking : 1;
uint8 m_ped_flagA20 : 1; // "look" method? - probably missing in SA
@@ -253,7 +267,7 @@ public:
uint8 bIsTalking : 1;
uint8 bIsInTheAir : 1;
uint8 bIsLanding : 1;
- uint8 m_ped_flagB20 : 1;
+ uint8 bIsRunning : 1; // not fleeing
uint8 m_ped_flagB40 : 1;
uint8 m_ped_flagB80 : 1;
@@ -296,7 +310,7 @@ public:
uint8 m_ped_flagG1 : 1;
uint8 m_ped_flagG2 : 1;
uint8 m_ped_flagG4 : 1;
- uint8 m_ped_flagG8 : 1;
+ uint8 m_ped_flagG8 : 1; // ped starts to go somewhere when set
uint8 m_ped_flagG10 : 1;
uint8 m_ped_flagG20 : 1;
uint8 m_ped_flagG40 : 1;
@@ -328,9 +342,7 @@ public:
eObjective m_prevObjective;
CPed *m_pedInObjective;
CVehicle *m_carInObjective;
- uint32 field_174;
- uint32 field_178;
- uint32 field_17C;
+ CVector m_nextRoutePointPos;
CPed *m_leader;
uint32 m_pedFormation;
uint32 m_fearFlags;
@@ -363,15 +375,15 @@ public:
private:
int8 _pad2B5[3];
public:
- CPathNode *m_pNextPathNode;
CPathNode *m_pLastPathNode;
+ CPathNode *m_pNextPathNode;
float m_fHealth;
float m_fArmour;
int16 m_routeLastPoint;
- uint16 m_routePoints;
- int16 m_routePos;
+ uint16 m_routeStartPoint;
+ int16 m_routePointsPassed;
int16 m_routeType;
- int16 m_routeCurDir;
+ int16 m_routePointsBeingPassed;
uint16 field_2D2;
CVector2D m_moved;
float m_fRotationCur;
@@ -382,12 +394,12 @@ public:
CEntity *m_pCurrentPhysSurface;
CVector m_vecOffsetFromPhysSurface;
CEntity *m_pCurSurface;
- CVector m_vecSeekVehicle;
+ CVector m_vecSeekPos;
CEntity *m_pSeekTarget;
CVehicle *m_pMyVehicle;
bool bInVehicle;
uint8 pad_315[3];
- float field_318;
+ float m_distanceToCountSeekDone;
bool bRunningToPhone;
uint8 field_31D;
int16 m_phoneId;
@@ -400,8 +412,8 @@ public:
float m_fleeFromPosY;
CEntity *m_fleeFrom;
uint32 m_fleeTimer;
- uint32 field_344;
- uint32 m_lastThreatTimer;
+ CEntity* m_collidingEntityWhileFleeing;
+ uint32 m_collidingThingTimer;
CEntity *m_pCollidingEntity;
uint8 m_stateUnused;
uint8 pad_351[3];
@@ -418,7 +430,7 @@ public:
PedFightMoves m_lastFightMove;
uint8 m_fightButtonPressure;
int8 m_fightUnk2; // TODO
- uint8 m_fightUnk1; // TODO
+ bool m_takeAStepAfterAttack;
uint8 pad_4B3;
CFire *m_pFire;
CEntity *m_pLookTarget;
@@ -429,7 +441,7 @@ public:
uint32 m_lookTimer;
uint32 m_standardTimer;
uint32 m_attackTimer;
- uint32 m_lastHitTime;
+ uint32 m_lastHitTime; // obviously not correct
uint32 m_hitRecoverTimer;
uint32 m_objectiveTimer;
uint32 m_duckTimer;
@@ -560,7 +572,6 @@ public:
void SetEvasiveDive(CPhysical*, uint8);
void SetAttack(CEntity*);
void StartFightAttack(uint8);
- void LoadFightData(void);
void SetWaitState(eWaitState, void*);
bool FightStrike(CVector&);
int GetLocalDirection(CVector2D&);
@@ -583,11 +594,38 @@ public:
void MakeTyresMuddySectorList(CPtrList&);
uint8 DoesLOSBulletHitPed(CColPoint &point);
bool DuckAndCover(void);
+ void EndFight(uint8);
+ void EnterCar(void);
+ uint8 GetNearestTrainPedPosition(CVehicle*, CVector&);
+ uint8 GetNearestTrainDoor(CVehicle*, CVector&);
+ void LineUpPedWithTrain(void);
+ void ExitCar(void);
+ void Fight(void);
+ bool FindBestCoordsFromNodes(CVector unused, CVector* a6);
+ void Wait(void);
+ void ProcessObjective(void);
+ bool SeekFollowingPath(CVector*);
+ void Flee(void);
+ void FollowPath(void);
+ CVector GetFormationPosition(void);
+ void GetNearestDoor(CVehicle*, CVector&);
+ bool GetNearestPassengerDoor(CVehicle*, CVector&);
+ int GetNextPointOnRoute(void);
+ uint8 GetPedRadioCategory(uint32);
+ int GetWeaponSlot(eWeaponType);
+ void GoToNearestDoor(CVehicle*);
+ bool HaveReachedNextPointOnRoute(float a2);
+ void Idle(void);
+ void InTheAir(void);
+ void SetLanding(void);
// Static methods
static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset);
static CVector GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult);
static CVector GetPositionToOpenCarDoor(CVehicle* veh, uint32 component);
+ static void Initialise(void);
+ static void SetAnimOffsetForEnterOrExitVehicle(void);
+ static void LoadFightData(void);
// Callbacks
static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data);
@@ -640,6 +678,9 @@ public:
void SetPedStats(ePedStats);
bool IsGangMember(void);
void Die(void);
+ void EnterTrain(void);
+ void ExitTrain(void);
+ void Fall(void);
bool HasWeapon(uint8 weaponType) { return m_weapons[weaponType].m_eWeaponType == weaponType; }
CWeapon &GetWeapon(uint8 weaponType) { return m_weapons[weaponType]; }
@@ -649,18 +690,17 @@ public:
void SetPedState(PedState state) { m_nPedState = state; }
// set by 0482:set_threat_reaction_range_multiplier opcode
- static uint16 &distanceMultToCountPedNear;
+ static uint16 &nThreatReactionRangeMultiplier;
- static CVector &offsetToOpenRegularCarDoor;
- static CVector &offsetToOpenLowCarDoor;
- static CVector &offsetToOpenVanDoor;
static bool &bNastyLimbsCheat;
static bool &bPedCheat2;
static bool &bPedCheat3;
- static CColPoint &ms_tempColPoint;
- static uint16 &unknownFightThing; // TODO
- static FightMove (&ms_fightMoves)[24];
- static CPedAudioData (&PedAudioData)[38];
+ static CVector2D &ms_vec2DFleePosition;
+ static CPedAudioData (&CommentWaitTime)[38];
+
+#ifndef FINAL
+ static bool bUnusedFightThingOnPlayer;
+#endif
};
void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg);
diff --git a/src/peds/PedRoutes.cpp b/src/peds/PedRoutes.cpp
new file mode 100644
index 00000000..c572d8be
--- /dev/null
+++ b/src/peds/PedRoutes.cpp
@@ -0,0 +1,6 @@
+#include "common.h"
+#include "patcher.h"
+#include "main.h"
+#include "PedRoutes.h"
+
+WRAPPER int16 CRouteNode::GetRouteThisPointIsOn(int16) { EAXJMP(0x4EE7A0); } \ No newline at end of file
diff --git a/src/peds/PedRoutes.h b/src/peds/PedRoutes.h
new file mode 100644
index 00000000..2530ebe6
--- /dev/null
+++ b/src/peds/PedRoutes.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class CRouteNode
+{
+public:
+ static int16 GetRouteThisPointIsOn(int16 point);
+}; \ No newline at end of file
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 77489e60..99d50e8f 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -200,9 +200,9 @@ CRenderer::RenderRoads(void)
if(gbShowCarRoadGroups || gbShowPedRoadGroups){
int ind = 0;
if(gbShowCarRoadGroups)
- ind += ThePaths.m_pathNodes[t->m_nodeIndicesCars[0]].group;
+ ind += ThePaths.m_pathNodes[t->m_nodeIndices[PATH_CAR][0]].group;
if(gbShowPedRoadGroups)
- ind += ThePaths.m_pathNodes[t->m_nodeIndicesPeds[0]].group;
+ ind += ThePaths.m_pathNodes[t->m_nodeIndices[PATH_PED][0]].group;
SetAmbientColoursToIndicateRoadGroup(ind);
}
#endif
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index 00f53762..488dcf69 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -312,7 +312,7 @@ CAutomobile::ProcessControl(void)
CVisibilityPlugins::SetClumpAlpha((RpClump*)m_rwObject, clumpAlpha);
AutoPilot.m_flag1 = false;
- AutoPilot.m_flag2 = false;
+ AutoPilot.m_bSlowedDownBecauseOfPeds = false;
// Set Center of Mass to make car more stable
if(strongGrip1 || bCheat3)
@@ -3930,7 +3930,7 @@ CAutomobile::PlayCarHorn(void)
void
CAutomobile::PlayHornIfNecessary(void)
{
- if(AutoPilot.m_flag2 ||
+ if(AutoPilot.m_bSlowedDownBecauseOfPeds ||
AutoPilot.m_flag1)
if(!HasCarStoppedBecauseOfLight())
PlayCarHorn();
diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h
index e6b64e6e..4dd3a087 100644
--- a/src/vehicles/Automobile.h
+++ b/src/vehicles/Automobile.h
@@ -58,6 +58,14 @@ enum eBombType
CARBOMB_ONIGNITIONACTIVE,
};
+enum {
+ CAR_DOOR_FLAG_UNKNOWN = 0x0,
+ CAR_DOOR_FLAG_LF = 0x1,
+ CAR_DOOR_FLAG_LR = 0x2,
+ CAR_DOOR_FLAG_RF = 0x4,
+ CAR_DOOR_FLAG_RR = 0x8
+};
+
class CAutomobile : public CVehicle
{
public:
@@ -189,14 +197,14 @@ static_assert(sizeof(CAutomobile) == 0x5A8, "CAutomobile: error");
inline uint8 GetCarDoorFlag(int32 carnode) {
switch (carnode) {
case CAR_DOOR_LF:
- return 1;
+ return CAR_DOOR_FLAG_LF;
case CAR_DOOR_LR:
- return 2;
+ return CAR_DOOR_FLAG_LR;
case CAR_DOOR_RF:
- return 4;
+ return CAR_DOOR_FLAG_RF;
case CAR_DOOR_RR:
- return 8;
+ return CAR_DOOR_FLAG_RR;
default:
- return 0;
+ return CAR_DOOR_FLAG_UNKNOWN;
}
}
diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp
index d43e8c19..9b1a651d 100644
--- a/src/vehicles/Heli.cpp
+++ b/src/vehicles/Heli.cpp
@@ -1,21 +1,1055 @@
#include "common.h"
+#include "main.h"
#include "patcher.h"
+#include "General.h"
+#include "Darkel.h"
+#include "Stats.h"
+#include "SurfaceTable.h"
+#include "ModelIndices.h"
+#include "Streaming.h"
+#include "Camera.h"
+#include "VisibilityPlugins.h"
+#include "ZoneCull.h"
+#include "Particle.h"
+#include "Shadows.h"
+#include "Coronas.h"
+#include "Explosion.h"
+#include "TimeCycle.h"
+#include "TempColModels.h"
+#include "World.h"
+#include "WaterLevel.h"
+#include "PlayerPed.h"
+#include "Object.h"
+#include "HandlingMgr.h"
#include "Heli.h"
-CHeli::CHeli(int mi, uint8 owner)
+enum
{
- ctor(mi, owner);
+ HELI_STATUS_HOVER,
+ HELI_STATUS_CHASE_PLAYER,
+ HELI_STATUS_FLY_AWAY,
+ HELI_STATUS_SHOT_DOWN,
+ HELI_STATUS_HOVER2,
+};
+
+CHeli **CHeli::pHelis = (CHeli**)0x72CF50;
+int16 &CHeli::NumRandomHelis = *(int16*)0x95CCAA;
+uint32 &CHeli::TestForNewRandomHelisTimer = *(uint32*)0x8F1A7C;
+int16 CHeli::NumScriptHelis; // unused
+bool &CHeli::CatalinaHeliOn = *(bool*)0x95CD85;
+bool &CHeli::CatalinaHasBeenShotDown = *(bool*)0x95CD56;
+bool &CHeli::ScriptHeliOn = *(bool*)0x95CD43;
+
+CHeli::CHeli(int32 id, uint8 CreatedBy)
+ : CVehicle(CreatedBy)
+{
+ int i;
+
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
+ m_vehType = VEHICLE_TYPE_HELI;
+ pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId);
+ SetModelIndex(id);
+ m_heliStatus = HELI_STATUS_HOVER;
+ m_pathState = 0;
+
+ m_fMass = 100000000.0f;
+ m_fTurnMass = 100000000.0f;
+ m_fAirResistance = 0.9994f;
+ m_fElasticity = 0.05f;
+
+ m_nHeliId = 0;
+ m_fRotorRotation = 0.0f;
+ m_nBulletDamage = 0;
+ m_fAngularSpeed = 0.0f;
+ m_fRotation = 0.0f;
+ m_nSearchLightTimer = CTimer::GetTimeInMilliseconds();
+ for(i = 0; i < 6; i++){
+ m_aSearchLightHistoryX[i] = 0.0f;
+ m_aSearchLightHistoryY[i] = 0.0f;
+ }
+
+ for(i = 0; i < 8; i++)
+ m_fHeliDustZ[i] = -50.0f;
+
+ m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds();
+ m_status = STATUS_HELI;
+ m_bTestRight = true;
+ m_fTargetOffset = 0.0f;
+ m_fSearchLightX = m_fSearchLightY = 0.0f;
+}
+
+void
+CHeli::SetModelIndex(uint32 id)
+{
+ int i;
+
+ CVehicle::SetModelIndex(id);
+ for(i = 0; i < NUM_HELI_NODES; i++)
+ m_aHeliNodes[i] = nil;
+ CClumpModelInfo::FillFrameArray(GetClump(), m_aHeliNodes);
+}
+
+static float CatalinaTargetX[7] = { -478.0, -677.0, -907.0, -1095.0, -1152.0, -1161.0, -1161.0 };
+static float CatalinaTargetY[7] = { 227.0, 206.0, 210.0, 242.0, 278.0, 341.0, 341.0 };
+static float CatalinaTargetZ[7] = { 77.0, 66.0, 60.0, 53.0, 51.0, 46.0, 30.0 };
+static float DamPathX[6] = { -1191.0, -1176.0, -1128.0, -1072.0, -1007.0, -971.0 };
+static float DamPathY[6] = { 350.0, 388.0, 429.0, 447.0, 449.0, 416.0 };
+static float DamPathZ[6] = { 42.0, 37.0, 28.0, 28.0, 31.0, 33.0 };
+static float ShortPathX[4] = { -974.0, -1036.0, -1112.0, -1173.0 };
+static float ShortPathY[4] = { 340.0, 312.0, 317.0, 294.0 };
+static float ShortPathZ[4] = { 41.0, 38.0, 32.0, 39.0 };
+static float LongPathX[7] = { -934.0, -905.0, -906.0, -1063.0, -1204.0, -1233.0, -1207.0 };
+static float LongPathY[7] = { 371.0, 362.0, 488.0, 548.0, 451.0, 346.0, 308.0 };
+static float LongPathZ[7] = { 57.0, 90.0, 105.0, 100.0, 81.0, 79.0, 70.0 };
+
+static int PathPoint;
+
+void
+CHeli::ProcessControl(void)
+{
+ int i;
+
+ if(gbModelViewer)
+ return;
+
+ // Find target
+ CVector target(0.0f, 0.0f, 0.0f);
+ CVector2D vTargetDist;
+ if(m_heliType == HELI_TYPE_CATALINA && m_heliStatus != HELI_STATUS_SHOT_DOWN){
+ switch(m_pathState){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ target.x = CatalinaTargetX[m_pathState];
+ target.y = CatalinaTargetY[m_pathState];
+ target.z = CatalinaTargetZ[m_pathState];
+ if((target - GetPosition()).Magnitude() < 9.0f)
+ m_pathState++;
+ break;
+ case 6:
+ target.x = CatalinaTargetX[m_pathState];
+ target.y = CatalinaTargetY[m_pathState];
+ target.z = CatalinaTargetZ[m_pathState];
+ if(GetPosition().z > 31.55f)
+ break;
+ m_pathState = 7;
+ GetPosition().z = 31.55f;
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ break;
+ case 7:
+ GetPosition().z = 31.55f;
+ target = GetPosition();
+ break;
+
+
+ // Take off
+ case 8:
+ target.x = GetPosition().x;
+ target.y = GetPosition().y;
+ target.z = 74.0f;
+ if(GetPosition().z < 40.0f)
+ break;
+ PathPoint = 2;
+ m_pathState = 9;
+ break;
+ // Circle around dam
+ case 9:
+ target.x = DamPathX[PathPoint];
+ target.y = DamPathY[PathPoint];
+ target.z = DamPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 6){
+ m_pathState = 10;
+ PathPoint = 0;
+ }
+ }
+ break;
+ case 10:
+ target.x = ShortPathX[PathPoint];
+ target.y = ShortPathY[PathPoint];
+ target.z = ShortPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 3){
+ m_pathState = 9;
+ PathPoint = 1;
+ }
+ }
+ break;
+ // how do we get here?
+ case 11:
+ target.x = LongPathX[PathPoint];
+ target.y = LongPathY[PathPoint];
+ target.z = LongPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 7){
+ m_pathState = 9;
+ PathPoint = 0;
+ }
+ }
+ break;
+
+
+ // Fly away
+ case 12:
+ target.x = GetPosition().x;
+ target.y = GetPosition().y;
+ target.z = 200.0f;
+ break;
+ }
+
+ vTargetDist = target - GetPosition();
+ m_fTargetZ = target.z;
+ if(m_pathState == 6){
+ GetPosition().x = GetPosition().x*0.99f + target.x*0.01f;
+ GetPosition().y = GetPosition().y*0.99f + target.y*0.01f;
+ }
+ }else{
+ vTargetDist = FindPlayerCoors() - GetPosition();
+ m_fTargetZ = FindPlayerCoors().z;
+
+ // Heli flies away to (0, 0)
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY && GetPosition().z > 20.0f){
+ vTargetDist.x = 0.0f - GetPosition().x;
+ vTargetDist.y = 0.0f - GetPosition().y;
+ }
+
+ float groundZ;
+ switch(m_heliStatus){
+ case HELI_STATUS_HOVER:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f;
+ break;
+ case HELI_STATUS_SHOT_DOWN:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset;
+ break;
+ case HELI_STATUS_HOVER2:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset;
+ break;
+ default:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 12.0f;
+ break;
+ }
+
+ // Move up if too low
+ if(GetPosition().z - 2.0f < groundZ && m_heliStatus != HELI_STATUS_SHOT_DOWN)
+ m_vecMoveSpeed.z += CTimer::GetTimeStep()*0.01f;
+ m_vecMoveSpeed.z = clamp(m_vecMoveSpeed.z, -0.3f, 0.3f);
+ }
+
+ float fTargetDist = vTargetDist.Magnitude();
+
+ switch(m_heliStatus){
+ case HELI_STATUS_HOVER:
+ case HELI_STATUS_HOVER2:{
+ float targetHeight;
+ if(m_heliType == HELI_TYPE_CATALINA)
+ targetHeight = 8.0f;
+ else
+ targetHeight = 40.0f - m_nHeliId*10.0f;
+ if(fTargetDist > targetHeight)
+ m_heliStatus = HELI_STATUS_CHASE_PLAYER;
+ }
+ // fall through, BUG?
+ case HELI_STATUS_CHASE_PLAYER:{
+ float targetHeight;
+ if(m_heliType == HELI_TYPE_CATALINA)
+ targetHeight = 4.0f;
+ else
+ targetHeight = 30.0f - m_nHeliId*7.5f;
+ if(fTargetDist < 1.0f ||
+ fTargetDist < targetHeight && CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false))
+ m_heliStatus = HELI_STATUS_HOVER;
+ }
+ }
+
+ // Find xy speed
+ float speed;
+ if(fTargetDist > 100.0f)
+ speed = 1.0f;
+ else if(fTargetDist > 75.0f)
+ speed = 0.7f;
+ else
+ speed = 0.4f;
+ if(m_heliStatus == HELI_STATUS_HOVER || m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN)
+ speed = 0.0f;
+
+ if(fTargetDist != 0.0f)
+ vTargetDist /= fTargetDist;
+ else
+ vTargetDist.x = 1.0f;
+ CVector2D targetSpeed = vTargetDist * speed;
+
+ if(m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ bool force = !!((CTimer::GetFrameCounter() + m_randomSeed) & 8);
+ if(m_bTestRight){
+ if(force || CWorld::TestSphereAgainstWorld(GetPosition() + 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ m_fTargetOffset -= CTimer::GetTimeStep()*0.05f;
+ targetSpeed.x -= -vTargetDist.x*0.15f;
+ targetSpeed.y -= vTargetDist.y*0.15f;
+ }else{
+ targetSpeed.x -= -vTargetDist.x*0.05f;
+ targetSpeed.y -= vTargetDist.y*0.05f;
+ }
+ }else{
+ m_bTestRight = false;
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_fTargetOffset += 5.0f;
+ else
+ m_fTargetOffset -= 5.0f;
+ }
+ }else{
+ if(force || CWorld::TestSphereAgainstWorld(GetPosition() - 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ m_fTargetOffset -= CTimer::GetTimeStep()*0.05f;
+ targetSpeed.x += -vTargetDist.x*0.15f;
+ targetSpeed.y += vTargetDist.y*0.15f;
+ }else{
+ targetSpeed.x += -vTargetDist.x*0.05f;
+ targetSpeed.y += vTargetDist.y*0.05f;
+ }
+ }else{
+ m_bTestRight = true;
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_fTargetOffset += 5.0f;
+ else
+ m_fTargetOffset -= 5.0f;
+ }
+ }
+
+ if(m_fTargetOffset > 30.0f)
+ m_fTargetOffset = 30.0f;
+
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN && force){
+ if(CWorld::TestSphereAgainstWorld(GetPosition() + 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false) ||
+ CWorld::TestSphereAgainstWorld(GetPosition() - 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false))
+ m_nExplosionTimer = CTimer::GetPreviousTimeInMilliseconds();
+ }
+ }else
+ if(m_fTargetOffset >= 2.0f)
+ m_fTargetOffset -= 2.0f;
+
+ if(m_heliType == HELI_TYPE_CATALINA)
+ if(m_pathState == 9 || m_pathState == 11 || m_pathState == 10){
+ float f = Pow(0.997f, CTimer::GetTimeStep());
+ m_vecMoveSpeed.x *= f;
+ m_vecMoveSpeed.y *= f;
+ }
+
+ CVector2D speedDir = targetSpeed - m_vecMoveSpeed;
+ float speedDiff = speedDir.Magnitude();
+ if(speedDiff != 0.0f)
+ speedDir /= speedDiff;
+ else
+ speedDir.x = 1.0f;
+ float speedInc = CTimer::GetTimeStep()*0.002f;
+ if(speedDiff < speedInc){
+ m_vecMoveSpeed.x = targetSpeed.x;
+ m_vecMoveSpeed.y = targetSpeed.y;
+ }else{
+ m_vecMoveSpeed.x += speedDir.x*speedInc;
+ m_vecMoveSpeed.y += speedDir.y*speedInc;
+ }
+ GetPosition().x += m_vecMoveSpeed.x*CTimer::GetTimeStep();
+ GetPosition().y += m_vecMoveSpeed.y*CTimer::GetTimeStep();
+
+ // Find z target
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY)
+ m_fTargetZ = 1000.0f;
+ if((CTimer::GetTimeInMilliseconds() + 800*m_nHeliId) & 0x800)
+ m_fTargetZ += 2.0f;
+ m_fTargetZ += m_nHeliId*5.0f;
+
+ // Find z speed
+ float targetSpeedZ = (m_fTargetZ - GetPosition().z)*0.01f;
+ float speedDiffZ = targetSpeedZ - m_vecMoveSpeed.z;
+ float speedIncZ = CTimer::GetTimeStep()*0.001f;
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY)
+ speedIncZ *= 1.5f;
+ if(Abs(speedDiffZ) < speedIncZ)
+ m_vecMoveSpeed.z = targetSpeedZ;
+ else if(speedDiffZ < 0.0f)
+ m_vecMoveSpeed.z -= speedIncZ;
+ else
+ m_vecMoveSpeed.z += speedIncZ*1.5f;
+ GetPosition().z += m_vecMoveSpeed.z*CTimer::GetTimeStep();
+
+ // Find angular speed
+ float targetAngularSpeed;
+ m_fAngularSpeed *= Pow(0.995f, CTimer::GetTimeStep());
+ if(fTargetDist < 8.0f)
+ targetAngularSpeed = 0.0f;
+ else{
+ float rotationDiff = CGeneral::GetATanOfXY(vTargetDist.x, vTargetDist.y) - m_fRotation;
+ while(rotationDiff < -3.14f) rotationDiff += 6.28f;
+ while(rotationDiff > 3.14f) rotationDiff -= 6.28f;
+ if(Abs(rotationDiff) > 0.4f){
+ if(rotationDiff < 0.0f)
+ targetAngularSpeed = -0.2f;
+ else
+ targetAngularSpeed = 0.2f;
+ }else
+ targetAngularSpeed = 0.0f;
+ }
+ float angularSpeedDiff = targetAngularSpeed - m_fAngularSpeed;
+ float angularSpeedInc = CTimer::GetTimeStep()*0.0001f;
+ if(Abs(angularSpeedDiff) < angularSpeedInc)
+ m_fAngularSpeed = targetAngularSpeed;
+ else if(angularSpeedDiff < 0.0f)
+ m_fAngularSpeed -= angularSpeedInc;
+ else
+ m_fAngularSpeed += angularSpeedInc;
+ m_fRotation += m_fAngularSpeed * CTimer::GetTimeStep();
+
+ // Set matrix
+ CVector up(3.0f*m_vecMoveSpeed.x, 3.0f*m_vecMoveSpeed.y, 1.0f);
+ up.Normalise();
+ CVector fwd(-Cos(m_fRotation), -Sin(m_fRotation), 0.0f); // not really forward
+ CVector right = CrossProduct(up, fwd);
+ fwd = CrossProduct(up, right);
+ GetRight() = right;
+ GetForward() = fwd;
+ GetUp() = up;
+
+ // Search light and shooting
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY || m_heliType == HELI_TYPE_CATALINA || CCullZones::PlayerNoRain())
+ m_fSearchLightIntensity = 0.0f;
+ else{
+ // Update search light history once every 1000ms
+ int timeDiff = CTimer::GetTimeInMilliseconds() - m_nSearchLightTimer;
+ while(timeDiff > 1000){
+ for(i = 5; i > 0; i--){
+ m_aSearchLightHistoryX[i] = m_aSearchLightHistoryX[i-1];
+ m_aSearchLightHistoryY[i] = m_aSearchLightHistoryY[i-1];
+ }
+ m_aSearchLightHistoryX[0] = FindPlayerCoors().x + FindPlayerSpeed().x*50.0f*(m_nHeliId+2);
+ m_aSearchLightHistoryY[0] = FindPlayerCoors().y + FindPlayerSpeed().y*50.0f*(m_nHeliId+2);
+
+ timeDiff -= 1000;
+ m_nSearchLightTimer += 1000;
+ }
+ assert(timeDiff <= 1000);
+ float f1 = timeDiff/1000.0f;
+ float f2 = 1.0f - f1;
+ m_fSearchLightX = m_aSearchLightHistoryX[m_nHeliId+2]*f2 + m_aSearchLightHistoryX[m_nHeliId+2-1]*f1;
+ m_fSearchLightY = m_aSearchLightHistoryY[m_nHeliId+2]*f2 + m_aSearchLightHistoryY[m_nHeliId+2-1]*f1;
+
+ float searchLightDist = (CVector2D(m_fSearchLightX, m_fSearchLightY) - GetPosition()).Magnitude();
+ if(searchLightDist > 60.0f)
+ m_fSearchLightIntensity = 0.0f;
+ else if(searchLightDist < 40.0f)
+ m_fSearchLightIntensity = 1.0f;
+ else
+ m_fSearchLightIntensity = 1.0f - (40.0f-searchLightDist)/40.0f;
+
+ if(m_fSearchLightIntensity < 0.9f || sq(FindPlayerCoors().x-m_fSearchLightX) + sq(FindPlayerCoors().y-m_fSearchLightY) > sq(7.0f))
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ else if(CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_HELI_PLAYER_FOUND, 0.0f);
+ m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF);
+ }
+
+ // Shoot
+ int shootTimeout;
+ if(m_heliType == HELI_TYPE_RANDOM){
+ switch(FindPlayerPed()->m_pWanted->m_nWantedLevel){
+ case 0:
+ case 1:
+ case 2: shootTimeout = 999999; break;
+ case 3: shootTimeout = 10000; break;
+ case 4: shootTimeout = 5000; break;
+ case 5: shootTimeout = 3500; break;
+ case 6: shootTimeout = 2000; break;
+ }
+ if(CCullZones::NoPolice())
+ shootTimeout /= 2;
+ }else
+ shootTimeout = 1500;
+
+ if(FindPlayerPed()->m_pWanted->IsIgnored())
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ else{
+ // Check if line of sight is clear
+ if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout &&
+ CTimer::GetPreviousTimeInMilliseconds() <= m_nShootTimer + shootTimeout){
+ if(CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)){
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_heliStatus = HELI_STATUS_HOVER;
+ }else{
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ if(m_heliStatus == HELI_STATUS_HOVER)
+ m_heliStatus = HELI_STATUS_HOVER2;
+ }
+ }
+
+ // Shoot!
+ if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout &&
+ CTimer::GetTimeInMilliseconds() > m_nLastShotTime + 200){
+ CVector shotTarget = FindPlayerCoors();
+ // some inaccuracy
+ shotTarget.x += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f;
+ shotTarget.y += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f;
+ CVector direction = FindPlayerCoors() - GetPosition();
+ direction.Normalise();
+ shotTarget += 3.0f*direction;
+ CVector shotSource = GetPosition();
+ shotSource += 3.0f*direction;
+ FireOneInstantHitRound(&shotSource, &shotTarget, 20);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
+ m_nLastShotTime = CTimer::GetTimeInMilliseconds();
+ }
+ }
+ }
+
+ // Drop Catalina's bombs
+ if(m_heliType == HELI_TYPE_CATALINA && m_pathState > 8 && (CTimer::GetTimeInMilliseconds()>>9) != (CTimer::GetPreviousTimeInMilliseconds()>>9)){
+ CVector bombPos = GetPosition() - 60.0f*m_vecMoveSpeed;
+ if(sq(FindPlayerCoors().x-bombPos.x) + sq(FindPlayerCoors().y-bombPos.y) < sq(35.0f)){
+ bool found;
+ float groundZ = CWorld::FindGroundZFor3DCoord(bombPos.x, bombPos.y, bombPos.z, &found);
+ float waterZ;
+ if(!CWaterLevel::GetWaterLevelNoWaves(bombPos.x, bombPos.y, bombPos.z, &waterZ))
+ waterZ = 0.0f;
+ if(groundZ > waterZ){
+ bombPos.z = groundZ + 2.0f;
+ CExplosion::AddExplosion(nil, this, EXPLOSION_HELI_BOMB, bombPos, 0);
+ }else{
+ bombPos.z = waterZ;
+ CVector dir;
+ for(i = 0; i < 16; i++){
+ dir.x = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f;
+ dir.y = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f;
+ dir.z = 0.5f;
+ CParticle::AddParticle(PARTICLE_BOAT_SPLASH, bombPos, dir, nil, 0.2f);
+ }
+ }
+ }
+ }
+
+ RemoveAndAdd();
+ bIsInSafePosition = true;
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+}
+
+void
+CHeli::PreRender(void)
+{
+ float angle;
+ uint8 i;
+ CColPoint point;
+ CEntity *entity;
+ uint8 r, g, b;
+ float testLowZ = FindPlayerCoors().z - 10.0f;
+ float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f;
+ int frm = CTimer::GetFrameCounter() & 7;
+
+ i = 0;
+ for(angle = 0.0f; angle < TWOPI; angle += TWOPI/32){
+ CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f);
+ CVector dir = pos*0.01f;
+ pos += GetPosition();
+
+ if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil))
+ m_fHeliDustZ[frm] = point.point.z;
+ else
+ m_fHeliDustZ[frm] = -101.0f;
+
+ switch(point.surfaceB){
+ default:
+ case SURFACE_TARMAC:
+ r = 10;
+ g = 10;
+ b = 10;
+ break;
+ case SURFACE_GRASS:
+ r = 10;
+ g = 6;
+ b = 3;
+ break;
+ case SURFACE_DIRT:
+ r = 10;
+ g = 8;
+ b = 7;
+ break;
+ case SURFACE_DIRTTRACK:
+ r = 10;
+ g = 6;
+ b = 3;
+ break;
+ }
+ RwRGBA col = { r, g, b, 32 };
+ pos.z = m_fHeliDustZ[(i - (i&3))/4]; // advance every 4 iterations, why not just /4?
+ if(pos.z > -200.0f && GetPosition().z - pos.z < 20.0f)
+ CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, 0.0f, col);
+ i++;
+ }
+}
+
+void
+CHeli::Render(void)
+{
+ CMatrix mat;
+ CVector pos;
+
+ mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_TOPROTOR]));
+ pos = mat.GetPosition();
+ mat.SetRotateZ(m_fRotorRotation);
+ mat.Translate(pos);
+ mat.UpdateRW();
+
+ m_fRotorRotation += 3.14f/6.5f;
+ if(m_fRotorRotation > 6.28f)
+ m_fRotorRotation -= 6.28f;
+
+ mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_BACKROTOR]));
+ pos = mat.GetPosition();
+ mat.SetRotateX(m_fRotorRotation);
+ mat.Translate(pos);
+ mat.UpdateRW();
+
+ CEntity::Render();
+}
+
+void
+CHeli::PreRenderAlways(void)
+{
+ CVector shadowPos(m_fSearchLightX, m_fSearchLightY, GetPosition().z);
+ if(m_fSearchLightIntensity > 0.0f){
+ CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &shadowPos,
+ 6.0f, 0.0f, 0.0f, -6.0f,
+ 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity,
+ 50.0f, true, 1.0f);
+
+ CVector front = GetMatrix() * CVector(0.0f, 7.0f, 0.0f);
+ CVector toPlayer = FindPlayerCoors() - front;
+ toPlayer.Normalise();
+ float intensity = m_fSearchLightIntensity*sq(CTimeCycle::GetSpriteBrightness());
+ if(DotProduct(toPlayer, TheCamera.GetForward()) < -0.8f)
+ CCoronas::RegisterCorona((uintptr)this, 255*intensity, 255*intensity, 255*intensity, 255,
+ front, 10.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ else
+ CCoronas::RegisterCorona((uintptr)this, 200*intensity, 200*intensity, 200*intensity, 255,
+ front, 8.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ }
+
+ CVector back = GetMatrix() * CVector(0.0f, -9.0f, 0.0f);
+ if(CTimer::GetTimeInMilliseconds() & 0x100)
+ CCoronas::RegisterCorona((uintptr)this + 2, 255, 0, 0, 255,
+ back, 1.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ else
+ CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 255,
+ back, 1.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+}
+
+RwObject*
+GetHeliAtomicObjectCB(RwObject *object, void *data)
+{
+ RpAtomic *atomic = (RpAtomic*)object;
+ assert(RwObjectGetType(object) == rpATOMIC);
+ if(RpAtomicGetFlags(atomic) & rpATOMICRENDER)
+ *(RpAtomic**)data = atomic;
+ return object;
+}
+
+CObject*
+CHeli::SpawnFlyingComponent(int32 component)
+{
+ RpAtomic *atomic;
+ RwFrame *frame;
+ RwMatrix *matrix;
+ CObject *obj;
+
+ if(m_aHeliNodes[component] == nil)
+ return nil;
+
+ atomic = nil;
+ RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic);
+ if(atomic == nil)
+ return nil;
+
+ obj = new CObject;
+ if(obj == nil)
+ return nil;
+
+ obj->SetModelIndexNoCreate(MI_CAR_WHEEL);
+ // object needs base model
+ obj->RefModelInfo(GetModelIndex());
+
+ // create new atomic
+ matrix = RwFrameGetLTM(m_aHeliNodes[component]);
+ frame = RwFrameCreate();
+ atomic = RpAtomicClone(atomic);
+ *RwFrameGetMatrix(frame) = *matrix;
+ RpAtomicSetFrame(atomic, frame);
+ CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
+ obj->AttachToRwObject((RwObject*)atomic);
+
+ // init object
+ obj->m_fMass = 10.0f;
+ obj->m_fTurnMass = 25.0f;
+ obj->m_fAirResistance = 0.99f;
+ obj->m_fElasticity = 0.1f;
+ obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f;
+ obj->ObjectCreatedBy = TEMP_OBJECT;
+ obj->bIsStatic = false;
+ obj->bIsPickup = false;
+
+ // life time
+ CObject::nNoTempObjects++;
+ if(component == HELI_TOPROTOR)
+ obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 1000;
+ else
+ obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 3000;
+
+ obj->m_vecMoveSpeed = m_vecMoveSpeed;
+ if(obj->m_vecMoveSpeed.z > 0.0f)
+ obj->m_vecMoveSpeed.z = 0.3f;
+ else
+ obj->m_vecMoveSpeed.z = 0.0f;
+
+ obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f;
+
+ if(component == HELI_BACKROTOR)
+ obj->m_vecTurnSpeed.x = 0.5f;
+ else if(component == HELI_TOPROTOR || component == HELI_TOPKNOT)
+ obj->m_vecTurnSpeed.z = 0.5f;
+ else
+ obj->m_vecTurnSpeed.y = 0.5f;
+
+ obj->bRenderScorched = true;
+
+ CWorld::Add(obj);
+
+ atomic = nil;
+ RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic);
+ if(atomic)
+ RpAtomicSetFlags(atomic, 0);
+
+ return obj;
}
-WRAPPER CHeli* CHeli::ctor(int, uint8) { EAXJMP(0x547220); }
-WRAPPER void CHeli::SpecialHeliPreRender(void) { EAXJMP(0x54AE10); }
+
+
+void
+CHeli::InitHelis(void)
+{
+ int i;
+
+ NumRandomHelis = 0;
+ TestForNewRandomHelisTimer = 0;
+ NumScriptHelis = 0;
+ CatalinaHeliOn = false;
+ ScriptHeliOn = false;
+ for(i = 0; i < NUM_HELIS; i++)
+ pHelis[i] = nil;
+
+ ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_ESCAPE))->SetColModel(&CTempColModels::ms_colModelPed1);
+ ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1);
+}
+
+CHeli*
+GenerateHeli(bool catalina)
+{
+ CHeli *heli;
+ CVector heliPos;
+ int i;
+
+ if(catalina)
+ heli = new CHeli(MI_ESCAPE, PERMANENT_VEHICLE);
+ else
+ heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE);
+
+ if(catalina)
+ heliPos = CVector(-224.0f, 201.0f, 83.0f);
+ else{
+ heliPos = FindPlayerCoors();
+ float angle = (float)(CGeneral::GetRandomNumber() & 0xFF)/0xFF * 6.28f;
+ heliPos.x += 250.0f*Sin(angle);
+ heliPos.y += 250.0f*Cos(angle);
+ if(heliPos.x < -2000.0f || heliPos.x > 2000.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){
+ // directly above player
+ heliPos.x -= 250.0f*Sin(angle);
+ heliPos.y -= 250.0f*Cos(angle);
+ }
+ heliPos.z += 50.0f;
+ }
+ heli->GetMatrix().SetTranslate(heliPos);
+ if(catalina)
+ heli->GetMatrix().SetRotateZOnly(DEGTORAD(270.0f)); // game actually uses 3.14 here
+
+ heli->m_status = STATUS_ABANDONED;
+
+ int id = -1;
+ bool found = false;
+ while(!found){
+ id++;
+ found = true;
+ for(i = 0; i < 4; i++)
+ if(CHeli::pHelis[i] && CHeli::pHelis[i]->m_nHeliId == id)
+ found = false;
+ }
+ heli->m_nHeliId = id;
+
+ CWorld::Add(heli);
+
+ return heli;
+}
+
+void
+CHeli::UpdateHelis(void)
+{
+ int i, j;
+
+ // Spawn new police helis
+ int numHelisRequired = FindPlayerPed()->m_pWanted->NumOfHelisRequired();
+ if(CStreaming::HasModelLoaded(MI_CHOPPER) && CTimer::GetTimeInMilliseconds() > TestForNewRandomHelisTimer){
+ // Spawn a police heli
+ TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 15000;
+ if(NumRandomHelis < numHelisRequired){
+ NumRandomHelis++;
+ CHeli *heli = GenerateHeli(false);
+ heli->m_heliType = HELI_TYPE_RANDOM;
+ if(pHelis[HELI_RANDOM0] == nil)
+ pHelis[HELI_RANDOM0] = heli;
+ else if(pHelis[HELI_RANDOM1] == nil)
+ pHelis[HELI_RANDOM1] = heli;
+ else
+ assert(0 && "too many helis");
+ }
+ }
+
+ // Handle script heli
+ if(ScriptHeliOn){
+ if(CStreaming::HasModelLoaded(MI_CHOPPER) && pHelis[HELI_SCRIPT] == nil){
+ pHelis[HELI_SCRIPT] = GenerateHeli(false);
+ pHelis[HELI_SCRIPT]->m_heliType = HELI_TYPE_SCRIPT;
+ }else
+ CStreaming::RequestModel(MI_CHOPPER, 0);
+ }else{
+ if(pHelis[HELI_SCRIPT])
+ pHelis[HELI_SCRIPT]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Handle Catalina's heli
+ if(CatalinaHeliOn){
+ if(CStreaming::HasModelLoaded(MI_ESCAPE) && pHelis[HELI_CATALINA] == nil){
+ pHelis[HELI_CATALINA] = GenerateHeli(true);
+ pHelis[HELI_CATALINA]->m_heliType = HELI_TYPE_CATALINA;
+ }else
+ CStreaming::RequestModel(MI_ESCAPE, STREAMFLAGS_DONT_REMOVE);
+ }else{
+ if(pHelis[HELI_CATALINA])
+ pHelis[HELI_CATALINA]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Delete helis that we no longer need
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_FLY_AWAY && pHelis[i]->GetPosition().z > 150.0f){
+ CWorld::Remove(pHelis[i]);
+ delete pHelis[i];
+ pHelis[i] = nil;
+ if(i != HELI_SCRIPT && i != HELI_CATALINA)
+ NumRandomHelis--;
+ }
+
+ // Handle explosions
+ for(i = 0; i < NUM_HELIS; i++){
+ if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds() > pHelis[i]->m_nExplosionTimer){
+ // Second part of explosion
+ static int nFrameGen;
+ CRGBA colors[8];
+
+ TheCamera.CamShake(0.7f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z);
+
+ colors[0] = CRGBA(0, 0, 0, 255);
+ colors[1] = CRGBA(224, 230, 238, 255);
+ colors[2] = CRGBA(0, 0, 0, 255);
+ colors[3] = CRGBA(0, 0, 0, 255);
+ colors[4] = CRGBA(66, 162, 252, 255);
+ colors[5] = CRGBA(0, 0, 0, 255);
+ colors[6] = CRGBA(0, 0, 0, 255);
+ colors[7] = CRGBA(0, 0, 0, 255);
+
+ CVector pos = pHelis[i]->GetPosition();
+ CVector dir;
+ for(j = 0; j < 40; j++){
+ dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
+ dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
+ dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f);
+ int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30);
+ if(CGeneral::GetRandomNumber() & 1)
+ rotSpeed = -rotSpeed;
+ int f = ++nFrameGen & 3;
+ CParticle::AddParticle(PARTICLE_HELI_DEBRIS, pos, dir,
+ nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
+ colors[nFrameGen], rotSpeed, 0, f, 0);
+ }
+
+ CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0);
+
+ pHelis[i]->SpawnFlyingComponent(HELI_SKID_LEFT);
+ pHelis[i]->SpawnFlyingComponent(HELI_SKID_RIGHT);
+ pHelis[i]->SpawnFlyingComponent(HELI_TOPROTOR);
+
+ CDarkel::RegisterCarBlownUpByPlayer(pHelis[i]);
+ CWorld::Remove(pHelis[i]);
+ delete pHelis[i];
+ pHelis[i] = nil;
+ if(i != HELI_SCRIPT && i != HELI_CATALINA)
+ NumRandomHelis--;
+ if(i == HELI_CATALINA)
+ CatalinaHasBeenShotDown = true;
+
+ CStats::HelisDestroyed++;
+ CStats::PeopleKilledByOthers += 2;
+ CStats::PedsKilledOfThisType[PEDTYPE_COP] += 2;
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250;
+ pos = CWorld::Players[CWorld::PlayerInFocus].m_pPed->GetPosition();
+ CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_SHOOT_HELI,
+ pos, i + 19843, false);
+
+ TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 50000;
+ }else if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds()+7000 > pHelis[i]->m_nExplosionTimer){
+ // First part of explosion
+ if(CTimer::GetPreviousTimeInMilliseconds()+7000 < pHelis[i]->m_nExplosionTimer){
+ pHelis[i]->SpawnFlyingComponent(HELI_BACKROTOR);
+ pHelis[i]->SpawnFlyingComponent(HELI_TAIL);
+ pHelis[i]->m_fAngularSpeed *= -2.5f;
+ pHelis[i]->bRenderScorched = true;
+
+ TheCamera.CamShake(0.4f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z);
+
+ CVector pos = pHelis[i]->GetPosition() - 2.5f*pHelis[i]->GetUp();
+ CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0);
+ }else
+ pHelis[i]->m_fAngularSpeed *= 1.03f;
+ }
+ }
+
+ // Find police helis to remove
+ for(i = 0; i < 2; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_FLY_AWAY){
+ if(numHelisRequired > 0)
+ numHelisRequired--;
+ else
+ pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Remove all helis if in a tunnel
+ if(FindPlayerCoors().z < - 2.0f)
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_SHOT_DOWN)
+ pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+}
+
+void
+CHeli::SpecialHeliPreRender(void)
+{
+ int i;
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i])
+ pHelis[i]->PreRenderAlways();
+}
+
+bool
+CHeli::TestRocketCollision(CVector *rocketPos)
+{
+ int i;
+ bool hit = false;
+
+ for(i = 0; i < NUM_HELIS; i++){
+ if(pHelis[i] && !pHelis[i]->bExplosionProof && (*rocketPos - pHelis[i]->GetPosition()).MagnitudeSqr() < sq(8.0f)){
+ pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f;
+ pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
+ pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000;
+ hit = true;
+ }
+ }
+ return hit;
+}
+
+bool
+CHeli::TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage)
+{
+ int i;
+ bool hit = false;
+
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pHelis[i]->GetPosition()) < 5.0f){
+ // Find bullet position
+ float distToHeli = (pHelis[i]->GetPosition() - *line0).Magnitude();
+ CVector line = (*line1 - *line0);
+ float lineLength = line.Magnitude();
+ *bulletPos = *line0 + line*max(1.0f, distToHeli-5.0f);
+
+ pHelis[i]->m_nBulletDamage += damage;
+
+ if(pHelis[i]->m_heliType == HELI_CATALINA && pHelis[i]->m_nBulletDamage > 400 ||
+ pHelis[i]->m_heliType != HELI_CATALINA && pHelis[i]->m_nBulletDamage > 700){
+ pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f;
+ pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
+ pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000;
+ }
+
+ hit = true;
+ }
+ return hit;
+}
+
+void CHeli::StartCatalinaFlyBy(void)
+{
+ CatalinaHeliOn = true;
+ CatalinaHasBeenShotDown = false;
+}
+
+void
+CHeli::RemoveCatalinaHeli(void)
+{
+ CatalinaHeliOn = false;
+ if(pHelis[HELI_CATALINA]){
+ CWorld::Remove(pHelis[HELI_CATALINA]);
+ delete pHelis[HELI_CATALINA];
+ pHelis[HELI_CATALINA] = nil;
+ }
+}
+
+CHeli *CHeli::FindPointerToCatalinasHeli(void) { return pHelis[HELI_CATALINA]; }
+void CHeli::CatalinaTakeOff(void) { pHelis[HELI_CATALINA]->m_pathState = 8; }
+void CHeli::MakeCatalinaHeliFlyAway(void) { pHelis[HELI_CATALINA]->m_pathState = 12; }
+bool CHeli::HasCatalinaBeenShotDown(void) { return CatalinaHasBeenShotDown; }
+
+void CHeli::ActivateHeli(bool activate) { ScriptHeliOn = activate; }
+
class CHeli_ : public CHeli
{
public:
+ void ctor(int32 id, uint8 CreatedBy) { ::new (this) CHeli(id, CreatedBy); }
void dtor(void) { CHeli::~CHeli(); }
};
STARTPATCHES
+ InjectHook(0x547220, &CHeli_::ctor, PATCH_JUMP);
InjectHook(0x5474A0, &CHeli_::dtor, PATCH_JUMP);
+ InjectHook(0x54AE50, &CHeli::SpawnFlyingComponent, PATCH_JUMP);
+ InjectHook(0x549970, CHeli::InitHelis, PATCH_JUMP);
+ InjectHook(0x5499F0, CHeli::UpdateHelis, PATCH_JUMP);
+ InjectHook(0x54AE10, CHeli::SpecialHeliPreRender, PATCH_JUMP);
+ InjectHook(0x54AA30, CHeli::TestRocketCollision, PATCH_JUMP);
+ InjectHook(0x54AB30, CHeli::TestBulletCollision, PATCH_JUMP);
+ InjectHook(0x54A640, GenerateHeli, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Heli.h b/src/vehicles/Heli.h
index db873ae2..bb10345d 100644
--- a/src/vehicles/Heli.h
+++ b/src/vehicles/Heli.h
@@ -2,15 +2,98 @@
#include "Vehicle.h"
+class CObject;
+
+enum eHeliNodes
+{
+ HELI_CHASSIS = 1,
+ HELI_TOPROTOR,
+ HELI_BACKROTOR,
+ HELI_TAIL,
+ HELI_TOPKNOT,
+ HELI_SKID_LEFT,
+ HELI_SKID_RIGHT,
+ NUM_HELI_NODES
+};
+
+enum
+{
+ HELI_RANDOM0,
+ HELI_RANDOM1,
+ HELI_SCRIPT,
+ HELI_CATALINA,
+ NUM_HELIS
+};
+
+enum
+{
+ HELI_TYPE_RANDOM,
+ HELI_TYPE_SCRIPT,
+ HELI_TYPE_CATALINA,
+};
+
+
class CHeli : public CVehicle
{
public:
// 0x288
- uint8 stuff[180];
+ RwFrame *m_aHeliNodes[NUM_HELI_NODES];
+ int8 m_heliStatus;
+ float m_fSearchLightX;
+ float m_fSearchLightY;
+ uint32 m_nExplosionTimer;
+ float m_fRotation;
+ float m_fAngularSpeed;
+ float m_fTargetZ;
+ float m_fSearchLightIntensity;
+ int8 m_nHeliId;
+ int8 m_heliType;
+ int8 m_pathState;
+ float m_aSearchLightHistoryX[6];
+ float m_aSearchLightHistoryY[6];
+ uint32 m_nSearchLightTimer;
+ uint32 m_nShootTimer;
+ uint32 m_nLastShotTime;
+ uint32 m_nBulletDamage;
+ float m_fRotorRotation;
+ float m_fHeliDustZ[8];
+ uint32 m_nPoliceShoutTimer;
+ float m_fTargetOffset;
+ bool m_bTestRight;
+
+ static CHeli **pHelis; //[NUM_HELIS]
+ static int16 &NumRandomHelis;
+ static uint32 &TestForNewRandomHelisTimer;
+ static int16 NumScriptHelis; // unused
+ static bool &CatalinaHeliOn;
+ static bool &CatalinaHasBeenShotDown;
+ static bool &ScriptHeliOn;
- CHeli(int, uint8);
+ CHeli(int32 id, uint8 CreatedBy);
CHeli* ctor(int, uint8);
+ // from CEntity
+ void SetModelIndex(uint32 id);
+ void ProcessControl(void);
+ void PreRender(void);
+ void Render(void);
+
+ void PreRenderAlways(void);
+ CObject *SpawnFlyingComponent(int32 component);
+
+ static void InitHelis(void);
+ static void UpdateHelis(void);
static void SpecialHeliPreRender(void);
+ static bool TestRocketCollision(CVector *coors);
+ static bool TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage);
+
+ static void StartCatalinaFlyBy(void);
+ static void RemoveCatalinaHeli(void);
+ static CHeli *FindPointerToCatalinasHeli(void);
+ static void CatalinaTakeOff(void);
+ static void MakeCatalinaHeliFlyAway(void);
+ static bool HasCatalinaBeenShotDown(void);
+
+ static void ActivateHeli(bool activate);
};
static_assert(sizeof(CHeli) == 0x33C, "CHeli: error");
diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp
index 8e0e313d..775cf572 100644
--- a/src/vehicles/Plane.cpp
+++ b/src/vehicles/Plane.cpp
@@ -137,15 +137,16 @@ CPlane::ProcessControl(void)
colors[6] = CRGBA(0, 0, 0, 255);
colors[7] = CRGBA(224, 230, 238, 255);
+ CVector dir;
for(i = 0; i < 40; i++){
- int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f);
+ dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
+ dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
+ dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f);
+ int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30);
if(CGeneral::GetRandomNumber() & 1)
rotSpeed = -rotSpeed;
int f = ++nFrameGen & 3;
- CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f),
- CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
- CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
- CGeneral::GetRandomNumberInRange(0.0f, 2.0f)),
+ CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir,
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
colors[nFrameGen], rotSpeed, 0, f, 0);
}
diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp
index dada1b21..4c07a63a 100644
--- a/src/vehicles/Train.cpp
+++ b/src/vehicles/Train.cpp
@@ -67,7 +67,7 @@ CTrain::SetModelIndex(uint32 id)
int i;
CVehicle::SetModelIndex(id);
- for(i = 0; i < 3; i++)
+ for(i = 0; i < NUM_TRAIN_NODES; i++)
m_aTrainNodes[i] = nil;
CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes);
}
diff --git a/src/vehicles/Train.h b/src/vehicles/Train.h
index 84b53537..bf541250 100644
--- a/src/vehicles/Train.h
+++ b/src/vehicles/Train.h
@@ -20,7 +20,8 @@ enum
enum eTrainNodes
{
TRAIN_DOOR_LHS = 1,
- TRAIN_DOOR_RHS
+ TRAIN_DOOR_RHS,
+ NUM_TRAIN_NODES
};
enum eTrainPositions
@@ -66,7 +67,7 @@ public:
uint32 m_nDoorTimer;
int16 m_nDoorState;
CTrainDoor Doors[2];
- RwFrame *m_aTrainNodes[3];
+ RwFrame *m_aTrainNodes[NUM_TRAIN_NODES];
// unused
static CVector aStationCoors[3];
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 6f7dd7d5..775689f7 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -30,6 +30,10 @@ void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehicleP
void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
void CVehicle::operator delete(void *p, int handle) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
+WRAPPER bool CVehicle::ShufflePassengersToMakeSpace(void) { EAXJMP(0x5528A0); }
+// or Weapon.cpp?
+WRAPPER void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage) { EAXJMP(0x563B00); }
+
CVehicle::CVehicle(uint8 CreatedBy)
{
int i;
@@ -57,7 +61,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
pPassengers[i] = nil;
m_nBombTimer = 0;
m_pBlowUpEntity = nil;
- field_1FB = 0;
+ m_nPacManPickupsCarried = 0;
bComedyControls = false;
bCraneMessageDone = false;
bExtendedRange = false;
@@ -68,7 +72,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
m_nTimeOfDeath = 0;
m_pCarFire = nil;
bHasBeenOwnedByPlayer = false;
- m_veh_flagC20 = false;
+ bCreateRoadBlockPeds = false;
bCanBeDamaged = true;
bUsingSpecialColModel = false;
m_veh_flagD1 = false;
@@ -99,7 +103,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
m_aCollPolys[0].valid = false;
m_aCollPolys[1].valid = false;
AutoPilot.m_nCarMission = MISSION_NONE;
- AutoPilot.m_nAnimationId = TEMPACT_NONE;
+ AutoPilot.m_nTempAction = TEMPACT_NONE;
AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds();
AutoPilot.m_flag4 = false;
AutoPilot.m_flag10 = false;
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 07893782..357e5603 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -106,6 +106,9 @@ enum eFlightModel
FLIGHT_MODEL_SEAPLANE
};
+// Or Weapon.h?
+void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage);
+
class CVehicle : public CPhysical
{
public:
@@ -156,7 +159,7 @@ public:
uint8 bHasBeenOwnedByPlayer : 1;// To work out whether stealing it is a crime
uint8 bFadeOut : 1; // Fade vehicle out
uint8 m_veh_flagC10 : 1;
- uint8 m_veh_flagC20 : 1;
+ uint8 bCreateRoadBlockPeds : 1; // If this vehicle gets close enough we will create peds (coppers or gang members) round it
uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions
uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure
@@ -171,8 +174,9 @@ public:
int8 m_numPedsUseItAsCover;
uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default)
- int8 field_1FB;
- int8 field_1FC[4];
+ int8 m_nPacManPickupsCarried;
+ uint8 m_nRoadblockType;
+ int16 m_nRoadblockNode;
float m_fHealth; // 1000.0f = full health. 250.0f = fire. 0 -> explode
uint8 m_nCurrentGear;
int8 field_205[3];
@@ -262,6 +266,7 @@ public:
void RemoveDriver(void);
void ProcessCarAlarm(void);
bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius);
+ bool ShufflePassengersToMakeSpace(void);
bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; }