Refactoring `CTrailer` (#1117)

This commit is contained in:
Vladik01-11 2025-09-21 18:11:53 +00:00 committed by GitHub
parent 7282a4644c
commit 0dd2b020cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 370 additions and 138 deletions

View File

@ -387,7 +387,7 @@ CAutomobile::CAutomobile(int32 modelIndex, eVehicleCreatedBy createdBy, bool set
m_fDoomVerticalRotation = 0.0f; m_fDoomVerticalRotation = 0.0f;
m_fDoomHorizontalRotation = 0.05f; m_fDoomHorizontalRotation = 0.05f;
m_fUpDownLightAngle[0] = 0.0f; m_fPropRotate = 0.0f;
m_fForcedOrientation = -1.0f; m_fForcedOrientation = -1.0f;
m_harvesterParticleCounter = 0; m_harvesterParticleCounter = 0;
RightDoorOpenForDriveBys = 0.f; RightDoorOpenForDriveBys = 0.f;

View File

@ -18,6 +18,8 @@
#include "eCarWheel.h" #include "eCarWheel.h"
#include "eCarNodes.h" #include "eCarNodes.h"
constexpr float BILLS_EXTENSION_LIMIT = 1.0f;
enum class eSkidmarkType : uint32; enum class eSkidmarkType : uint32;
class CVehicleModelInfo; class CVehicleModelInfo;
@ -108,7 +110,8 @@ public:
float m_fDoomVerticalRotation; float m_fDoomVerticalRotation;
float m_fDoomHorizontalRotation; float m_fDoomHorizontalRotation;
float m_fForcedOrientation; float m_fForcedOrientation;
std::array<float, 2> m_fUpDownLightAngle; float m_fPropRotate;
float m_fCumulativeDamage;
uint8 m_nNumContactWheels; uint8 m_nNumContactWheels;
uint8 m_NumDriveWheelsOnGround; uint8 m_NumDriveWheelsOnGround;
uint8 m_NumDriveWheelsOnGroundLastFrame; uint8 m_NumDriveWheelsOnGroundLastFrame;

View File

@ -3,28 +3,34 @@
#include "Trailer.h" #include "Trailer.h"
#include "VehicleRecording.h" #include "VehicleRecording.h"
static float TRAILER_TOWED_MINRATIO = 0.9f; // 0x8D346C // define?
static float RELINK_TRAILER_DIFF_LIMIT_XY = 0.4f; // 0x8D3470 constexpr float BAGGAGE_TRAILER_TOWED_RATIO = -1000.0f;
static float RELINK_TRAILER_DIFF_LIMIT_Z = 1.0f; // 0x8D3474
static float m_fTrailerSuspensionForce = 1.5f; // 0x8D3464 float TRAILER_TOWED_MINRATIO = 0.9f; // 0x8D346C
static float m_fTrailerDampingForce = 0.1f; // 0x8D3468 constexpr float TRAILER_SUPPORT_RETRACTION_RATE = 0.002f; // 0x871C14
constexpr float TRAILER_SUPPORT_EXTENSION_RATE = 0.002f; // 0x871C18
constexpr float TRAILER_SUPPORT_WAIT_EXTENSION_RATE = 0.0005f; // 0x871C1C
float TRAILER_TOWBAR_OFFSET_Y = -0.05f; // 0x871C20
float RELINK_TRAILER_DIFF_LIMIT_XY = 0.4f; // 0x8D3470
float RELINK_TRAILER_DIFF_LIMIT_Z = 1.0f; // 0x8D3474
void CTrailer::InjectHooks() { void CTrailer::InjectHooks() {
RH_ScopedVirtualClass(CTrailer, 0x871c28, 71); RH_ScopedVirtualClass(CTrailer, 0x871c28, 71);
RH_ScopedCategory("Vehicle"); RH_ScopedCategory("Vehicle");
RH_ScopedInstall(Constructor, 0x6D03A0); RH_ScopedInstall(Constructor, 0x6D03A0);
RH_ScopedInstall(ScanForTowLink, 0x6CF030); RH_ScopedInstall(ScanForTowLink, 0x6CF030);
RH_ScopedVMTInstall(SetupSuspensionLines, 0x6CF1A0, { .reversed = false }); RH_ScopedVMTInstall(SetupSuspensionLines, 0x6CF1A0);
RH_ScopedVMTInstall(SetTowLink, 0x6CFDF0); RH_ScopedVMTInstall(SetTowLink, 0x6CFDF0);
RH_ScopedVMTInstall(ResetSuspension, 0x6CEE50); RH_ScopedVMTInstall(ResetSuspension, 0x6CEE50);
RH_ScopedVMTInstall(ProcessSuspension, 0x6CF6A0, { .reversed = false }); RH_ScopedVMTInstall(ProcessSuspension, 0x6CF6A0);
RH_ScopedVMTInstall(ProcessEntityCollision, 0x6CFFD0); RH_ScopedVMTInstall(ProcessEntityCollision, 0x6CFFD0);
RH_ScopedVMTInstall(ProcessControl, 0x6CED20, { .reversed = false }); RH_ScopedVMTInstall(ProcessControl, 0x6CED20);
RH_ScopedVMTInstall(ProcessAI, 0x6CF590, { .reversed = false }); RH_ScopedVMTInstall(ProcessAI, 0x6CF590);
RH_ScopedVMTInstall(PreRender, 0x6CFAC0, { .reversed = false }); RH_ScopedVMTInstall(PreRender, 0x6CFAC0);
RH_ScopedVMTInstall(GetTowHitchPos, 0x6CEEA0); RH_ScopedVMTInstall(GetTowHitchPos, 0x6CEEA0);
RH_ScopedVMTInstall(GetTowBarPos, 0x6CFD60); RH_ScopedVMTInstall(GetTowBarPos, 0x6CFD60);
RH_ScopedVMTInstall(BreakTowLink, 0x6CEFB0); RH_ScopedVMTInstall(BreakTowLink, 0x6CEFB0);
@ -34,17 +40,100 @@ void CTrailer::InjectHooks() {
CTrailer::CTrailer(int32 modelIndex, eVehicleCreatedBy createdBy) : CAutomobile(modelIndex, createdBy, false) { CTrailer::CTrailer(int32 modelIndex, eVehicleCreatedBy createdBy) : CAutomobile(modelIndex, createdBy, false) {
m_nVehicleSubType = VEHICLE_TYPE_TRAILER; m_nVehicleSubType = VEHICLE_TYPE_TRAILER;
if (m_nModelIndex == MODEL_BAGBOXA || m_nModelIndex == MODEL_BAGBOXB) if (m_nModelIndex == MODEL_BAGBOXA || m_nModelIndex == MODEL_BAGBOXB) {
m_fTrailerTowedRatio = -1000.0f; m_fTrailerTowedRatio = BAGGAGE_TRAILER_TOWED_RATIO;
}
CTrailer::SetupSuspensionLines(); SetupSuspensionLines();
SetStatus(eEntityStatus::STATUS_ABANDONED);
m_nStatus = eEntityStatus::STATUS_ABANDONED;
} }
// 0x6CF1A0 // 0x6CF1A0
void CTrailer::SetupSuspensionLines() { void CTrailer::SetupSuspensionLines() {
plugin::CallMethod<0x6CF1A0, CTrailer*>(this); if (m_fTrailerTowedRatio == BAGGAGE_TRAILER_TOWED_RATIO) {
CAutomobile::SetupSuspensionLines();
return;
}
auto* mi = CModelInfo::ms_modelInfoPtrs[m_nModelIndex]->AsVehicleModelInfoPtr();
CColModel* cl = GetColModel();
CCollisionData* colData = cl->m_pColData;
if (!colData->m_pLines) {
colData->m_nNumLines = NUM_TRAILER_SUSP_LINES;
colData->m_pLines = (CColLine*)CMemoryMgr::Malloc(sizeof(CColLine) * NUM_TRAILER_SUSP_LINES);
}
for (int32 i = 0; i < NUM_TRAILER_WHEELS; ++i) {
CVector wheelPos;
mi->GetWheelPosn(i, wheelPos, false);
m_wheelPosition[i] = wheelPos.z;
CVector lineStart = wheelPos;
lineStart.z += m_pHandlingData->m_fSuspensionUpperLimit;
colData->m_pLines[i].m_vecStart = lineStart;
CVector lineEnd = lineStart;
lineEnd.z += m_pHandlingData->m_fSuspensionLowerLimit - m_pHandlingData->m_fSuspensionUpperLimit - mi->m_fWheelSizeFront / 2;
colData->m_pLines[i].m_vecEnd = lineEnd;
m_aSuspensionSpringLength[i] = m_pHandlingData->m_fSuspensionUpperLimit - m_pHandlingData->m_fSuspensionLowerLimit;
m_aSuspensionLineLength[i] = colData->m_pLines[i].m_vecStart.z - colData->m_pLines[i].m_vecEnd.z;
}
cl->m_boundBox.m_vecMin.z = std::min(cl->m_boundBox.m_vecMin.z, colData->m_pLines[0].m_vecEnd.z);
// 0x6CF313
const float maxDistSq = std::max(cl->m_boundBox.m_vecMin.Magnitude(), cl->m_boundBox.m_vecMax.Magnitude());
cl->m_boundSphere.m_fRadius = std::max(cl->m_boundSphere.m_fRadius, maxDistSq);
if (m_aCarNodes[TRAILER_MISC_A]) {
RwMatrix matrix;
RwFrame* wheelFront = m_aCarNodes[TRAILER_MISC_A];
matrix = *RwFrameGetMatrix(wheelFront);
if (auto parent = RwFrameGetParent(wheelFront)) {
do {
RwMatrixTransform(&matrix, RwFrameGetMatrix(parent), rwCOMBINEPOSTCONCAT);
parent = RwFrameGetParent(parent);
} while (parent != wheelFront && parent);
}
m_fHeight = matrix.pos.z;
}
CColLine* supportLines = &colData->m_pLines[NUM_TRAILER_WHEELS];
for (int32 i = 0; i < NUM_TRAILER_SUPPORTS; ++i) {
CVector startPos;
startPos.x = (i == 0) ? cl->m_boundBox.m_vecMin.x : -cl->m_boundBox.m_vecMin.x;
startPos.y = cl->m_boundBox.m_vecMax.y;
startPos.z = cl->m_boundBox.m_vecMax.z;
supportLines[i].m_vecStart = startPos;
CVector endPos = startPos;
endPos.z = cl->m_boundBox.m_vecMin.z;
supportLines[i].m_vecEnd = endPos;
}
const float supportLegY = fabs(supportLines[0].m_vecStart.y);
const float avgWheelY = (fabs(colData->m_pLines[0].m_vecStart.y) + fabs(colData->m_pLines[1].m_vecStart.y)) * 0.5f;
const float weightRatio = supportLegY / (avgWheelY + supportLegY);
const float suspensionForceFactor = m_pHandlingData->m_fSuspensionForceLevel * 4.0f;
const float heightAboveRoad = m_aSuspensionSpringLength[0] * (1.0f - weightRatio / suspensionForceFactor)
+ mi->m_fWheelSizeFront / 2 - colData->m_pLines[0].m_vecStart.z;
m_fFrontHeightAboveRoad = heightAboveRoad;
m_fRearHeightAboveRoad = heightAboveRoad;
const float supportForce = CTrailer::m_fTrailerSuspensionForce * 2.0f;
const float newEndZ = supportLines[0].m_vecStart.z - (supportLines[0].m_vecStart.z + heightAboveRoad)
/ (1.0f - (1.0f - weightRatio) / supportForce);
for (int i = 0; i < NUM_TRAILER_SUPPORTS; ++i) {
supportLines[i].m_vecEnd.z = newEndZ;
m_wheelPosition[i] = mi->m_fWheelSizeFront / 2 - m_fFrontHeightAboveRoad;
}
} }
// 0x6CFDF0 // 0x6CFDF0
@ -53,11 +142,12 @@ bool CTrailer::SetTowLink(CVehicle* vehicle, bool setMyPosToTowBar) {
return false; return false;
} }
if (m_nStatus != STATUS_PHYSICS && m_nStatus != STATUS_IS_TOWED && m_nStatus != STATUS_ABANDONED) { const auto status = GetStatus();
if (status != STATUS_PHYSICS && status != STATUS_IS_TOWED && status != STATUS_ABANDONED) {
return false; return false;
} }
m_nStatus = STATUS_IS_TOWED; SetStatus(STATUS_IS_TOWED);
m_pTowingVehicle = vehicle; m_pTowingVehicle = vehicle;
m_pTowingVehicle->RegisterReference(m_pTowingVehicle); m_pTowingVehicle->RegisterReference(m_pTowingVehicle);
@ -71,24 +161,20 @@ bool CTrailer::SetTowLink(CVehicle* vehicle, bool setMyPosToTowBar) {
AddToMovingList(); AddToMovingList();
vehicle->AddToMovingList(); vehicle->AddToMovingList();
vehicle->m_pVehicleBeingTowed->vehicleFlags.bLightsOn = true; // NOTSA | FIX_BUGS
if (!setMyPosToTowBar) { if (!setMyPosToTowBar) {
UpdateTractorLink(true, false); UpdateTractorLink(true, false);
vehicle->m_vehicleAudio.AddAudioEvent(AE_TRAILER_ATTACH, 0.0f); vehicle->m_vehicleAudio.AddAudioEvent(AE_TRAILER_ATTACH, 0.0f);
return true; return true;
} }
if (m_fTrailerTowedRatio > -1000.0f) { m_fTrailerTowedRatio = std::clamp(m_fTrailerTowedRatio, 0.0f, BAGGAGE_TRAILER_TOWED_RATIO);
m_fTrailerTowedRatio = 0.0f;
}
SetHeading(GetHeading()); SetHeading(GetHeading());
auto towBarPos = CVector{}; CVector towBarPos{}, towHitchPos{};
auto towHitchPos = CVector{}; if (!GetTowHitchPos(towHitchPos, true, this) || !vehicle->GetTowBarPos(towBarPos, true, this)) {
if (!GetTowHitchPos(towHitchPos, true, this) || !vehicle->GetTowBarPos(towBarPos, true, this))
return false; return false;
}
towHitchPos -= GetPosition(); towHitchPos -= GetPosition();
@ -102,33 +188,34 @@ bool CTrailer::SetTowLink(CVehicle* vehicle, bool setMyPosToTowBar) {
// 0x6CF030 // 0x6CF030
void CTrailer::ScanForTowLink() { void CTrailer::ScanForTowLink() {
if (m_pTowingVehicle || CVehicleRecording::IsPlaybackGoingOnForCar(this) || m_nModelIndex == MODEL_FARMTR1) if (m_pTowingVehicle || CVehicleRecording::IsPlaybackGoingOnForCar(this) || m_nModelIndex == MODEL_FARMTR1) {
return; return;
}
CVector towHitchPos; CVector towHitchPos;
if (!GetTowHitchPos(towHitchPos, false, this)) if (!GetTowHitchPos(towHitchPos, false, this)) {
return; return;
}
int16 count = 0; int16 count = 0;
CEntity* objects[16]{}; std::array<CEntity*, 16> objects{};
CWorld::FindObjectsInRange(towHitchPos, 10.0f, true, &count, int16(std::size(objects)), objects, false, true, false, false, false); CWorld::FindObjectsInRange(towHitchPos, 10.0f, true, &count, int16(objects.size()), objects.data(), false, true, false, false, false);
CVector towBarPos; for (const auto& entity : std::span{ objects.data(), (size_t)count }) {
CVehicle* vehicle = nullptr; if (auto* vehicle = entity->AsVehicle()) {
for (auto& obj : std::span{ objects, (size_t)count }) { if (vehicle == this) {
vehicle = obj->AsVehicle(); continue;
if (vehicle == this) { }
continue;
}
if (!vehicle->GetTowBarPos(towBarPos, false, this)) { CVector towBarPos;
continue; if (!vehicle->GetTowBarPos(towBarPos, false, this)) {
} continue;
}
const auto dist = DistanceBetweenPoints2D(towBarPos, towHitchPos); if (DistanceBetweenPoints2D(towBarPos, towHitchPos) < RELINK_TRAILER_DIFF_LIMIT_XY && std::fabs(towHitchPos.z - towBarPos.z) < RELINK_TRAILER_DIFF_LIMIT_Z) {
if (dist < RELINK_TRAILER_DIFF_LIMIT_XY && std::fabs(towHitchPos.z - towBarPos.z) < RELINK_TRAILER_DIFF_LIMIT_Z) { SetTowLink(vehicle, false);
SetTowLink(vehicle, false); return;
return; }
} }
} }
} }
@ -137,24 +224,73 @@ void CTrailer::ScanForTowLink() {
void CTrailer::ResetSuspension() { void CTrailer::ResetSuspension() {
CAutomobile::ResetSuspension(); CAutomobile::ResetSuspension();
rng::fill(m_supportRatios, 1.f); rng::fill(m_supportRatios, 1.f);
if (m_pTowingVehicle && m_fTrailerTowedRatio > -1000.0f) if (m_pTowingVehicle && m_fTrailerTowedRatio > BAGGAGE_TRAILER_TOWED_RATIO) {
m_fTrailerTowedRatio = 0.0f; m_fTrailerTowedRatio = 0.0f;
else } else {
m_fTrailerTowedRatio = 1.0f; m_fTrailerTowedRatio = 1.0f;
}
} }
// 0x6CF6A0 // 0x6CF6A0
void CTrailer::ProcessSuspension() { void CTrailer::ProcessSuspension() {
plugin::CallMethod<0x6CF6A0, CTrailer*>(this); CAutomobile::ProcessSuspension();
if (m_fTrailerTowedRatio == BAGGAGE_TRAILER_TOWED_RATIO) {
return;
}
CVector contactPoints[NUM_TRAILER_SUPPORTS]{};
CVector contactSpeeds[NUM_TRAILER_SUPPORTS]{};
float impulseMagnitudes[NUM_TRAILER_SUPPORTS]{};
for (int32 i = 0; i < NUM_TRAILER_SUPPORTS; ++i) {
if (m_supportRatios[i] < m_fTrailerTowedRatio2 && m_fTrailerTowedRatio2 > 0.1f) {
CVector forceDir = m_matrix->GetDown();
contactPoints[i] = m_supportCPs[i].m_vecPoint - GetPosition();
const float compression = m_supportRatios[i] / m_fTrailerTowedRatio2;
CPhysical::ApplySpringCollisionAlt(m_fTrailerSuspensionForce, forceDir, contactPoints[i], compression, 1.0f, m_supportCPs[i].m_vecNormal, impulseMagnitudes[i]);
contactSpeeds[i] = GetSpeed(contactPoints[i]);
CVector dampeningDir = m_supportCPs[i].m_vecNormal.z > 0.35f
? -m_supportCPs[i].m_vecNormal
: m_matrix->GetDown();
CPhysical::ApplySpringDampening(m_fTrailerDampingForce, impulseMagnitudes[i], dampeningDir, contactPoints[i], contactSpeeds[i]);
}
}
const auto status = GetStatus();
if (status != STATUS_IS_TOWED && status != STATUS_IS_SIMPLE_TOWED) {
eCarWheelStatus originalWheelStatus[NUM_TRAILER_SUPPORTS];
for (int32 i = 0; i < NUM_TRAILER_SUPPORTS; ++i) {
originalWheelStatus[i] = m_damageManager.GetWheelStatus((eCarWheel)i);
if (m_supportRatios[i] >= m_fTrailerTowedRatio2 || m_fTrailerTowedRatio2 <= 0.1f) {
m_damageManager.SetWheelStatus((eCarWheel)i, eCarWheelStatus::WHEEL_STATUS_BURST);
} else {
contactSpeeds[i] = GetSpeed(contactPoints[i]);
m_damageManager.SetWheelStatus((eCarWheel)i, eCarWheelStatus::WHEEL_STATUS_OK);
}
}
const float traction = m_pHandlingData->m_fTractionMultiplier * 0.004f * 0.25f;
CAutomobile::ProcessCarWheelPair(CAR_WHEEL_FRONT_LEFT, CAR_WHEEL_REAR_LEFT, 0.0f, contactSpeeds, contactPoints, traction, 0.0f, 1000.0f, true);
m_damageManager.SetWheelStatus(CAR_WHEEL_FRONT_LEFT, originalWheelStatus[0]);
m_damageManager.SetWheelStatus(CAR_WHEEL_REAR_LEFT, originalWheelStatus[1]);
}
} }
// 0x6CFFD0 // 0x6CFFD0
int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints) { int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints) {
if (m_fTrailerTowedRatio == -1000.f) { if (m_fTrailerTowedRatio == BAGGAGE_TRAILER_TOWED_RATIO) {
return CAutomobile::ProcessEntityCollision(entity, outColPoints); return CAutomobile::ProcessEntityCollision(entity, outColPoints);
} }
if (m_nStatus != STATUS_SIMPLE) { if (GetStatus() != STATUS_SIMPLE) {
vehicleFlags.bVehicleColProcessed = true; vehicleFlags.bVehicleColProcessed = true;
} }
@ -172,16 +308,16 @@ int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints)
} }
// Hide triangles in some cases // Hide triangles in some cases
const auto didHideTriangles = m_pTowingVehicle == entity || m_pVehicleBeingTowed == entity; const auto shouldHideTriangles = m_pTowingVehicle == entity || m_pVehicleBeingTowed == entity;
const auto tNumTri = tcd->m_nNumTriangles, // Saving my sanity here, and unconditionally assigning const auto originalTNumTri = tcd->m_nNumTriangles, // Saving my sanity here, and unconditionally assigning
oNumTri = ocd->m_nNumTriangles; originalONumTri = ocd->m_nNumTriangles;
if (didHideTriangles) { if (shouldHideTriangles) {
tcd->m_nNumTriangles = ocd->m_nNumTriangles = 0; tcd->m_nNumTriangles = ocd->m_nNumTriangles = 0;
} }
std::array<CColPoint, NUM_TRAILER_SUSP_LINES> suspLineCPs{}; std::array<CColPoint, NUM_TRAILER_SUSP_LINES> suspLineCPs{};
std::array<float, NUM_TRAILER_SUSP_LINES> suspLineTouchDists{}; std::array<float, NUM_TRAILER_SUSP_LINES> suspLineTouchDists{};
rng::copy(m_fWheelsSuspensionCompression, suspLineTouchDists.begin()); rng::copy(m_fWheelsSuspensionCompression, suspLineTouchDists.begin());
rng::copy(m_supportRatios, suspLineTouchDists.begin() + m_fWheelsSuspensionCompression.size()); rng::copy(m_supportRatios, suspLineTouchDists.begin() + m_fWheelsSuspensionCompression.size());
@ -195,63 +331,58 @@ int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints)
); );
// Restore hidden triangles // Restore hidden triangles
if (didHideTriangles) { if (shouldHideTriangles) {
tcd->m_nNumTriangles = tNumTri; tcd->m_nNumTriangles = originalTNumTri;
ocd->m_nNumTriangles = oNumTri; ocd->m_nNumTriangles = originalONumTri;
} }
size_t numProcessedLines{}; size_t numProcessedLines{};
if (tcd->m_nNumLines) { if (tcd->m_nNumLines) {
// Process the real wheel's susp lines // Process the real wheel's susp lines
for (auto i = 0; i < MAX_CARWHEELS; i++) { for (auto i = 0; i < NUM_TRAILER_WHEELS; i++) {
// 0x6AD0D4 // 0x6AD0D4
const auto& cp = suspLineCPs[i]; const auto& cp = suspLineCPs[i];
const auto touchDist = suspLineTouchDists[i]; const auto touchDist = suspLineTouchDists[i];
if (touchDist >= 1.f || touchDist >= m_fWheelsSuspensionCompression[i]) { if (touchDist < BILLS_EXTENSION_LIMIT && touchDist < m_fWheelsSuspensionCompression[i]) {
continue; numProcessedLines++;
} m_fWheelsSuspensionCompression[i] = touchDist;
m_wheelColPoint[i] = cp;
m_anCollisionLighting[i] = cp.m_nLightingB;
m_nContactSurface = cp.m_nSurfaceTypeB;
numProcessedLines++; switch (entity->GetType()) {
case ENTITY_TYPE_VEHICLE:
case ENTITY_TYPE_OBJECT: {
CEntity::ChangeEntityReference(m_apWheelCollisionEntity[i], entity->AsPhysical());
m_fWheelsSuspensionCompression[i] = touchDist; m_vWheelCollisionPos[i] = cp.m_vecPoint - entity->GetPosition();
m_wheelColPoint[i] = cp; if (entity->IsVehicle()) {
m_anCollisionLighting[i] = entity->AsVehicle()->m_anCollisionLighting[i];
m_anCollisionLighting[i] = cp.m_nLightingB; }
m_nContactSurface = cp.m_nSurfaceTypeB; break;
}
switch (entity->GetType()) { case ENTITY_TYPE_BUILDING: {
case ENTITY_TYPE_VEHICLE: m_pEntityWeAreOn = entity;
case ENTITY_TYPE_OBJECT: { m_bTunnel = entity->m_bTunnel;
CEntity::ChangeEntityReference(m_apWheelCollisionEntity[i], entity->AsPhysical()); m_bTunnelTransition = entity->m_bTunnelTransition;
break;
m_vWheelCollisionPos[i] = cp.m_vecPoint - entity->GetPosition(); }
if (entity->IsVehicle()) {
m_anCollisionLighting[i] = entity->AsVehicle()->m_anCollisionLighting[i];
} }
break;
}
case ENTITY_TYPE_BUILDING: {
m_pEntityWeAreOn = entity;
m_bTunnel = entity->m_bTunnel;
m_bTunnelTransition = entity->m_bTunnelTransition;
break;
}
}
}
// Process trailer support lines
for (auto i = 0; i < NUM_TRAILER_SUPPORTS; i++) {
const auto suspLineIdx = NUM_TRAILER_WHEELS + i;
const auto touchDist = suspLineTouchDists[suspLineIdx];
if (touchDist >= 1.f || touchDist >= m_supportRatios[i]) {
continue;
} }
numProcessedLines++; // Process trailer support lines
m_supportRatios[i] = touchDist; for (auto i = 0; i < NUM_TRAILER_SUPPORTS; i++) {
m_supportCPs[i] = suspLineCPs[suspLineIdx]; const auto suspLineIdx = NUM_TRAILER_WHEELS + i;
const auto touchDist = suspLineTouchDists[suspLineIdx];
if (touchDist >= BILLS_EXTENSION_LIMIT || touchDist >= m_supportRatios[i]) {
continue;
}
numProcessedLines++;
m_supportRatios[i] = touchDist;
m_supportCPs[i] = suspLineCPs[suspLineIdx];
}
} }
} else { } else {
tcd->m_nNumLines = NUM_TRAILER_SUSP_LINES; tcd->m_nNumLines = NUM_TRAILER_SUSP_LINES;
@ -262,12 +393,9 @@ int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints)
if (!entity->IsBuilding()) { if (!entity->IsBuilding()) {
entity->AsPhysical()->AddCollisionRecord(this); entity->AsPhysical()->AddCollisionRecord(this);
} }
if (numColPts > 0) { if (numColPts > 0 && entity->IsBuilding()
if ( entity->IsBuilding() || (entity->IsObject() && entity->AsPhysical()->physicalFlags.bDisableCollisionForce)) {
|| (entity->IsObject() && entity->AsPhysical()->physicalFlags.bDisableCollisionForce) m_bHasHitWall = true;
) {
m_bHasHitWall = true;
}
} }
} }
@ -276,36 +404,126 @@ int32 CTrailer::ProcessEntityCollision(CEntity* entity, CColPoint* outColPoints)
// 0x6CED20 // 0x6CED20
void CTrailer::ProcessControl() { void CTrailer::ProcessControl() {
plugin::CallMethod<0x6CED20, CTrailer*>(this); CAutomobile::ProcessControl();
if ((m_nFlags & 0x40) == 0 && m_fTrailerTowedRatio != BAGGAGE_TRAILER_TOWED_RATIO) {
const float timeStep = CTimer::GetTimeStep();
float& currentExtension = m_fTrailerTowedRatio;
if (m_pTowingVehicle && currentExtension > 0.0f) {
// up
currentExtension = std::max(currentExtension - timeStep * TRAILER_SUPPORT_RETRACTION_RATE, 0.0f);
} else if (!m_pTowingVehicle && currentExtension < 1.0f) {
// down
const float growthRate = (currentExtension <= 0.1f)
? timeStep * TRAILER_SUPPORT_WAIT_EXTENSION_RATE
: timeStep * TRAILER_SUPPORT_EXTENSION_RATE;
currentExtension = std::min(currentExtension + growthRate, 1.0f);
m_nFakePhysics = 0;
}
const auto status = GetStatus();
if ((status == STATUS_IS_TOWED || status == STATUS_IS_SIMPLE_TOWED) && TRAILER_TOWED_MINRATIO > currentExtension) {
m_fTrailerTowedRatio2 = TRAILER_TOWED_MINRATIO;
} else {
m_fTrailerTowedRatio2 = currentExtension;
}
rng::fill(m_supportRatios, m_fTrailerTowedRatio2);
}
} }
// 0x6CF590 // 0x6CF590
bool CTrailer::ProcessAI(uint32& extraHandlingFlags) { bool CTrailer::ProcessAI(uint32& extraHandlingFlags) {
return plugin::CallMethodAndReturn<bool, 0x6CF590, CTrailer*, uint32&>(this, extraHandlingFlags); const auto returnValue = CAutomobile::ProcessAI(extraHandlingFlags);
if (GetStatus() == STATUS_ABANDONED && (m_fTrailerTowedRatio > 0.5f || m_fTrailerTowedRatio == BAGGAGE_TRAILER_TOWED_RATIO)) {
ScanForTowLink();
return returnValue;
}
if (GetStatus() != STATUS_REMOTE_CONTROLLED || !m_pTowingVehicle) {
return returnValue;
}
if (m_fTrailerTowedRatio != BAGGAGE_TRAILER_TOWED_RATIO) {
m_BrakePedal = m_pTowingVehicle->m_BrakePedal;
return returnValue;
}
// Wheel turning for luggage trailers
float headingTow = m_pTowingVehicle->GetHeading();
float headingSelf = GetHeading();
// Calculation of the shortest angle difference
float angleDiff = headingTow - headingSelf;
if (angleDiff > PI) {
angleDiff -= TWO_PI;
} else if (angleDiff < -PI) {
angleDiff += TWO_PI;
}
m_fSteerAngle = angleDiff;
return returnValue;
} }
// 0x6CFAC0 // 0x6CFAC0
void CTrailer::PreRender() { void CTrailer::PreRender() {
plugin::CallMethod<0x6CFAC0, CTrailer*>(this); CAutomobile::PreRender();
// Support animation
if (m_aCarNodes[TRAILER_MISC_A]) {
auto* colData = CModelInfo::GetModelInfo(m_nModelIndex)->m_pColModel->m_pColData;
if (colData->m_nNumLines > NUM_TRAILER_WHEELS && (!m_vecMoveSpeed.IsZero() || !m_vecTurnSpeed.IsZero() || !m_nFakePhysics)) {
CMatrix mat;
mat.Attach(&m_aCarNodes[TRAILER_MISC_A]->modelling, false);
const float avgSupportRatio = (m_supportRatios[0] + m_supportRatios[1]) * 0.5f;
const float supportExtensionRatio = std::min(avgSupportRatio, m_fTrailerTowedRatio);
const auto& supportLegLine = colData->m_pLines[NUM_TRAILER_WHEELS];
const float supportTravel = supportExtensionRatio * (supportLegLine.m_vecEnd.z - supportLegLine.m_vecStart.z);
const float verticalPosRatio = mat.GetPosition().y / supportLegLine.m_vecStart.y;
CVector newPos = mat.GetPosition();
const float targetZ = (supportTravel + supportLegLine.m_vecStart.z) * verticalPosRatio
- (1.0f - verticalPosRatio) * m_fFrontHeightAboveRoad;
newPos.z = std::min(targetZ, m_fHeight);
mat.SetTranslate(newPos);
mat.UpdateRW();
}
}
// Animation for FARMTR1
if (m_nModelIndex == MODEL_FARMTR1) {
m_fPropRotate = fmod(m_fPropRotate, TWO_PI);
const float forwardSpeed = m_vecMoveSpeed.Dot(m_matrix->GetForward());
const float rotationSpeedFactor = std::clamp(forwardSpeed, 0.0f, 0.1f);
m_fPropRotate -= rotationSpeedFactor * CTimer::GetTimeStep();
CVehicle::SetComponentRotation(m_aCarNodes[TRAILER_MISC_B], AXIS_X, m_fPropRotate, true);
}
} }
// 0x6CEEA0 // 0x6CEEA0
bool CTrailer::GetTowHitchPos(CVector& outPos, bool bCheckModelInfo, CVehicle* vehicle) { bool CTrailer::GetTowHitchPos(CVector& outPos, bool bCheckModelInfo, CVehicle* vehicle) {
auto mi = GetVehicleModelInfo(); auto mi = GetVehicleModelInfo();
outPos = mi->GetModelDummyPosition(DUMMY_TRAILER_ATTACH); outPos = mi->GetModelDummyPosition(DUMMY_TRAILER_ATTACH);
if (vehicle && vehicle->m_nModelIndex == MODEL_TOWTRUCK) { if (vehicle && vehicle->m_nModelIndex == MODEL_TOWTRUCK) {
return false; return false;
} }
if (outPos.IsZero()) { if (outPos.IsZero()) {
if (bCheckModelInfo) { if (!bCheckModelInfo) {
outPos.x = 0.0f; return false;
outPos.y = mi->GetColModel()->GetBoundingBox().m_vecMax.y + 1.0f;
outPos.z = 0.5f - m_fFrontHeightAboveRoad;
outPos = m_matrix->TransformPoint(outPos);
return true;
} }
return false; outPos.x = 0.0f;
outPos.y = mi->GetColModel()->GetBoundingBox().m_vecMax.y + 1.0f;
outPos.z = 0.5f - m_fFrontHeightAboveRoad;
outPos = m_matrix->TransformPoint(outPos);
return true;
} }
outPos = m_matrix->TransformPoint(outPos); outPos = m_matrix->TransformPoint(outPos);
return true; return true;
@ -321,7 +539,7 @@ bool CTrailer::GetTowBarPos(CVector& outPos, bool bCheckModelInfo, CVehicle* veh
auto mi = GetVehicleModelInfo(); auto mi = GetVehicleModelInfo();
outPos.x = 0.0f; outPos.x = 0.0f;
outPos.y = mi->GetColModel()->GetBoundingBox().m_vecMin.y - -0.05f; outPos.y = mi->GetColModel()->GetBoundingBox().m_vecMin.y - TRAILER_TOWBAR_OFFSET_Y;
outPos.z = 0.5f - m_fFrontHeightAboveRoad; outPos.z = 0.5f - m_fFrontHeightAboveRoad;
outPos = m_matrix->TransformPoint(outPos); outPos = m_matrix->TransformPoint(outPos);
return true; return true;
@ -338,10 +556,11 @@ bool CTrailer::BreakTowLink() {
CEntity::ClearReference(m_pTowingVehicle); CEntity::ClearReference(m_pTowingVehicle);
} }
if (m_nStatus != STATUS_IS_TOWED && m_nStatus != STATUS_IS_SIMPLE_TOWED) { const auto status = GetStatus();
if (status != STATUS_IS_TOWED && status != STATUS_IS_SIMPLE_TOWED) {
return false; return false;
} }
m_nStatus = STATUS_ABANDONED; SetStatus(STATUS_ABANDONED);
return true; return true;
} }

View File

@ -40,12 +40,16 @@ class NOTSA_EXPORT_VTABLE CTrailer : public CAutomobile {
static constexpr auto NUM_TRAILER_WHEELS = 4; static constexpr auto NUM_TRAILER_WHEELS = 4;
static constexpr auto NUM_TRAILER_SUPPORTS = 2; static constexpr auto NUM_TRAILER_SUPPORTS = 2;
static constexpr auto NUM_TRAILER_SUSP_LINES = NUM_TRAILER_WHEELS + NUM_TRAILER_SUPPORTS; static constexpr auto NUM_TRAILER_SUSP_LINES = NUM_TRAILER_WHEELS + NUM_TRAILER_SUPPORTS;
public: public:
std::array<CColPoint, NUM_TRAILER_SUPPORTS> m_supportCPs{}; std::array<CColPoint, NUM_TRAILER_SUPPORTS> m_supportCPs{}; // m_aSupportColPoints
std::array<float, NUM_TRAILER_SUPPORTS> m_supportRatios{ 1.f, 1.f }; std::array<float, NUM_TRAILER_SUPPORTS> m_supportRatios{ 1.f, 1.f }; // m_aSupportRatios
float m_fHeight; // 0x6CF3DC, 0x6CFC11 float m_fHeight; // m_fSupportStartZ, 0x6CF3DC, 0x6CFC11
float m_fTrailerTowedRatio{ 1.f }; float m_fTrailerTowedRatio{ 1.f }; // m_fSupportExtension
float m_fTrailerTowedRatio2{ 1.f }; float m_fTrailerTowedRatio2{ 1.f }; // m_fSupportMinRatio
static constexpr float m_fTrailerSuspensionForce = 1.5f; // 0x8D3464
static constexpr float m_fTrailerDampingForce = 0.1f; // 0x8D3468
static constexpr auto Type = VEHICLE_TYPE_TRAILER; static constexpr auto Type = VEHICLE_TYPE_TRAILER;
@ -53,28 +57,34 @@ public:
CTrailer(int32 modelIndex, eVehicleCreatedBy createdBy); CTrailer(int32 modelIndex, eVehicleCreatedBy createdBy);
~CTrailer() override = default; // 0x6CED10 ~CTrailer() override = default; // 0x6CED10
bool SetTowLink(CVehicle* tractor, bool setMyPosToTowBar) override;
void ScanForTowLink();
void SetupSuspensionLines() override; void SetupSuspensionLines() override;
void ResetSuspension() override;
void ProcessControl() override;
bool ProcessAI(uint32& extraHandlingFlags) override;
void ProcessSuspension() override; void ProcessSuspension() override;
void PreRender() override;
void ResetSuspension() override;
bool GetTowHitchPos(CVector& outPos, bool checkModelInfo, CVehicle* vehicle) override;
bool GetTowBarPos(CVector& outPos, bool checkModelInfo, CVehicle* vehicle) override;
bool SetTowLink(CVehicle* tractor, bool setMyPosToTowBar) override;
bool BreakTowLink() override;
void ScanForTowLink();
int32 ProcessEntityCollision(CEntity* entity, CColPoint* colPoint) override; int32 ProcessEntityCollision(CEntity* entity, CColPoint* colPoint) override;
void ProcessControl() override;
bool ProcessAI(uint32& extraHandlingFlags) override;
void PreRender() override;
bool GetTowHitchPos(CVector& outPos, bool bCheckModelInfo, CVehicle* vehicle) override;
bool GetTowBarPos(CVector& outPos, bool bCheckModelInfo, CVehicle* vehicle) override;
bool BreakTowLink() override;
private: private:
friend void InjectHooksMain(); friend void InjectHooksMain();
static void InjectHooks(); static void InjectHooks();
CTrailer* Constructor(int32 modelIndex, eVehicleCreatedBy createdBy) { this->CTrailer::CTrailer(modelIndex, createdBy); return this; } CTrailer* Constructor(int32 modelIndex, eVehicleCreatedBy createdBy) {
this->CTrailer::CTrailer(modelIndex, createdBy);
return this;
}
}; };
VALIDATE_SIZE(CTrailer, 0x9F4); VALIDATE_SIZE(CTrailer, 0x9F4);