|
|
@@ -5,16 +5,17 @@ |
|
|
#include "FileMgr.h" |
|
|
#include "Pad.h" |
|
|
#include "Pools.h" |
|
|
#include "Streaming.h" |
|
|
#include "Timer.h" |
|
|
#include "VehicleModelInfo.h" |
|
|
#include "World.h" |
|
|
|
|
|
uint16 &CRecordDataForGame::RecordingState = *(uint16*)0x95CC24; |
|
|
uint8*& CRecordDataForGame::pDataBuffer = *(uint8**)0x8F1B70; |
|
|
uint8*& CRecordDataForGame::pDataBufferPointer = *(uint8**)0x8F1AB0; |
|
|
int& CRecordDataForGame::FId = *(int*)0x885BA4; |
|
|
tGameBuffer& CRecordDataForGame::pDataBufferForFrame = *(tGameBuffer*)0x72CED0; |
|
|
|
|
|
uint8 &CRecordDataForChase::Status = *(uint8*)0x95CDCE; |
|
|
|
|
|
#define MEMORY_FOR_GAME_RECORD (150000) |
|
|
|
|
|
void CRecordDataForGame::Init(void) |
|
|
@@ -176,16 +177,348 @@ uint16 CRecordDataForGame::CalcGameChecksum(void) |
|
|
return checksum ^ checksum >> 16; |
|
|
} |
|
|
|
|
|
WRAPPER void CRecordDataForChase::Init(void) |
|
|
uint8& CRecordDataForChase::Status = *(uint8*)0x95CDCE; |
|
|
int& CRecordDataForChase::PositionChanges = *(int*)0x8F59C8; |
|
|
uint8& CRecordDataForChase::CurrentCar = *(uint8*)0x95CDC9; |
|
|
CAutomobile* (&CRecordDataForChase::pChaseCars)[NUM_CHASE_CARS] = *(CAutomobile * (*)[NUM_CHASE_CARS])*(uintptr*)0x6F46A8; |
|
|
uint32& CRecordDataForChase::AnimStartTime = *(uint32*)0x8F1AEC; |
|
|
float& CRecordDataForChase::AnimTime = *(float*)0x880F88; |
|
|
CCarStateEachFrame* (&CRecordDataForChase::pBaseMemForCar)[NUM_CHASE_CARS] = *(CCarStateEachFrame * (*)[NUM_CHASE_CARS])*(uintptr*)0x70EA18; |
|
|
float& CRecordDataForChase::TimeMultiplier = *(float*)0x8E2A94; |
|
|
int& CRecordDataForChase::FId2 = *(int*)0x8E2C18; |
|
|
|
|
|
#define CHASE_SCENE_LENGTH_IN_SECONDS (80) |
|
|
#define CHASE_SCENE_FRAMES_PER_SECOND (15) // skipping every second frame |
|
|
#define CHASE_SCENE_FRAMES_IN_RECORDING (CHASE_SCENE_LENGTH_IN_SECONDS * CHASE_SCENE_FRAMES_PER_SECOND) |
|
|
#define CHASE_SCENE_LENGTH_IN_FRAMES (CHASE_SCENE_FRAMES_IN_RECORDING * 2) |
|
|
|
|
|
void CRecordDataForChase::Init(void) |
|
|
{ |
|
|
Status = STATE_NONE; |
|
|
PositionChanges = 0; |
|
|
CurrentCar = 0; |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) |
|
|
pChaseCars[i] = nil; |
|
|
AnimStartTime = 0; |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) |
|
|
{ |
|
|
switch (Status) { |
|
|
case STATE_NONE: |
|
|
return; |
|
|
case STATE_RECORD: |
|
|
{ |
|
|
if ((CTimer::GetFrameCounter() & 1) == 0) |
|
|
StoreInfoForCar(pChaseCars[CurrentCar], &pBaseMemForCar[CurrentCar][CTimer::GetFrameCounter() / 2]); |
|
|
if (CTimer::GetFrameCounter() < CHASE_SCENE_LENGTH_IN_FRAMES * 2) |
|
|
return; |
|
|
CFileMgr::SetDir("data\\paths"); |
|
|
sprintf(gString, "chase%d.dat", CurrentCar); |
|
|
int fid = CFileMgr::OpenFileForWriting(gString); |
|
|
uint32 fs = CHASE_SCENE_LENGTH_IN_FRAMES * sizeof(CCarStateEachFrame); |
|
|
printf("FileSize:%d\n", fs); |
|
|
CFileMgr::Write(fid, (char*)pBaseMemForCar[CurrentCar], fs); |
|
|
CFileMgr::CloseFile(fid); |
|
|
CFileMgr::SetDir(""); |
|
|
sprintf(gString, "car%d.max", CurrentCar); |
|
|
int fid2 = CFileMgr::OpenFileForWriting(gString); |
|
|
for (int i = 0; i < CHASE_SCENE_FRAMES_IN_RECORDING; i++) { |
|
|
// WTF? Was it ever used? |
|
|
#ifdef FIX_BUGS |
|
|
CCarStateEachFrame* pState = pBaseMemForCar[CurrentCar]; |
|
|
#else |
|
|
CCarStateEachFrame* pState = (CCarStateEachFrame*)pChaseCars[CurrentCar]; |
|
|
#endif |
|
|
CVector right = CVector(pState->rightX, pState->rightY, pState->rightZ) / (UINT8_MAX / 2); |
|
|
CVector forward = CVector(pState->forwardX, pState->forwardY, pState->forwardZ) / (UINT8_MAX / 2); |
|
|
CVector up = CrossProduct(right, forward); |
|
|
sprintf(gString, "%f %f %f\n", pState->pos.x, pState->pos.y, pState->pos.z); |
|
|
CFileMgr::Write(fid2, gString, strlen(gString) - 1); |
|
|
sprintf(gString, "%f %f %f\n", right.x, right.y, right.z); |
|
|
CFileMgr::Write(fid2, gString, strlen(gString) - 1); |
|
|
sprintf(gString, "%f %f %f\n", forward.x, forward.y, forward.z); |
|
|
CFileMgr::Write(fid2, gString, strlen(gString) - 1); |
|
|
sprintf(gString, "%f %f %f\n", up.x, up.y, up.z); |
|
|
CFileMgr::Write(fid2, gString, strlen(gString) - 1); |
|
|
} |
|
|
CFileMgr::CloseFile(fid2); |
|
|
} |
|
|
case STATE_PLAYBACK: |
|
|
case STATE_PLAYBACK_BEFORE_RECORDING: |
|
|
case STATE_PLAYBACK_INIT: |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
struct tCoors { |
|
|
CVector pos; |
|
|
float angle; |
|
|
}; |
|
|
|
|
|
// I guess developer was filling this with actual data before running the game |
|
|
tCoors NewCoorsForRecordedCars[7]; |
|
|
|
|
|
void CRecordDataForChase::SaveOrRetrieveCarPositions(void) |
|
|
{ |
|
|
switch (Status) { |
|
|
case STATE_NONE: |
|
|
return; |
|
|
case STATE_RECORD: |
|
|
case STATE_PLAYBACK_BEFORE_RECORDING: |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) { |
|
|
if (i != CurrentCar && CTimer::GetFrameCounter()) { |
|
|
RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][CTimer::GetFrameCounter() / 2], false); |
|
|
pChaseCars[i]->GetMatrix().UpdateRW(); |
|
|
pChaseCars[i]->UpdateRwFrame(); |
|
|
} |
|
|
} |
|
|
if (Status == STATE_PLAYBACK_BEFORE_RECORDING && CTimer::GetFrameCounter()) { |
|
|
RestoreInfoForCar(pChaseCars[CurrentCar], &pBaseMemForCar[CurrentCar][CTimer::GetFrameCounter() / 2], false); |
|
|
pChaseCars[CurrentCar]->GetMatrix().UpdateRW(); |
|
|
pChaseCars[CurrentCar]->UpdateRwFrame(); |
|
|
} |
|
|
if (CPad::GetPad(0)->GetLeftShockJustDown() && CPad::GetPad(0)->GetRightShockJustDown()) { |
|
|
if (!CPad::GetPad(0)->GetRightShockJustDown()) { |
|
|
pChaseCars[CurrentCar]->GetPosition() = NewCoorsForRecordedCars[PositionChanges].pos; |
|
|
pChaseCars[CurrentCar]->SetMoveSpeed(0.0f, 0.0f, 0.0f); |
|
|
pChaseCars[CurrentCar]->GetMatrix().SetRotateZOnly(DEGTORAD(NewCoorsForRecordedCars[PositionChanges].angle)); |
|
|
++PositionChanges; |
|
|
} |
|
|
if (Status == STATE_PLAYBACK_BEFORE_RECORDING) { |
|
|
Status = STATE_RECORD; |
|
|
pChaseCars[CurrentCar]->m_status = STATUS_PLAYER; |
|
|
} |
|
|
} |
|
|
break; |
|
|
case STATE_PLAYBACK_INIT: |
|
|
Status = STATE_PLAYBACK; |
|
|
break; |
|
|
case STATE_PLAYBACK: |
|
|
{ |
|
|
TimeMultiplier += CTimer::GetTimeStepNonClippedInSeconds(); |
|
|
float EndOfFrameTime = CHASE_SCENE_FRAMES_PER_SECOND * min(CHASE_SCENE_LENGTH_IN_SECONDS, TimeMultiplier); |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) { |
|
|
if (!pBaseMemForCar[i]) |
|
|
continue; |
|
|
if (!pChaseCars[i]) |
|
|
continue; |
|
|
if (EndOfFrameTime < CHASE_SCENE_FRAMES_IN_RECORDING - 1) { |
|
|
int FlooredEOFTime = EndOfFrameTime; |
|
|
RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][FlooredEOFTime], false); |
|
|
CMatrix tmp; |
|
|
float dp = EndOfFrameTime - FlooredEOFTime; |
|
|
RestoreInfoForMatrix(tmp, &pBaseMemForCar[i][FlooredEOFTime + 1]); |
|
|
pChaseCars[i]->GetRight() += (tmp.GetRight() - pChaseCars[i]->GetRight()) * dp; |
|
|
pChaseCars[i]->GetForward() += (tmp.GetForward() - pChaseCars[i]->GetForward()) * dp; |
|
|
pChaseCars[i]->GetUp() += (tmp.GetUp() - pChaseCars[i]->GetUp()) * dp; |
|
|
pChaseCars[i]->GetPosition() += (tmp.GetPosition() - pChaseCars[i]->GetPosition()) * dp; |
|
|
} |
|
|
else{ |
|
|
RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][CHASE_SCENE_FRAMES_IN_RECORDING - 1], true); |
|
|
if (i == 0) |
|
|
pChaseCars[i]->GetPosition().z += 0.2f; |
|
|
} |
|
|
pChaseCars[i]->GetMatrix().UpdateRW(); |
|
|
pChaseCars[i]->UpdateRwFrame(); |
|
|
pChaseCars[i]->RemoveAndAdd(); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::StoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState) |
|
|
{ |
|
|
pState->rightX = UINT8_MAX / 2 * pCar->GetRight().x; |
|
|
pState->rightY = UINT8_MAX / 2 * pCar->GetRight().y; |
|
|
pState->rightZ = UINT8_MAX / 2 * pCar->GetRight().z; |
|
|
pState->forwardX = UINT8_MAX / 2 * pCar->GetForward().x; |
|
|
pState->forwardY = UINT8_MAX / 2 * pCar->GetForward().y; |
|
|
pState->forwardZ = UINT8_MAX / 2 * pCar->GetForward().z; |
|
|
pState->pos = pCar->GetPosition(); |
|
|
pState->velX = 1.0f * (UINT16_MAX / 2) * pCar->GetMoveSpeed().x; |
|
|
pState->velY = 1.0f * (UINT16_MAX / 2) * pCar->GetMoveSpeed().y; |
|
|
pState->velZ = 1.0f * (UINT16_MAX / 2) * pCar->GetMoveSpeed().z; |
|
|
pState->wheel = 20 * pCar->m_fSteerAngle; |
|
|
pState->gas = 100 * pCar->m_fGasPedal; |
|
|
pState->brake = 100 * pCar->m_fBrakePedal; |
|
|
pState->handbrake = pCar->bIsHandbrakeOn; |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::RestoreInfoForMatrix(CMatrix& matrix, CCarStateEachFrame* pState) |
|
|
{ |
|
|
EAXJMP(0x434780); |
|
|
matrix.GetRight() = CVector(pState->rightX, pState->rightY, pState->rightZ) / (UINT8_MAX / 2); |
|
|
matrix.GetForward() = CVector(pState->forwardX, pState->forwardY, pState->forwardZ) / (UINT8_MAX / 2); |
|
|
matrix.GetUp() = CrossProduct(matrix.GetRight(), matrix.GetForward()); |
|
|
matrix.GetPosition() = pState->pos; |
|
|
} |
|
|
|
|
|
WRAPPER void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) { EAXJMP(0x4347F0); } |
|
|
WRAPPER void CRecordDataForChase::ProcessControlCars(void) { EAXJMP(0x435540); } |
|
|
WRAPPER void CRecordDataForChase::SaveOrRetrieveCarPositions(void) { EAXJMP(0x434B20); } |
|
|
WRAPPER void CRecordDataForChase::StartChaseScene(float) { EAXJMP(0x435690); } |
|
|
WRAPPER void CRecordDataForChase::CleanUpChaseScene() { EAXJMP(0x4357C0); } |
|
|
WRAPPER void CRecordDataForChase::RemoveCarFromChase(int32) { EAXJMP(0x435BC0); } |
|
|
WRAPPER CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32) { EAXJMP(0x435C00); } |
|
|
void CRecordDataForChase::RestoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState, bool stop) |
|
|
{ |
|
|
CVector oldPos = pCar->GetPosition(); |
|
|
RestoreInfoForMatrix(pCar->GetMatrix(), pState); |
|
|
pCar->SetMoveSpeed(CVector(pState->velX, pState->velY, pState->velZ) * 1.0f / (UINT16_MAX / 2)); |
|
|
pCar->SetTurnSpeed(0.0f, 0.0f, 0.0f); |
|
|
pCar->m_fSteerAngle = pState->wheel / 20.0f; |
|
|
pCar->m_fGasPedal = pState->gas / 100.0f; |
|
|
pCar->m_fBrakePedal = pState->brake / 100.0f; |
|
|
pCar->bIsHandbrakeOn = pState->handbrake; |
|
|
if ((oldPos - pCar->GetPosition()).Magnitude() > 15.0f) { |
|
|
if (pCar == pChaseCars[14]) { |
|
|
pCar->m_currentColour1 = 58; |
|
|
pCar->m_currentColour2 = 1; |
|
|
} |
|
|
else |
|
|
pCar->GetModelInfo()->ChooseVehicleColour(pCar->m_currentColour1, pCar->m_currentColour2); |
|
|
} |
|
|
pCar->m_fHealth = min(pCar->m_fHealth, 500.0f); |
|
|
if (stop) { |
|
|
pCar->m_fGasPedal = 0.0f; |
|
|
pCar->m_fBrakePedal = 0.0f; |
|
|
pCar->SetMoveSpeed(0.0f, 0.0f, 0.0f); |
|
|
pCar->bIsHandbrakeOn = false; |
|
|
} |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::ProcessControlCars(void) |
|
|
{ |
|
|
if (Status != STATE_PLAYBACK) |
|
|
return; |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) { |
|
|
if (pChaseCars[i]) |
|
|
pChaseCars[i]->ProcessControl(); |
|
|
} |
|
|
} |
|
|
|
|
|
#if (defined(GTA_PS2) || defined(FIX_BUGS)) |
|
|
bool CRecordDataForChase::ShouldThisPadBeLeftAlone(uint8 pad) |
|
|
{ |
|
|
// may be wrong |
|
|
if (Status == STATE_NONE || Status == STATE_PLAYBACK) |
|
|
return false; |
|
|
return pad != 0; |
|
|
} |
|
|
#endif |
|
|
|
|
|
void CRecordDataForChase::GiveUsACar(int32 mi, CVector pos, float angle, CAutomobile** ppCar, uint8 colour1, uint8 colour2) |
|
|
{ |
|
|
CStreaming::RequestModel(mi, STREAMFLAGS_DEPENDENCY); |
|
|
CStreaming::LoadAllRequestedModels(false); |
|
|
if (!CStreaming::HasModelLoaded(mi)) |
|
|
return; |
|
|
CAutomobile* pCar = new CAutomobile(mi, MISSION_VEHICLE); |
|
|
pCar->GetPosition() = pos; |
|
|
pCar->m_status = STATUS_PLAYER_PLAYBACKFROMBUFFER; |
|
|
pCar->GetMatrix().SetRotateZOnly(DEGTORAD(angle)); |
|
|
pCar->pDriver = nil; |
|
|
pCar->m_currentColour1 = colour1; |
|
|
pCar->m_currentColour2 = colour2; |
|
|
CWorld::Add(pCar); |
|
|
*ppCar = pCar; |
|
|
} |
|
|
|
|
|
void RemoveUnusedCollision(void) |
|
|
{ |
|
|
static const char* dontDeleteArray[] = { |
|
|
"rd_SrRoad2A50", "rd_SrRoad2A20", "rd_CrossRda1w22", "rd_CrossRda1rw22", |
|
|
"road_broadway02", "road_broadway01", "com_21way5", "com_21way50", |
|
|
"cm1waycrosscom", "com_21way20", "com_21way10", "road_broadway04", |
|
|
"com_rvroads52", "com_roadsrv", "com_roadkb23", "com_roadkb22" |
|
|
}; |
|
|
for (int i = 0; i < ARRAY_SIZE(dontDeleteArray); i++) |
|
|
CModelInfo::GetModelInfo(dontDeleteArray[i], nil)->GetColModel()->level = LEVEL_NONE; |
|
|
CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_NONE); |
|
|
for (int i = 0; i < ARRAY_SIZE(dontDeleteArray); i++) |
|
|
CModelInfo::GetModelInfo(dontDeleteArray[i], nil)->GetColModel()->level = LEVEL_COMMERCIAL; |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::StartChaseScene(float startTime) |
|
|
{ |
|
|
char filename[28]; |
|
|
SetUpCarsForChaseScene(); |
|
|
Status = STATE_PLAYBACK; |
|
|
AnimTime = startTime; |
|
|
AnimStartTime = CTimer::GetTimeInMilliseconds(); |
|
|
RemoveUnusedCollision(); |
|
|
CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN); |
|
|
CGame::TidyUpMemory(true, true); |
|
|
CStreaming::ImGonnaUseStreamingMemory(); |
|
|
CFileMgr::SetDir("data\\paths"); |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) { |
|
|
if (!pChaseCars[i]) { |
|
|
pBaseMemForCar[i] = nil; |
|
|
continue; |
|
|
} |
|
|
sprintf(filename, "chase%d.dat", i); |
|
|
FId2 = CFileMgr::OpenFile(filename, "rb"); |
|
|
if (FId2 <= 0) { |
|
|
pBaseMemForCar[i] = nil; |
|
|
continue; |
|
|
} |
|
|
pBaseMemForCar[i] = new CCarStateEachFrame[CHASE_SCENE_FRAMES_IN_RECORDING]; |
|
|
for (int j = 0; j < CHASE_SCENE_FRAMES_IN_RECORDING; j++) { |
|
|
CFileMgr::Read(FId2, (char*)&pBaseMemForCar[i][j], sizeof(CCarStateEachFrame)); |
|
|
CFileMgr::Seek(FId2, sizeof(CCarStateEachFrame), 1); |
|
|
} |
|
|
CFileMgr::CloseFile(FId2); |
|
|
} |
|
|
CFileMgr::SetDir(""); |
|
|
CStreaming::IHaveUsedStreamingMemory(); |
|
|
TimeMultiplier = 0.0f; |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::CleanUpChaseScene(void) |
|
|
{ |
|
|
if (Status != STATE_PLAYBACK_INIT && Status != STATE_PLAYBACK) |
|
|
return; |
|
|
Status = STATE_NONE; |
|
|
CleanUpCarsForChaseScene(); |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) { |
|
|
if (pBaseMemForCar[i]) { |
|
|
delete[] pBaseMemForCar[i]; |
|
|
pBaseMemForCar[i] = nil; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::SetUpCarsForChaseScene(void) |
|
|
{ |
|
|
GiveUsACar(MI_POLICE, CVector(273.54221f, -1167.1907f, 24.880601f), 63.0f, &pChaseCars[0], 2, 1); |
|
|
GiveUsACar(MI_ENFORCER, CVector(231.1783f, -1388.8322f, 25.978201f), 90.0f, &pChaseCars[1], 2, 1); |
|
|
GiveUsACar(MI_TAXI, CVector(184.3156f, -1473.251f, 25.978201f), 0.0f, &pChaseCars[4], 6, 6); |
|
|
GiveUsACar(MI_CHEETAH, CVector(173.8868f, -1377.6514f, 25.978201f), 0.0f, &pChaseCars[6], 4, 5); |
|
|
GiveUsACar(MI_STINGER, CVector(102.5946f, -943.93628f, 25.9781f), 270.0f, &pChaseCars[7], 53, 53); |
|
|
GiveUsACar(MI_CHEETAH, CVector(-177.7157f, -862.18652f, 25.978201f), 155.0f, &pChaseCars[10], 41, 1); |
|
|
GiveUsACar(MI_STINGER, CVector(-170.56979f, -889.02362f, 25.978201f), 154.0f, &pChaseCars[11], 10, 10); |
|
|
GiveUsACar(MI_KURUMA, CVector(402.60809f, -917.49628f, 37.381001f), 90.0f, &pChaseCars[14], 34, 1); |
|
|
GiveUsACar(MI_TAXI, CVector(-33.496201f, -938.4563f, 25.9781f), 266.0f, &pChaseCars[16], 6, 6); |
|
|
GiveUsACar(MI_KURUMA, CVector(49.363098f, -987.60498f, 25.9781f), 0.0f, &pChaseCars[18], 51, 1); |
|
|
GiveUsACar(MI_TAXI, CVector(179.0049f, -1154.6686f, 25.9781f), 0.0f, &pChaseCars[19], 6, 76); |
|
|
GiveUsACar(MI_RUMPO, CVector(-28.9762f, -1031.3367f, 25.990601f), 242.0f, &pChaseCars[2], 1, 75); |
|
|
GiveUsACar(MI_PATRIOT, CVector(114.1564f, -796.69379f, 24.978201f), 180.0f, &pChaseCars[3], 0, 0); |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::CleanUpCarsForChaseScene(void) |
|
|
{ |
|
|
for (int i = 0; i < NUM_CHASE_CARS; i++) |
|
|
RemoveCarFromChase(i); |
|
|
} |
|
|
|
|
|
void CRecordDataForChase::RemoveCarFromChase(int32 i) |
|
|
{ |
|
|
if (!pChaseCars[i]) |
|
|
return; |
|
|
CWorld::Remove(pChaseCars[i]); |
|
|
delete pChaseCars[i]; |
|
|
pChaseCars[i] = nil; |
|
|
} |
|
|
|
|
|
CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32 i) |
|
|
{ |
|
|
CVehicle* pVehicle = pChaseCars[i]; |
|
|
pChaseCars[i] = nil; |
|
|
pVehicle->m_status = STATUS_PHYSICS; |
|
|
return pVehicle; |
|
|
} |
|
|
|