summaryrefslogtreecommitdiffstats
path: root/src/vehicles
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/vehicles/Automobile.cpp1704
-rw-r--r--src/vehicles/Automobile.h10
-rw-r--r--src/vehicles/Boat.cpp2
-rw-r--r--src/vehicles/DamageManager.h9
-rw-r--r--src/vehicles/Floater.cpp14
-rw-r--r--src/vehicles/Floater.h4
-rw-r--r--src/vehicles/Vehicle.cpp25
-rw-r--r--src/vehicles/Vehicle.h21
8 files changed, 1728 insertions, 61 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index fb42e6e6..28ede4cd 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -7,14 +7,28 @@
#include "ModelIndices.h"
#include "VisibilityPlugins.h"
#include "DMAudio.h"
+#include "Clock.h"
+#include "TimeCycle.h"
+#include "ZoneCull.h"
#include "Camera.h"
#include "Darkel.h"
#include "Rubbish.h"
#include "Fire.h"
#include "Explosion.h"
#include "Particle.h"
+#include "ParticleObject.h"
+#include "Antennas.h"
+#include "Skidmarks.h"
+#include "Shadows.h"
+#include "PointLights.h"
+#include "Coronas.h"
+#include "SpecialFX.h"
+#include "WaterCannon.h"
+#include "WaterLevel.h"
+#include "Floater.h"
#include "World.h"
#include "SurfaceTable.h"
+#include "Weather.h"
#include "HandlingMgr.h"
#include "Record.h"
#include "Remote.h"
@@ -49,7 +63,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
bTaxiLight = m_sAllTaxiLights;
m_auto_flagA20 = false;
m_auto_flagA40 = false;
- m_auto_flagA80 = false;
+ bWaterTight = false;
SetModelIndex(id);
@@ -118,7 +132,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
bNotDamagedUpsideDown = false;
bMoreResistantToDamage = false;
m_fVelocityChangeForAudio = 0.f;
- field_4E2 = 0;
+ m_hydraulicState = 0;
for(i = 0; i < 4; i++){
m_aGroundPhysical[i] = nil;
@@ -128,7 +142,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
m_aWheelTimer[i] = 0.0f;
m_aWheelRotation[i] = 0.0f;
m_aWheelSpeed[i] = 0.0f;
- m_aWheelState[i] = WHEEL_STATE_0;
+ m_aWheelState[i] = WHEEL_STATE_NORMAL;
m_aWheelSkidmarkMuddy[i] = false;
m_aWheelSkidmarkBloody[i] = false;
}
@@ -162,7 +176,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
m_fCarGunLR = 0.0f;
m_fCarGunUD = 0.05f;
- m_fWindScreenRotation = 0.0f;
+ m_fPropellerRotation = 0.0f;
m_weaponDoorTimerLeft = 0.0f;
m_weaponDoorTimerRight = m_weaponDoorTimerLeft;
@@ -201,7 +215,7 @@ CAutomobile::ProcessControl(void)
int i;
CColModel *colModel;
- if(m_veh_flagC80)
+ if(bUsingSpecialColModel)
colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel;
else
colModel = GetColModel();
@@ -358,7 +372,7 @@ CAutomobile::ProcessControl(void)
pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear);
{
- float wheelRot = ProcessWheelRotation(WHEEL_STATE_0, GetForward(), m_vecMoveSpeed, 0.35f);
+ float wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.35f);
for(i = 0; i < 4; i++)
m_aWheelRotation[i] += wheelRot;
}
@@ -484,7 +498,7 @@ CAutomobile::ProcessControl(void)
m_aSuspensionSpringRatio[0] < 1.0f &&
CPad::GetPad(0)->HornJustDown()){
- DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRALIC_1, 0.0f);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f);
DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, 1.0f);
CParticle::AddParticle(PARTICLE_ENGINE_STEAM,
@@ -1179,10 +1193,6 @@ CAutomobile::ProcessControl(void)
m_vecTurnSpeed.z = 0.0f;
}
}
-
-// TEMP
-if(pDriver)
- pDriver->m_fHealth = 100.0f;
}
void
@@ -1200,9 +1210,911 @@ CAutomobile::Teleport(CVector pos)
CWorld::Add(this);
}
-WRAPPER void CAutomobile::PreRender(void) { EAXJMP(0x535B40); }
-WRAPPER void CAutomobile::Render(void) { EAXJMP(0x539EA0); }
+void
+CAutomobile::PreRender(void)
+{
+ int i, j, n;
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+
+ if(GetModelIndex() == MI_RCBANDIT){
+ CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f);
+ CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f);
+ }
+
+ float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f;
+
+
+ // Wheel particles
+
+ if(GetModelIndex() == MI_DODO){
+ ; // nothing
+ }else if(GetModelIndex() == MI_RCBANDIT){
+ for(i = 0; i < 4; i++){
+ // Game has same code three times here
+ switch(m_aWheelState[i]){
+ case WHEEL_STATE_SPINNING:
+ case WHEEL_STATE_SKIDDING:
+ case WHEEL_STATE_FIXED:
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.05f),
+ CVector(0.0f, 0.0f, 0.0f), nil, 0.1f);
+ break;
+ }
+ }
+ }else{
+ if(m_status == STATUS_SIMPLE){
+ CMatrix mat;
+ CVector pos;
+
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB]));
+ pos = mat.GetPosition();
+ pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_RIGHT];
+ m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point = GetMatrix() * pos;
+ m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB = SURFACE_DEFAULT;
+
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB]));
+ pos = mat.GetPosition();
+ pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_LEFT];
+ m_aWheelColPoints[CARWHEEL_REAR_LEFT].point = GetMatrix() * pos;
+ m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB = SURFACE_DEFAULT;
+
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF]));
+ pos = mat.GetPosition();
+ pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_RIGHT];
+ m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point = GetMatrix() * pos;
+ m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB = SURFACE_DEFAULT;
+
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF]));
+ pos = mat.GetPosition();
+ pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_LEFT];
+ m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point = GetMatrix() * pos;
+ m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB = SURFACE_DEFAULT;
+ }
+
+ int drawParticles = Abs(fwdSpeed) < 90.0f;
+ if(m_status == STATUS_SIMPLE || m_status == STATUS_PHYSICS ||
+ m_status == STATUS_PLAYER || m_status == STATUS_PLAYER_PLAYBACKFROMBUFFER){
+ bool rearSkidding = false;
+ if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SKIDDING ||
+ m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SKIDDING)
+ rearSkidding = true;
+
+ for(i = 0; i < 4; i++){
+ switch(m_aWheelState[i]){
+ case WHEEL_STATE_SPINNING:
+ if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){
+ CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f),
+ CVector(0.0f, 0.0f, 0.0f));
+
+ CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f),
+ CVector(0.0f, 0.0f, 0.05f));
+ }
+
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f),
+ CVector(0.0f, 0.0f, 0.0f));
+
+ if(m_aWheelTimer[i] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]);
+ break;
+
+ case WHEEL_STATE_SKIDDING:
+ if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){
+ // same as below
+
+ AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles);
+
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f),
+ CVector(0.0f, 0.0f, 0.0f));
+
+ if(m_aWheelTimer[i] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]);
+ }
+ break;
+
+ case WHEEL_STATE_FIXED:
+ AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles);
+
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f),
+ CVector(0.0f, 0.0f, 0.0f));
+
+ if(m_aWheelTimer[i] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]);
+ break;
+
+ default:
+ if(Abs(fwdSpeed) > 0.5f)
+ AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles);
+ if(m_aWheelSkidmarkBloody[i] && m_aWheelTimer[i] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]);
+ }
+ }
+ }
+ }
+
+ if(m_aCarNodes[CAR_WHEEL_RM]){
+ // assume middle wheels are two units before rear ones
+ CVector offset = GetForward()*2.0f;
+
+ switch(m_aWheelState[CARWHEEL_REAR_LEFT]){
+ // Game has same code three times here
+ case WHEEL_STATE_SPINNING:
+ case WHEEL_STATE_SKIDDING:
+ case WHEEL_STATE_FIXED:
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + CVector(0.0f, 0.0f, 0.25f) + offset,
+ CVector(0.0f, 0.0f, 0.0f));
+
+ if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_LEFT,
+ m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + offset,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]);
+ break;
+ }
+
+ switch(m_aWheelState[CARWHEEL_REAR_RIGHT]){
+ // Game has same code three times here
+ case WHEEL_STATE_SPINNING:
+ case WHEEL_STATE_SKIDDING:
+ case WHEEL_STATE_FIXED:
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + CVector(0.0f, 0.0f, 0.25f) + offset,
+ CVector(0.0f, 0.0f, 0.0f));
+
+ if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f)
+ CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_RIGHT,
+ m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + offset,
+ GetForward().x, GetForward().y,
+ &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]);
+ break;
+ }
+ }
+
+
+ // Rain on roof
+ if(!CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() &&
+ Abs(fwdSpeed) < 20.0f && CWeather::Rain > 0.02f){
+ CColModel *colModel = GetColModel();
+
+ for(i = 0; i < colModel->numTriangles; i++){
+ CVector p1, p2, p3, c;
+
+ colModel->GetTrianglePoint(p1, colModel->triangles[i].a);
+ p1 = GetMatrix() * p1;
+ colModel->GetTrianglePoint(p2, colModel->triangles[i].b);
+ p2 = GetMatrix() * p2;
+ colModel->GetTrianglePoint(p3, colModel->triangles[i].c);
+ p3 = GetMatrix() * p3;
+ c = (p1 + p2 + p3)/3.0f;
+
+ n = 6.0f*CWeather::Rain;
+ for(j = 0; j <= n; j++)
+ CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP,
+ c + CVector(CGeneral::GetRandomNumberInRange(-.04f, 0.4f), CGeneral::GetRandomNumberInRange(-.04f, 0.4f), 0.0f),
+ CVector(0.0f, 0.0f, 0.0f),
+ nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1);
+ }
+ }
+
+ AddDamagedVehicleParticles();
+
+ // Exhaust smoke
+ if(bEngineOn && fwdSpeed < 90.0f){
+ CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST];
+ CVector pos1, pos2, dir;
+
+ if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){
+ dir.z = 0.0f;
+ if(fwdSpeed < 10.0f){
+ CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f);
+ steerFwd = Multiply3x3(GetMatrix(), steerFwd);
+ float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f);
+ dir.x = steerFwd.x * r;
+ dir.y = steerFwd.y * r;
+ }else{
+ dir.x = m_vecMoveSpeed.x;
+ dir.y = m_vecMoveSpeed.y;
+ }
+
+ bool dblExhaust = false;
+ pos1 = GetMatrix() * exhaustPos;
+ if(pHandling->Flags & HANDLING_DBL_EXHAUST){
+ dblExhaust = true;
+ pos2 = exhaustPos;
+ pos2.x = -pos2.x;
+ pos2 = GetMatrix() * pos2;
+ }
+
+ n = 4.0f*m_fGasPedal;
+ if(dblExhaust)
+ for(i = 0; i <= n; i++){
+ CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir);
+ CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir);
+ }
+ else
+ for(i = 0; i <= n; i++)
+ CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir);
+ }
+ }
+
+
+ // Siren and taxi lights
+ switch(GetModelIndex()){
+ case MI_FIRETRUCK:
+ case MI_AMBULAN:
+ case MI_POLICE:
+ case MI_ENFORCER:
+ if(m_bSirenOrAlarm){
+ CVector pos1, pos2;
+ uint8 r1, g1, b1;
+ uint8 r2, g2, b2;
+ uint8 r, g, b;
+
+ switch(GetModelIndex()){
+ case MI_FIRETRUCK:
+ pos1 = CVector(1.1f, 1.7f, 2.0f);
+ pos2 = CVector(-1.1f, 1.7f, 2.0f);
+ r1 = 255; g1 = 0; b1 = 0;
+ r2 = 255; g2 = 255; b2 = 0;
+ break;
+ case MI_AMBULAN:
+ pos1 = CVector(1.1f, 0.9f, 1.6f);
+ pos2 = CVector(-1.1f, 0.9f, 1.6f);
+ r1 = 255; g1 = 0; b1 = 0;
+ r2 = 255; g2 = 255; b2 = 255;
+ break;
+ case MI_POLICE:
+ pos1 = CVector(0.7f, -0.4f, 1.0f);
+ pos2 = CVector(-0.7f, -0.4f, 1.0f);
+ r1 = 255; g1 = 0; b1 = 0;
+ r2 = 0; g2 = 0; b2 = 255;
+ break;
+ case MI_ENFORCER:
+ pos1 = CVector(1.1f, 0.8f, 1.2f);
+ pos2 = CVector(-1.1f, 0.8f, 1.2f);
+ r1 = 255; g1 = 0; b1 = 0;
+ r2 = 0; g2 = 0; b2 = 255;
+ break;
+ }
+
+ uint32 t = CTimer::GetTimeInMilliseconds() & 0x3FF; // 1023
+ if(t < 512){
+ r = r1/6;
+ g = g1/6;
+ b = b1/6;
+ }else{
+ r = r2/6;
+ g = g2/6;
+ b = b2/6;
+ }
+
+ t = CTimer::GetTimeInMilliseconds() & 0x1FF; // 511
+ if(t < 100){
+ float f = t/100.0f;
+ r *= f;
+ g *= f;
+ b *= f;
+ }else if(t > 412){
+ float f = (512-t)/100.0f;
+ r *= f;
+ g *= f;
+ b *= f;
+ }
+
+ CVector pos = GetPosition();
+ float angle = (CTimer::GetTimeInMilliseconds() & 0x3FF)*TWOPI/0x3FF;
+ float s = 8.0f*Sin(angle);
+ float c = 8.0f*Cos(angle);
+ CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex,
+ &pos, c, s, s, -c, r, g, b, 8.0f);
+
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ pos + GetUp()*2.0f, CVector(0.0f, 0.0f, 0.0f), 12.0f,
+ r*0.02f, g*0.02f, b*0.02f, CPointLights::FOG_NONE, true);
+
+ pos1 = GetMatrix() * pos1;
+ pos2 = GetMatrix() * pos2;
+
+ for(i = 0; i < 4; i++){
+ uint8 sirenTimer = ((CTimer::GetTimeInMilliseconds() + (i<<6))>>8) & 3;
+ pos = (pos1*i + pos2*(3.0f-i))/3.0f;
+
+ switch(sirenTimer){
+ case 0:
+ CCoronas::RegisterCorona((uintptr)this + 21 + i,
+ r1, g1, b1, 255,
+ pos, 0.4f, 50.0f,
+ CCoronas::TYPE_STAR,
+ i == 1 ? CCoronas::FLARE_HEADLIGHTS : CCoronas::FLARE_NONE,
+ CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ break;
+ case 2:
+ CCoronas::RegisterCorona((uintptr)this + 21 + i,
+ r2, g2, b2, 255,
+ pos, 0.4f, 50.0f,
+ CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE,
+ CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ break;
+ default:
+ CCoronas::UpdateCoronaCoors((uintptr)this + 21 + i, pos, 50.0f, 0.0f);
+ break;
+ }
+ }
+ }
+ break;
+
+ case MI_FBICAR:
+ if(m_bSirenOrAlarm){
+ CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f);
+ if(CTimer::GetTimeInMilliseconds() & 0x100 &&
+ DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f)
+ CCoronas::RegisterCorona((uintptr)this + 21,
+ 0, 0, 255, 255,
+ pos, 0.4f, 50.0f,
+ CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE,
+ CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ else
+ CCoronas::UpdateCoronaCoors((uintptr)this + 21, pos, 50.0f, 0.0f);
+ }
+ break;
+
+ case MI_TAXI:
+ case MI_CABBIE:
+ case MI_BORGNINE:
+ if(bTaxiLight){
+ CVector pos = GetPosition() + GetUp()*0.95f;
+ CCoronas::RegisterCorona((uintptr)this + 21,
+ 128, 128, 0, 255,
+ pos, 0.8f, 50.0f,
+ CCoronas::TYPE_NORMAL,
+ CCoronas::FLARE_NONE,
+ CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ pos, CVector(0.0f, 0.0f, 0.0f), 10.0f,
+ 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true);
+ }
+ break;
+ }
+
+ if(GetModelIndex() == MI_RCBANDIT ||
+ GetModelIndex() == MI_DODO ||
+ GetModelIndex() == MI_RHINO)
+ goto nolights;
+
+ // Turn lights on/off
+ bool shouldLightsBeOn =
+ CClock::GetHours() > 20 ||
+ CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) ||
+ CClock::GetHours() < 7 ||
+ CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) ||
+ m_randomSeed/50000.0f < CWeather::Foggyness ||
+ m_randomSeed/50000.0f < CWeather::WetRoads;
+ if(shouldLightsBeOn != bLightsOn && m_status != STATUS_WRECKED){
+ if(m_status == STATUS_ABANDONED){
+ // Turn off lights on abandoned vehicles only when we they're far away
+ if(bLightsOn &&
+ Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f)
+ bLightsOn = false;
+ }else
+ bLightsOn = shouldLightsBeOn;
+ }
+
+ // Actually render the lights
+ bool alarmOn = false;
+ bool alarmOff = false;
+ if(IsAlarmOn()){
+ if(CTimer::GetTimeInMilliseconds() & 0x100)
+ alarmOn = true;
+ else
+ alarmOff = true;
+ }
+ if(bEngineOn && bLightsOn || alarmOn || alarmOff){
+ CVector lookVector = GetPosition() - TheCamera.GetPosition();
+ float camDist = lookVector.Magnitude();
+ if(camDist != 0.0f)
+ lookVector *= 1.0f/camDist;
+ else
+ lookVector = CVector(1.0f, 0.0f, 0.0f);
+
+ // 1.0 if directly behind car, -1.0 if in front
+ // BUG on PC: Abs of DotProduct is taken
+ float behindness = DotProduct(lookVector, GetForward());
+ behindness = clamp(behindness, -1.0f, 1.0f); // shouldn't be necessary
+ // 0.0 if behind car, PI if in front
+ // Abs not necessary
+ float angle = Abs(Acos(behindness));
+
+ // Headlights
+
+ CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS];
+ CVector lightR = GetMatrix() * headLightPos;
+ CVector lightL = lightR;
+ lightL -= GetRight()*2.0f*headLightPos.x;
+
+ // Headlight coronas
+ if(behindness < 0.0f){
+ // In front of car
+ float intensity = -0.5f*behindness + 0.3f;
+ float size = 1.0f - behindness;
+
+ if(behindness < -0.97f && camDist < 30.0f){
+ // Directly in front and not too far away
+ if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255,
+ lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 7, 150, 150, 195, 255,
+ lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle);
+ }else{
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255,
+ lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 7, 160, 160, 140, 255,
+ lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle);
+ }
+ }
+
+ if(alarmOff){
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0,
+ lightL, size, 0.0f,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 1, 0, 0, 0, 0,
+ lightR, size, 0.0f,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }else{
+ if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this, 190*intensity, 190*intensity, 255*intensity, 255,
+ lightL, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255,
+ lightR, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }else{
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this, 210*intensity, 210*intensity, 195*intensity, 255,
+ lightL, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255,
+ lightR, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }
+ }
+ }else{
+ // Behind car
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this, lightL, 50.0f*TheCamera.LODDistMultiplier, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 1, lightR, 50.0f*TheCamera.LODDistMultiplier, angle);
+ }
+
+ // bright lights
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK && !bNoBrightHeadLights)
+ CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4);
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights)
+ CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4);
+
+ // Taillights
+
+ CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS];
+ lightR = GetMatrix() * tailLightPos;
+ lightL = lightR;
+ lightL -= GetRight()*2.0f*tailLightPos.x;
+
+ // Taillight coronas
+ if(behindness > 0.0f){
+ // Behind car
+ float intensity = 0.4f*behindness + 0.4;
+ float size = (behindness + 1.0f)/2.0f;
+
+ if(m_fGasPedal < 0.0f){
+ // reversing
+ intensity += 0.4f;
+ size += 0.3f;
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 128*intensity, 128*intensity, 255,
+ lightL, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 128*intensity, 128*intensity, 255,
+ lightR, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }else{
+ if(m_fBrakePedal > 0.0){
+ intensity += 0.4f;
+ size += 0.3f;
+ }
+
+ if(alarmOff){
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 0,
+ lightL, size, 0.0f,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 3, 0, 0, 0, 0,
+ lightR, size, 0.0f,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }else{
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 0, 0, 255,
+ lightL, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 0, 0, 255,
+ lightR, size, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle);
+ }
+ }
+ }else{
+ // In front of car
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, angle);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, angle);
+ }
+
+ // bright lights
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8);
+
+ // Light shadows
+ if(!alarmOff){
+ CVector pos = GetPosition();
+ CVector2D fwd(GetForward());
+ fwd.Normalise();
+ float f = headLightPos.y + 6.0f;
+ pos += CVector(f*fwd.x, f*fwd.y, 2.0f);
+
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK ||
+ Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowHeadLightsTex, &pos,
+ 7.0f*fwd.x, 7.0f*fwd.y, 7.0f*fwd.y, -7.0f*fwd.x, 45, 45, 45, 7.0f);
+
+ f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f);
+ pos += CVector(f*fwd.x, f*fwd.y, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK ||
+ Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos,
+ 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f);
+ }
+
+ if(this == FindPlayerVehicle() && !alarmOff){
+ if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK ||
+ Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK)
+ CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(),
+ 20.0f, 1.0f, 1.0f, 1.0f,
+ FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE,
+ false);
+ CVector pos = GetPosition() - 4.0f*GetForward();
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK ||
+ Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ if(m_fBrakePedal > 0.0f)
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f),
+ 10.0f, 1.0f, 0.0f, 0.0f,
+ CPointLights::FOG_NONE, false);
+ else
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f),
+ 7.0f, 0.6f, 0.0f, 0.0f,
+ CPointLights::FOG_NONE, false);
+ }
+ }else{
+ // Lights off
+
+ if(m_status == STATUS_ABANDONED || m_status == STATUS_WRECKED)
+ goto nolights;
+
+ CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS];
+ CVector lightR = GetMatrix() * lightPos;
+ CVector lightL = lightR;
+ lightL -= GetRight()*2.0f*lightPos.x;
+
+ if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){
+ CVector lookVector = GetPosition() - TheCamera.GetPosition();
+ lookVector.Normalise();
+ float behindness = DotProduct(lookVector, GetForward());
+ if(behindness > 0.0f){
+ if(m_fGasPedal < 0.0f){
+ // reversing
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 2, 120, 120, 120, 255,
+ lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 3, 120, 120, 120, 255,
+ lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4);
+ }else{
+ // braking
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 2, 120, 0, 0, 255,
+ lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::RegisterCorona((uintptr)this + 3, 120, 0, 0, 255,
+ lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier,
+ CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8);
+ }
+ }else{
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f);
+ }
+ }else{
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f);
+ if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK)
+ CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f);
+ }
+ }
+
+nolights:
+ CShadows::StoreShadowForCar(this);
+}
+
+void
+CAutomobile::Render(void)
+{
+ int i;
+ CMatrix mat;
+ CVector pos;
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+ if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){
+ // Rhino has no bonnet...what are we doing here?
+ CMatrix m;
+ CVector p;
+ m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET]));
+ p = m.GetPosition();
+ m.SetRotateZ(m_fCarGunLR);
+ m.Translate(p);
+ m.UpdateRW();
+ }
+
+ CVector contactPoints[4]; // relative to model
+ CVector contactSpeeds[4]; // speed at contact points
+ CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f));
+ CVector rearWheelFwd = GetForward();
+ for(i = 0; i < 4; i++){
+ contactPoints[i] = m_aWheelColPoints[i].point - GetPosition();
+ contactSpeeds[i] = GetSpeed(contactPoints[i]);
+ if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT)
+ m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], frontWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale);
+ else
+ m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], rearWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale);
+ m_aWheelRotation[i] += m_aWheelSpeed[i];
+ }
+
+ // Rear right wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT];
+ if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT]));
+ else
+ mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]));
+
+ // Rear left wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT];
+ if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT]));
+ else
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]));
+
+ // Mid right wheel
+ if(m_aCarNodes[CAR_WHEEL_RM]){
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT];
+ if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT]));
+ else
+ mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM]));
+ }
+
+ // Mid left wheel
+ if(m_aCarNodes[CAR_WHEEL_LM]){
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT];
+ if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT]));
+ else
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM]));
+ }
+
+ if(GetModelIndex() == MI_DODO){
+ // Front wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT];
+ if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT]));
+ else
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]));
+
+ // Rotate propeller
+ if(m_aCarNodes[CAR_WINDSCREEN]){
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN]));
+ pos = mat.GetPosition();
+ mat.SetRotateY(m_fPropellerRotation);
+ mat.Translate(pos);
+ mat.UpdateRW();
+
+ m_fPropellerRotation += m_fGasPedal != 0.0f ? TWOPI/13.0f : TWOPI/26.0f;
+ if(m_fPropellerRotation > TWOPI)
+ m_fPropellerRotation -= TWOPI;
+ }
+
+ // Rudder
+ if(Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING && m_aCarNodes[CAR_BOOT]){
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT]));
+ pos = mat.GetPosition();
+ mat.SetRotate(0.0f, 0.0f, -m_fSteerAngle);
+ mat.Rotate(0.0f, Sin(m_fSteerAngle)*DEGTORAD(22.0f), 0.0f);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ }
+
+ ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT);
+ ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT);
+ }else if(GetModelIndex() == MI_RHINO){
+ // Front right wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT];
+ // no damaged wheels or steering
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, 0.0f);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]));
+
+ // Front left wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT];
+ // no damaged wheels or steering
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]));
+ }else{
+ // Front right wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT];
+ if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT]));
+ else
+ mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]));
+
+ // Front left wheel
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF]));
+ pos.x = mat.GetPosition().x;
+ pos.y = mat.GetPosition().y;
+ pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT];
+ if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST)
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle+0.3f*Sin(-m_aWheelRotation[CARWHEEL_FRONT_LEFT]));
+ else
+ mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle);
+ mat.Scale(mi->m_wheelScale);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ if(CVehicle::bWheelsOnlyCheat)
+ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]));
+
+ ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT);
+ ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT);
+ ProcessSwingingDoor(CAR_DOOR_LR, DOOR_REAR_LEFT);
+ ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT);
+ ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET);
+ ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT);
+
+ mi->SetVehicleColour(m_currentColour1, m_currentColour2);
+ }
+
+ if(!CVehicle::bWheelsOnlyCheat)
+ CEntity::Render();
+}
int32
CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints)
@@ -1213,7 +2125,7 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints)
if(m_status != STATUS_SIMPLE)
bVehicleColProcessed = true;
- if(m_veh_flagC80)
+ if(bUsingSpecialColModel)
colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel;
else
colModel = GetColModel();
@@ -1416,24 +2328,594 @@ CAutomobile::ProcessControlInputs(uint8 pad)
}
}
-WRAPPER void
+void
CAutomobile::FireTruckControl(void)
-{ EAXJMP(0x522590);
+{
+ if(this == FindPlayerVehicle()){
+ if(!CPad::GetPad(0)->GetWeapon())
+ return;
+ m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight()*0.00025f*CTimer::GetTimeStep();
+ m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown()*0.0001f*CTimer::GetTimeStep();
+ m_fCarGunUD = clamp(m_fCarGunUD, 0.05f, 0.3f);
+
+ CVector cannonPos(0.0f, 1.5f, 1.9f);
+ cannonPos = GetMatrix() * cannonPos;
+ CVector cannonDir(
+ Sin(m_fCarGunLR) * Cos(m_fCarGunUD),
+ Cos(m_fCarGunLR) * Cos(m_fCarGunUD),
+ Sin(m_fCarGunUD));
+ cannonDir = Multiply3x3(GetMatrix(), cannonDir);
+ cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f;
+ CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir);
+ }else if(m_status == STATUS_PHYSICS){
+ CFire *fire = gFireManager.FindFurthestFire_NeverMindFireMen(GetPosition(), 10.0f, 35.0f);
+ if(fire == nil)
+ return;
+
+ // Target cannon onto fire
+ float targetAngle = CGeneral::GetATanOfXY(fire->m_vecPos.x-GetPosition().x, fire->m_vecPos.y-GetPosition().y);
+ float fwdAngle = CGeneral::GetATanOfXY(GetForward().x, GetForward().y);
+ float targetCannonAngle = fwdAngle - targetAngle;
+ float angleDelta = CTimer::GetTimeStep()*0.01f;
+ float cannonDelta = targetCannonAngle - m_fCarGunLR;
+ while(cannonDelta < PI) cannonDelta += TWOPI;
+ while(cannonDelta > PI) cannonDelta -= TWOPI;
+ if(Abs(cannonDelta) < angleDelta)
+ m_fCarGunLR = targetCannonAngle;
+ else if(cannonDelta > 0.0f)
+ m_fCarGunLR += angleDelta;
+ else
+ m_fCarGunLR -= angleDelta;
+
+ // Go up and down a bit
+ float upDown = Sin((float)(CTimer::GetTimeInMilliseconds() & 0xFFF)/0x1000 * TWOPI);
+ m_fCarGunUD = 0.2f + 0.2f*upDown;
+
+ // Spray water every once in a while
+ if((CTimer::GetTimeInMilliseconds()>>10) & 3){
+ CVector cannonPos(0.0f, 0.0f, 2.2f); // different position than player's firetruck!
+ cannonPos = GetMatrix() * cannonPos;
+ CVector cannonDir(
+ Sin(m_fCarGunLR) * Cos(m_fCarGunUD),
+ Cos(m_fCarGunLR) * Cos(m_fCarGunUD),
+ Sin(m_fCarGunUD));
+ cannonDir = Multiply3x3(GetMatrix(), cannonDir);
+ cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f;
+ CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir);
+ }
+ }
}
-WRAPPER void
+void
CAutomobile::TankControl(void)
-{ EAXJMP(0x53D530);
+{
+ int i;
+
+ // These coords are 1 unit higher then they should be relative to model center
+ CVector turrentBase(0.0f, -1.394f, 2.296f);
+ CVector gunEnd(0.0f, 1.813f, 2.979f);
+ CVector baseToEnd = gunEnd - turrentBase;
+
+ if(this != FindPlayerVehicle())
+ return;
+ if(CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING)
+ return;
+
+ // Rotate turret
+ float prevAngle = m_fCarGunLR;
+ m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep();
+ if(m_fCarGunLR < 0.0f)
+ m_fCarGunLR += TWOPI;
+ if(m_fCarGunLR > TWOPI)
+ m_fCarGunLR -= TWOPI;
+ if(m_fCarGunLR != prevAngle)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(m_fCarGunLR - prevAngle));
+
+ // Shoot
+ if(CPad::GetPad(0)->CarGunJustDown() &&
+ CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun + 800){
+ CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun = CTimer::GetTimeInMilliseconds();
+
+ // more like -sin(angle), cos(angle), i.e. rotated (0,1,0)
+ CVector turretDir = CVector(Sin(-m_fCarGunLR), Cos(-m_fCarGunLR), 0.0f);
+ turretDir = Multiply3x3(GetMatrix(), turretDir);
+
+ float c = Cos(m_fCarGunLR);
+ float s = Sin(m_fCarGunLR);
+ CVector rotatedEnd(
+ c*baseToEnd.x - s*baseToEnd.y,
+ s*baseToEnd.x + c*baseToEnd.y,
+ baseToEnd.z - 1.0f); // correct offset here
+ rotatedEnd += turrentBase;
+
+ CVector point1 = GetMatrix() * rotatedEnd;
+ CVector point2 = point1 + 60.0f*turretDir;
+ m_vecMoveSpeed -= 0.06f*turretDir;
+ m_vecMoveSpeed.z += 0.05f;
+
+ CWeapon::DoTankDoomAiming(FindPlayerVehicle(), FindPlayerPed(), &point1, &point2);
+ CColPoint colpoint;
+ CEntity *entity = nil;
+ CWorld::ProcessLineOfSight(point1, point2, colpoint, entity, true, true, true, true, true, true, false);
+ if(entity)
+ point2 = colpoint.point - 0.04f*(colpoint.point - point1);
+
+ CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_TANK_GRENADE, point2, 0);
+
+ // Add particles on the way to the explosion;
+ float shotDist = (point2 - point1).Magnitude();
+ int n = shotDist/4.0f;
+ RwRGBA black = { 0, 0, 0, 0 };
+ for(i = 0; i < n; i++){
+ float f = (float)i/n;
+ CParticle::AddParticle(PARTICLE_HELI_DUST,
+ point1 + f*(point2 - point1),
+ CVector(0.0f, 0.0f, 0.0f),
+ nil, 0.1f, black);
+ }
+
+ // More particles
+ CVector shotDir = point2 - point1;
+ shotDir.Normalise();
+ for(i = 0; i < 15; i++){
+ float f = i/15.0f;
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1,
+ shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f,
+ nil, CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*f, black);
+ }
+
+ // And some gun flashes near the gun
+ CVector flashPos = point1;
+ CVector nullDir(0.0f, 0.0f, 0.0f);
+ int lifeSpan = 250;
+ if(m_vecMoveSpeed.Magnitude() > 0.08f){
+ lifeSpan = 125;
+ flashPos.x += 0.5f*m_vecMoveSpeed.x;
+ flashPos.y += 0.5f*m_vecMoveSpeed.y;
+ }
+ CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.4f, black, 0, 0, 0, lifeSpan);
+ flashPos += 0.3f*shotDir;
+ CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.2f, black, 0, 0, 0, lifeSpan);
+ flashPos += 0.1f*shotDir;
+ CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.15f, black, 0, 0, 0, lifeSpan);
+ }
+
+ // Actually update turret node
+ if(m_aCarNodes[CAR_WINDSCREEN]){
+ CMatrix mat;
+ CVector pos;
+
+ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN]));
+ pos = mat.GetPosition();
+ mat.SetRotateZ(m_fCarGunLR);
+ mat.Translate(pos);
+ mat.UpdateRW();
+ }
}
-WRAPPER void
+void
CAutomobile::HydraulicControl(void)
-{ EAXJMP(0x52D4E0);
+{
+ int i;
+ float wheelPositions[4];
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+ CColModel *normalColModel = mi->GetColModel();
+ float wheelRadius = 0.5f*mi->m_wheelScale;
+ CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus];
+ CColModel *specialColModel = &playerInfo->m_ColModel;
+
+ if(m_status != STATUS_PLAYER){
+ // reset hydraulics for non-player cars
+
+ if(!bUsingSpecialColModel)
+ return;
+ if(specialColModel != nil) // this is always true
+ for(i = 0; i < 4; i++)
+ wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ for(i = 0; i < 4; i++){
+ m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit;
+ m_aSuspensionLineLength[i] = normalColModel->lines[i].p0.z - normalColModel->lines[i].p1.z;
+ m_aSuspensionSpringRatio[i] = (normalColModel->lines[i].p0.z - wheelPositions[i]) / m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ }
+
+ if(m_hydraulicState == 0)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f);
+ else if(m_hydraulicState >= 100)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+
+ if(playerInfo->m_pVehicleEx == this)
+ playerInfo->m_pVehicleEx = nil;
+ bUsingSpecialColModel = false;
+ m_hydraulicState = 0;
+ return;
+ }
+
+ // Player car
+
+ float normalUpperLimit = pHandling->fSuspensionUpperLimit;
+ float normalLowerLimit = pHandling->fSuspensionLowerLimit;
+ float normalSpringLength = normalUpperLimit - normalLowerLimit;
+ float extendedUpperLimit = normalUpperLimit - 0.2f;
+ float extendedLowerLimit = normalLowerLimit - 0.2f;
+ float extendedSpringLength = extendedUpperLimit - extendedLowerLimit;
+
+ if(!bUsingSpecialColModel){
+ // Init special col model
+
+ if(playerInfo->m_pVehicleEx && playerInfo->m_pVehicleEx == this)
+ playerInfo->m_pVehicleEx->bUsingSpecialColModel = false;
+ playerInfo->m_pVehicleEx = this;
+ playerInfo->m_ColModel = *normalColModel;
+ bUsingSpecialColModel = true;
+ specialColModel = &playerInfo->m_ColModel;
+
+ if(m_fVelocityChangeForAudio > 0.1f)
+ m_hydraulicState = 20;
+ else{
+ m_hydraulicState = 0;
+ normalUpperLimit += -0.12f;
+ normalSpringLength = normalUpperLimit - (normalLowerLimit+0.14f);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+ }
+
+ // Setup suspension
+ float normalLineLength = normalSpringLength + wheelRadius;
+ CVector pos;
+ for(i = 0; i < 4; i++){
+ wheelPositions[i] = normalColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ mi->GetWheelPosn(i, pos);
+ pos.z += normalUpperLimit;
+ specialColModel->lines[i].p0 = pos;
+ pos.z -= normalLineLength;
+ specialColModel->lines[i].p1 = pos;
+ m_aSuspensionSpringLength[i] = normalSpringLength;
+ m_aSuspensionLineLength[i] = normalLineLength;
+
+ if(m_aSuspensionSpringRatio[i] < 1.0f){
+ m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ }
+ }
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+
+ // Adjust col model
+ mi->GetWheelPosn(0, pos);
+ float minz = pos.z + extendedLowerLimit - wheelRadius;
+ if(minz < specialColModel->boundingBox.min.z)
+ specialColModel->boundingBox.min.z = minz;
+ float radius = max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude());
+ if(specialColModel->boundingSphere.radius < radius)
+ specialColModel->boundingSphere.radius = radius;
+
+ }
+
+ if(playerInfo->m_WBState != WBSTATE_PLAYING)
+ return;
+
+ bool setPrevRatio = false;
+ if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){
+ if(m_hydraulicState == 0){
+ m_hydraulicState = 20;
+ for(i = 0; i < 4; i++)
+ m_aWheelPosition[i] -= 0.06f;
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f);
+ setPrevRatio = true;
+ }else{
+ m_hydraulicState++;
+ }
+ }else if(m_hydraulicState != 0){ // must always be true
+ if(m_hydraulicState < 21 && m_fVelocityChangeForAudio < 0.1f){
+ m_hydraulicState--;
+ if(m_hydraulicState == 0)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+ }
+ }
+
+ if(CPad::GetPad(0)->HornJustDown()){
+ // Switch between normal and extended
+
+ if(m_hydraulicState < 100)
+ m_hydraulicState = 100;
+ else{
+ if(m_fVelocityChangeForAudio > 0.1f)
+ m_hydraulicState = 20;
+ else
+ m_hydraulicState = 0;
+ }
+
+ if(m_hydraulicState < 100){
+ if(m_hydraulicState == 0){
+ normalUpperLimit += -0.12f;
+ normalLowerLimit += 0.14f;
+ normalSpringLength = normalUpperLimit - normalLowerLimit;
+ }
+
+ // Reset suspension to normal
+ float normalLineLength = normalSpringLength + wheelRadius;
+ CVector pos;
+ for(i = 0; i < 4; i++){
+ wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ mi->GetWheelPosn(i, pos);
+ pos.z += normalUpperLimit;
+ specialColModel->lines[i].p0 = pos;
+ pos.z -= normalLineLength;
+ specialColModel->lines[i].p1 = pos;
+ m_aSuspensionSpringLength[i] = normalSpringLength;
+ m_aSuspensionLineLength[i] = normalLineLength;
+
+ if(m_aSuspensionSpringRatio[i] < 1.0f){
+ m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ }
+ }
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+ }else{
+ // Reset suspension to extended
+ float extendedLineLength = extendedSpringLength + wheelRadius;
+ CVector pos;
+ for(i = 0; i < 4; i++){
+ wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ mi->GetWheelPosn(i, pos);
+ pos.z += extendedUpperLimit;
+ specialColModel->lines[i].p0 = pos;
+ pos.z -= extendedLineLength;
+ specialColModel->lines[i].p1 = pos;
+ m_aSuspensionSpringLength[i] = extendedSpringLength;
+ m_aSuspensionLineLength[i] = extendedLineLength;
+
+ if(m_aSuspensionSpringRatio[i] < 1.0f){
+ m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ }
+
+ setPrevRatio = true;
+ m_aWheelPosition[i] -= 0.05f;
+ }
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+ }
+ }else{
+ float suspChange[4];
+ float maxDelta = 0.0f;
+ float rear = CPad::GetPad(0)->GetCarGunUpDown()/128.0f;
+ float front = -rear;
+ float right = CPad::GetPad(0)->GetCarGunLeftRight()/128.0f;
+ float left = -right;
+ suspChange[CARWHEEL_FRONT_LEFT] = max(front+left, 0.0f);
+ suspChange[CARWHEEL_REAR_LEFT] = max(rear+left, 0.0f);
+ suspChange[CARWHEEL_FRONT_RIGHT] = max(front+right, 0.0f);
+ suspChange[CARWHEEL_REAR_RIGHT] = max(rear+right, 0.0f);
+
+ if(m_hydraulicState < 100){
+ // Lowered, move wheels up
+
+ if(m_hydraulicState == 0){
+ normalUpperLimit += -0.12f;
+ normalLowerLimit += 0.14f;
+ normalSpringLength = normalUpperLimit - normalLowerLimit;
+ }
+
+ // Set suspension
+ CVector pos;
+ for(i = 0; i < 4; i++){
+ if(suspChange[i] > 1.0f)
+ suspChange[i] = 1.0f;
+
+ float oldZ = specialColModel->lines[i].p1.z;
+ float upperLimit = suspChange[i]*(extendedUpperLimit-normalUpperLimit) + normalUpperLimit;
+ float springLength = suspChange[i]*(extendedSpringLength-normalSpringLength) + normalSpringLength;
+ float lineLength = springLength + wheelRadius;
+
+ wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ mi->GetWheelPosn(i, pos);
+ pos.z += upperLimit;
+ specialColModel->lines[i].p0 = pos;
+ pos.z -= lineLength;
+ if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta))
+ maxDelta = pos.z - specialColModel->lines[i].p1.z;
+ specialColModel->lines[i].p1 = pos;
+ m_aSuspensionSpringLength[i] = springLength;
+ m_aSuspensionLineLength[i] = lineLength;
+
+ if(m_aSuspensionSpringRatio[i] < 1.0f){
+ m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ m_aWheelPosition[i] -= (oldZ - specialColModel->lines[i].p1.z)*0.3f;
+ }
+ }
+ }else{
+ if(m_hydraulicState < 104){
+ m_hydraulicState++;
+ for(i = 0; i < 4; i++)
+ m_aWheelPosition[i] -= 0.1f;
+ }
+
+ if(m_fVelocityChangeForAudio < 0.1f){
+ normalUpperLimit += -0.12f;
+ normalLowerLimit += 0.14f;
+ normalSpringLength = normalUpperLimit - normalLowerLimit;
+ }
+
+ // Set suspension
+ CVector pos;
+ for(i = 0; i < 4; i++){
+ if(suspChange[i] > 1.0f)
+ suspChange[i] = 1.0f;
+
+ float upperLimit = suspChange[i]*(normalUpperLimit-extendedUpperLimit) + extendedUpperLimit;
+ float springLength = suspChange[i]*(normalSpringLength-extendedSpringLength) + extendedSpringLength;
+ float lineLength = springLength + wheelRadius;
+
+ wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i];
+ mi->GetWheelPosn(i, pos);
+ pos.z += upperLimit;
+ specialColModel->lines[i].p0 = pos;
+ pos.z -= lineLength;
+ if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta))
+ maxDelta = pos.z - specialColModel->lines[i].p1.z;
+ specialColModel->lines[i].p1 = pos;
+ m_aSuspensionSpringLength[i] = springLength;
+ m_aSuspensionLineLength[i] = lineLength;
+
+ if(m_aSuspensionSpringRatio[i] < 1.0f){
+ m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i];
+ if(m_aSuspensionSpringRatio[i] > 1.0f)
+ m_aSuspensionSpringRatio[i] = 1.0f;
+ }
+ }
+ }
+
+ float limitDiff = extendedLowerLimit - normalLowerLimit;
+ if(limitDiff != 0.0f && Abs(maxDelta/limitDiff) > 0.01f){
+ float f = (maxDelta + limitDiff)/2.0f/limitDiff;
+ f = clamp(f, 0.0f, 1.0f);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_3, f);
+ if(f < 0.4f || f > 0.6f)
+ setPrevRatio = true;
+ if(f < 0.25f)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
+ else if(f > 0.75f)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f);
+ }
+ }
+
+ if(setPrevRatio)
+ for(i = 0; i < 4; i++){
+ // wheel radius in relation to suspension line
+ float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i];
+ m_aSuspensionSpringRatioPrev[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius);
+ }
}
-WRAPPER void
-CAutomobile::ProcessBuoyancy(void)
-{ EAXJMP(0x5308D0);
+void
+CAutomobile::ProcessBuoyancy(void)
+{
+ int i;
+ CVector impulse, point;
+
+ if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){
+ m_flagD8 = true;
+ ApplyMoveForce(impulse);
+ ApplyTurnForce(impulse, point);
+
+ CVector initialSpeed = m_vecMoveSpeed;
+ float timeStep = max(CTimer::GetTimeStep(), 0.01f);
+ float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep);
+ float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep());
+ m_vecMoveSpeed *= waterResistance;
+ m_vecTurnSpeed *= waterResistance;
+
+ if(impulseRatio > 0.5f){
+ bIsInWater = true;
+ if(m_vecMoveSpeed.z < -0.1f)
+ m_vecMoveSpeed.z = -0.1f;
+
+ if(pDriver){
+ pDriver->bIsInWater = true;
+ if(pDriver->IsPlayer() || !bWaterTight)
+ pDriver->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0);
+ }
+ for(i = 0; i < m_nNumMaxPassengers; i++)
+ if(pPassengers[i]){
+ pPassengers[i]->bIsInWater = true;
+ if(pPassengers[i]->IsPlayer() || !bWaterTight)
+ pPassengers[i]->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0);
+ }
+ }else
+ bIsInWater = false;
+
+ static uint32 nGenerateRaindrops = 0;
+ static uint32 nGenerateWaterCircles = 0;
+
+ if(initialSpeed.z < -0.3f && impulse.z > 0.3f){
+ RwRGBA color;
+ color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed())*0.45f*255;
+ color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen())*0.45f*255;
+ color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue())*0.45f*255;
+ color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128;
+ CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(),
+ CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.3f)),
+ 0.0f, 75, color, true);
+
+ nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300;
+ nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60;
+ if(m_vecMoveSpeed.z < -0.2f)
+ m_vecMoveSpeed.z = -0.2f;
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f);
+ }
+
+ if(nGenerateWaterCircles > 0 && nGenerateWaterCircles < CTimer::GetTimeInMilliseconds()){
+ CVector pos = GetPosition();
+ float waterLevel = 0.0f;
+ if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false))
+ pos.z = waterLevel;
+ static RwRGBA black;
+ if(pos.z != 0.0f){
+ nGenerateWaterCircles = 0;
+ pos.z += 1.0f;
+ for(i = 0; i < 4; i++){
+ CVector p = pos;
+ p.x += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f);
+ p.y += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f);
+ CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW,
+ p, CVector(0.0f, 0.0f, 0.0f),
+ nil, 0.0f, black, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ if(nGenerateRaindrops > 0 && nGenerateRaindrops < CTimer::GetTimeInMilliseconds()){
+ CVector pos = GetPosition();
+ float waterLevel = 0.0f;
+ if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false))
+ pos.z = waterLevel;
+ static RwRGBA black;
+ if(pos.z >= 0.0f){
+ nGenerateRaindrops = 0;
+ pos.z += 0.5;
+ CParticleObject::AddObject(POBJECT_SPLASHES_AROUND,
+ pos, CVector(0.0f, 0.0f, 0.0f), 6.5f, 2500, black, true);
+ }
+ }
+ }else{
+ bIsInWater = false;
+ m_flagD8 = false;
+
+ static RwRGBA splashCol = {155, 155, 185, 196};
+ static RwRGBA smokeCol = {255, 255, 255, 255};
+
+ for(i = 0; i < 4; i++){
+ if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].surfaceB == SURFACE_PUDDLE){
+ CVector pos = m_aWheelColPoints[i].point + 0.3f*GetUp() - GetPosition();
+ CVector vSpeed = GetSpeed(pos);
+ vSpeed.z = 0.0f;
+ float fSpeed = vSpeed.MagnitudeSqr();
+ if(fSpeed > sq(0.05f)){
+ fSpeed = Sqrt(fSpeed);
+ float size = min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f);
+ CVector right = 0.2f*fSpeed*GetRight() + 0.2f*vSpeed;
+
+ CParticle::AddParticle(PARTICLE_PED_SPLASH,
+ pos + GetPosition(), -0.5f*right,
+ nil, size, splashCol,
+ CGeneral::GetRandomNumberInRange(0.0f, 10.0f),
+ CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, 0);
+
+ CParticle::AddParticle(PARTICLE_RUBBER_SMOKE,
+ pos + GetPosition(), -0.6f*right,
+ nil, size, smokeCol, 0, 0, 0, 0);
+
+ if((CTimer::GetFrameCounter() & 0xF) == 0)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed);
+ }
+ }
+ }
+ }
}
void
@@ -1896,6 +3378,112 @@ CAutomobile::dmgDrawCarCollidingParticles(const CVector &pos, float amount)
}
void
+CAutomobile::AddDamagedVehicleParticles(void)
+{
+ if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson())
+ return;
+
+ uint8 engineStatus = Damage.GetEngineStatus();
+ if(engineStatus < ENGINE_STATUS_STEAM1)
+ return;
+
+ float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()) * 180.0f;
+ CVector direction = 0.5f*m_vecMoveSpeed;
+ CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS];
+
+ switch(Damage.GetDoorStatus(DOOR_BONNET)){
+ case DOOR_STATUS_OK:
+ case DOOR_STATUS_SMASHED:
+ // Bonnet is still there, smoke comes out at the edge
+ damagePos += vecDAMAGE_ENGINE_POS_SMALL;
+ break;
+ case DOOR_STATUS_SWINGING:
+ case DOOR_STATUS_MISSING:
+ // Bonnet is gone, smoke comes out at the engine
+ damagePos += vecDAMAGE_ENGINE_POS_BIG;
+ break;
+ }
+
+ if(GetModelIndex() == MI_BFINJECT)
+ damagePos = CVector(0.3f, -1.5f, -0.1f);
+
+ damagePos = GetMatrix()*damagePos;
+ damagePos.z += 0.15f;
+
+ if(engineStatus < ENGINE_STATUS_STEAM2){
+ if(fwdSpeed < 90.0f){
+ direction.z += 0.05f;
+ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.1f);
+ }
+ }else if(engineStatus < ENGINE_STATUS_SMOKE){
+ if(fwdSpeed < 90.0f)
+ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f);
+ }else if(engineStatus < ENGINE_STATUS_ON_FIRE){
+ if(fwdSpeed < 90.0f){
+ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f);
+ CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.3f*direction, nil, 0.0f);
+ }
+ }else if(m_fHealth > 250.0f){
+ if(fwdSpeed < 90.0f)
+ CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.2f*direction, nil, 0.0f);
+ }
+}
+
+int32
+CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed)
+{
+ int i;
+ CVector dir;
+ static RwRGBA grassCol = { 8, 24, 8, 255 };
+ static RwRGBA dirtCol = { 64, 64, 64, 255 };
+ static RwRGBA dirttrackCol = { 64, 32, 16, 255 };
+ static RwRGBA waterCol = { 48, 48, 64, 0 };
+
+ if(!belowEffectSpeed)
+ return 0;
+
+ switch(colpoint->surfaceB){
+ case SURFACE_GRASS:
+ dir.x = -0.05f*m_vecMoveSpeed.x;
+ dir.y = -0.05f*m_vecMoveSpeed.y;
+ for(i = 0; i < 4; i++){
+ dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f);
+ CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil,
+ CGeneral::GetRandomNumberInRange(0.02f, 0.1f), grassCol);
+ }
+ return 0;
+ case SURFACE_DIRT:
+ dir.x = -0.05f*m_vecMoveSpeed.x;
+ dir.y = -0.05f*m_vecMoveSpeed.y;
+ for(i = 0; i < 4; i++){
+ dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f);
+ CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil,
+ CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirtCol);
+ }
+ return 1;
+ case SURFACE_DIRTTRACK:
+ dir.x = -0.05f*m_vecMoveSpeed.x;
+ dir.y = -0.05f*m_vecMoveSpeed.y;
+ for(i = 0; i < 4; i++){
+ dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f);
+ CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil,
+ CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirttrackCol);
+ }
+ return 0;
+ default:
+ // Is this even visible?
+ if(CWeather::WetRoads > 0.01f && CTimer::GetFrameCounter() & 1){
+ CParticle::AddParticle(PARTICLE_WATERSPRAY,
+ colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f),
+ CVector(0.0f, 0.0f, 1.0f), nil,
+ CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol);
+ return 0;
+ }
+ return 1;
+ }
+}
+
+void
CAutomobile::GetComponentWorldPosition(int32 component, CVector &pos)
{
if(m_aCarNodes[component] == nil){
@@ -2192,9 +3780,9 @@ CAutomobile::BlowUpCar(CEntity *culprit)
gFireManager.StartFire(this, culprit, 0.8f, 1); // TODO
CDarkel::RegisterCarBlownUpByPlayer(this);
if(GetModelIndex() == MI_RCBANDIT)
- CExplosion::AddExplosion(this, culprit, EXPLOSION_4, GetPosition(), 0); // TODO
+ CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR_QUICK, GetPosition(), 0);
else
- CExplosion::AddExplosion(this, culprit, EXPLOSION_3, GetPosition(), 0); // TODO
+ CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0);
}
bool
@@ -2253,7 +3841,64 @@ CAutomobile::BurstTyre(uint8 wheel)
}
}
-WRAPPER bool CAutomobile::IsRoomForPedToLeaveCar(uint32, CVector *) { EAXJMP(0x53C5B0); }
+bool
+CAutomobile::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset)
+{
+ CColPoint colpoint;
+ CEntity *ent;
+ colpoint.point = CVector(0.0f, 0.0f, 0.0f);
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+
+ CVector seatPos;
+ switch(component){
+ case CAR_DOOR_RF:
+ seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT];
+ break;
+ case CAR_DOOR_LF:
+ seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT];
+ seatPos.x = -seatPos.x;
+ break;
+ case CAR_DOOR_RR:
+ seatPos = mi->m_positions[CAR_POS_BACKSEAT];
+ break;
+ case CAR_DOOR_LR:
+ seatPos = mi->m_positions[CAR_POS_BACKSEAT];
+ seatPos.x = -seatPos.x;
+ break;
+ }
+ seatPos = GetMatrix() * seatPos;
+
+ CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component);
+ if(doorOffset){
+ CVector off = *doorOffset;
+ if(component == CAR_DOOR_RF || component == CAR_DOOR_RR)
+ off.x = -off.x;
+ doorPos += Multiply3x3(GetMatrix(), off);
+ }
+
+ if(GetUp().z < 0.0f){
+ seatPos.z += 0.5f;
+ doorPos.z += 0.5f;
+ }
+
+ CVector dist = doorPos - seatPos;
+ float length = dist.Magnitude();
+ CVector pedPos = seatPos + dist*((length+0.6f)/length);
+
+ if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false))
+ return false;
+ if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false))
+ return false;
+ if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil))
+ if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f)
+ return false;
+ float upperZ = colpoint.point.z;
+ if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil))
+ return false;
+ if(upperZ != 0.0f && upperZ < colpoint.point.z)
+ return false;
+ return true;
+}
float
CAutomobile::GetHeightAboveRoad(void)
@@ -2300,7 +3945,7 @@ CAutomobile::ResetSuspension(void)
m_aSuspensionSpringRatio[i] = 1.0f;
m_aWheelTimer[i] = 0.0f;
m_aWheelRotation[i] = 0.0f;
- m_aWheelState[i] = WHEEL_STATE_0;
+ m_aWheelState[i] = WHEEL_STATE_NORMAL;
}
}
@@ -2867,6 +4512,8 @@ STARTPATCHES
InjectHook(0x52D190, &CAutomobile_::SetModelIndex_, PATCH_JUMP);
InjectHook(0x531470, &CAutomobile_::ProcessControl_, PATCH_JUMP);
InjectHook(0x535180, &CAutomobile_::Teleport_, PATCH_JUMP);
+ InjectHook(0x539EA0, &CAutomobile_::Render_, PATCH_JUMP);
+ InjectHook(0x535B40, &CAutomobile_::PreRender_, PATCH_JUMP);
InjectHook(0x53B270, &CAutomobile_::ProcessEntityCollision_, PATCH_JUMP);
InjectHook(0x53B660, &CAutomobile_::ProcessControlInputs_, PATCH_JUMP);
InjectHook(0x52E5F0, &CAutomobile_::GetComponentWorldPosition_, PATCH_JUMP);
@@ -2881,10 +4528,13 @@ STARTPATCHES
InjectHook(0x53BC60, &CAutomobile_::BlowUpCar_, PATCH_JUMP);
InjectHook(0x53BF70, &CAutomobile_::SetUpWheelColModel_, PATCH_JUMP);
InjectHook(0x53C0E0, &CAutomobile_::BurstTyre_, PATCH_JUMP);
+ InjectHook(0x53C5B0, &CAutomobile_::IsRoomForPedToLeaveCar_, PATCH_JUMP);
InjectHook(0x437690, &CAutomobile_::GetHeightAboveRoad_, PATCH_JUMP);
InjectHook(0x53C450, &CAutomobile_::PlayCarHorn_, PATCH_JUMP);
InjectHook(0x53E090, &CAutomobile::PlaceOnRoadProperly, PATCH_JUMP);
InjectHook(0x52F030, &CAutomobile::dmgDrawCarCollidingParticles, PATCH_JUMP);
+ InjectHook(0x535450, &CAutomobile::AddDamagedVehicleParticles, PATCH_JUMP);
+ InjectHook(0x5357D0, &CAutomobile::AddWheelDirtAndWater, PATCH_JUMP);
InjectHook(0x5353A0, &CAutomobile::ResetSuspension, PATCH_JUMP);
InjectHook(0x52D210, &CAutomobile::SetupSuspensionLines, PATCH_JUMP);
InjectHook(0x53E000, &CAutomobile::BlowUpCarsInPath, PATCH_JUMP);
diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h
index 1a103777..6226b555 100644
--- a/src/vehicles/Automobile.h
+++ b/src/vehicles/Automobile.h
@@ -48,13 +48,13 @@ public:
uint8 bHadDriver : 1; // for bombs
uint8 m_auto_flagA20 : 1;
uint8 m_auto_flagA40 : 1;
- uint8 m_auto_flagA80 : 1;
+ uint8 bWaterTight : 1; // no damage for non-player peds
uint8 bNotDamagedUpsideDown : 1;
uint8 bMoreResistantToDamage : 1;
uint8 field_4DB;
CEntity *m_pBombRigger;
int16 field_4E0;
- int16 field_4E2;
+ uint16 m_hydraulicState;
uint32 m_nBusDoorTimerEnd;
uint32 m_nBusDoorTimerStart;
float m_aSuspensionSpringLength[4];
@@ -71,7 +71,7 @@ public:
float m_weaponDoorTimerRight;
float m_fCarGunLR;
float m_fCarGunUD;
- float m_fWindScreenRotation;
+ float m_fPropellerRotation;
uint8 stuff4[4];
uint8 m_nWheelsOnGround;
uint8 m_nDriveWheelsOnGround;
@@ -108,7 +108,7 @@ public:
void BlowUpCar(CEntity *ent);
bool SetUpWheelColModel(CColModel *colModel);
void BurstTyre(uint8 tyre);
- bool IsRoomForPedToLeaveCar(uint32, CVector *);
+ bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset);
float GetHeightAboveRoad(void);
void PlayCarHorn(void);
@@ -122,6 +122,8 @@ public:
int32 RcbanditCheck1CarWheels(CPtrList &list);
void PlaceOnRoadProperly(void);
void dmgDrawCarCollidingParticles(const CVector &pos, float amount);
+ void AddDamagedVehicleParticles(void);
+ int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed);
void PlayHornIfNecessary(void);
void ResetSuspension(void);
void SetupSuspensionLines(void);
diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp
index c18bad25..f614b78f 100644
--- a/src/vehicles/Boat.cpp
+++ b/src/vehicles/Boat.cpp
@@ -5,7 +5,7 @@
float &fShapeLength = *(float*)0x600E78;
float &fShapeTime = *(float*)0x600E7C;
float &fRangeMult = *(float*)0x600E80; //0.6f; // 0.75f gta 3
-float &fTimeMult = *(float*)0xA0FCF4;
+float &fTimeMult = *(float*)0x943008;
float MAX_WAKE_LENGTH = 50.0f;
float MIN_WAKE_INTERVAL = 1.0f;
diff --git a/src/vehicles/DamageManager.h b/src/vehicles/DamageManager.h
index adcd7430..bf0c59a1 100644
--- a/src/vehicles/DamageManager.h
+++ b/src/vehicles/DamageManager.h
@@ -6,6 +6,9 @@
enum eEngineStatus
{
+ ENGINE_STATUS_STEAM1 = 100,
+ ENGINE_STATUS_STEAM2 = 150,
+ ENGINE_STATUS_SMOKE = 200,
ENGINE_STATUS_ON_FIRE = 225
};
@@ -32,6 +35,12 @@ enum eWheelStatus
WHEEL_STATUS_MISSING
};
+enum eLightStatus
+{
+ LIGHT_STATUS_OK,
+ LIGHT_STATUS_BROKEN
+};
+
enum tComponent
{
COMPONENT_DEFAULT,
diff --git a/src/vehicles/Floater.cpp b/src/vehicles/Floater.cpp
index cabe00c3..6b8bf755 100644
--- a/src/vehicles/Floater.cpp
+++ b/src/vehicles/Floater.cpp
@@ -22,7 +22,7 @@ static float fBoatVolumeDistribution[9] = {
};
bool
-cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point)
+cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse)
{
m_numSteps = 2.0f;
@@ -32,7 +32,7 @@ cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CV
PreCalcSetup(phys, buoyancy);
SimpleCalcBuoyancy();
- float f = CalcBuoyancyForce(phys, impulse, point);
+ float f = CalcBuoyancyForce(phys, point, impulse);
if(m_isBoat)
return true;
return f != 0.0f;
@@ -82,7 +82,7 @@ cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy)
m_haveVolume = false;
m_numPartialVolumes = 1.0f;
m_volumeUnderWater = 0.0f;
- m_impulse = CVector(0.0f, 0.0f, 0.0f);
+ m_impulsePoint = CVector(0.0f, 0.0f, 0.0f);
m_position = phys->GetPosition();
m_positionZ = CVector(0.0f, 0.0f, m_position.z);
m_buoyancy = buoyancy;
@@ -148,7 +148,7 @@ cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition)
fFraction = 1.0f/m_numPartialVolumes;
fRemainingSlice = 1.0f - fFraction;
- m_impulse = m_impulse*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction;
+ m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction;
m_numPartialVolumes += 1.0f;
m_haveVolume = true;
return fThisVolume;
@@ -175,13 +175,13 @@ cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel
}
bool
-cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point)
+cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse)
{
if(!m_haveVolume)
return false;
- *impulse = Multiply3x3(m_matrix, m_impulse);
- *point = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep());
+ *point = Multiply3x3(m_matrix, m_impulsePoint);
+ *impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep());
return true;
}
diff --git a/src/vehicles/Floater.h b/src/vehicles/Floater.h
index ede2b9d0..0a2ae809 100644
--- a/src/vehicles/Floater.h
+++ b/src/vehicles/Floater.h
@@ -1,6 +1,6 @@
#pragma once
-class Physical;
+class CPhysical;
enum tWaterLevel
{
@@ -33,7 +33,7 @@ public:
char m_field_B9;
bool m_isBoat;
float m_volumeUnderWater;
- CVector m_impulse;
+ CVector m_impulsePoint;
bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point);
void PreCalcSetup(CPhysical *phys, float buoyancy);
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index cc98bbe9..36675c8b 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -59,8 +59,8 @@ CVehicle::CVehicle(uint8 CreatedBy)
m_pBlowUpEntity = nil;
field_1FB = 0;
bComedyControls = false;
- m_veh_flagB40 = false;
- m_veh_flagB80 = false;
+ bCraneMessageDone = false;
+ bExtendedRange = false;
bTakeLessDamage = false;
bIsDamaged = false;
bFadeOut = false;
@@ -70,7 +70,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
bHasBeenOwnedByPlayer = false;
m_veh_flagC20 = false;
bCanBeDamaged = true;
- m_veh_flagC80 = false;
+ bUsingSpecialColModel = false;
m_veh_flagD1 = false;
m_veh_flagD2 = false;
m_nGunFiringTime = 0;
@@ -255,9 +255,9 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd);
float contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight);
- if(*wheelState != WHEEL_STATE_0)
+ if(*wheelState != WHEEL_STATE_NORMAL)
bAlreadySkidding = true;
- *wheelState = WHEEL_STATE_0;
+ *wheelState = WHEEL_STATE_NORMAL;
adhesion *= CTimer::GetTimeStep();
if(bAlreadySkidding)
@@ -299,7 +299,7 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
if(brake > adhesion){
if(Abs(contactSpeedFwd) > 0.005f)
- *wheelState = WHEEL_STATE_STATIC;
+ *wheelState = WHEEL_STATE_FIXED;
}else {
if(fwd > 0.0f){
if(fwd > brake)
@@ -312,11 +312,11 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
}
if(sq(adhesion) < sq(right) + sq(fwd)){
- if(*wheelState != WHEEL_STATE_STATIC){
+ if(*wheelState != WHEEL_STATE_FIXED){
if(bDriving && contactSpeedFwd < 0.2f)
- *wheelState = WHEEL_STATE_1;
+ *wheelState = WHEEL_STATE_SPINNING;
else
- *wheelState = WHEEL_STATE_2;
+ *wheelState = WHEEL_STATE_SKIDDING;
}
float l = Sqrt(sq(right) + sq(fwd));
@@ -343,10 +343,10 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec
{
float angularVelocity;
switch(state){
- case WHEEL_STATE_1:
+ case WHEEL_STATE_SPINNING:
angularVelocity = -1.1f; // constant speed forward
break;
- case WHEEL_STATE_STATIC:
+ case WHEEL_STATE_FIXED:
angularVelocity = 0.0f; // not moving
break;
default:
@@ -377,12 +377,13 @@ CVehicle::ProcessDelayedExplosion(void)
return;
int tick = CTimer::GetTimeStep()/60.0f*1000.0f;
+ int16 prev = m_nBombTimer;
if(tick > m_nBombTimer)
m_nBombTimer = 0;
else
m_nBombTimer -= tick;
- if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != 0xFE00)
+ if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != (prev & 0xFE00))
DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_BOMB_TICK, 0.0f);
if (m_nBombTimer != 0)
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 38d411cd..262bd62c 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -59,6 +59,11 @@ enum
CAR_POS_EXHAUST = 9,
};
+enum
+{
+ BOAT_POS_FRONTSEAT
+};
+
enum eDoors
{
DOOR_BONNET = 0,
@@ -119,10 +124,10 @@ enum
enum tWheelState
{
- WHEEL_STATE_0 = 0,
- WHEEL_STATE_1 = 1, // constant velocity
- WHEEL_STATE_2 = 2, // normal
- WHEEL_STATE_STATIC = 3, // not moving
+ WHEEL_STATE_NORMAL, // standing still or rolling normally
+ WHEEL_STATE_SPINNING, // rotating but not moving
+ WHEEL_STATE_SKIDDING,
+ WHEEL_STATE_FIXED, // not rotating
};
enum eFlightModel
@@ -176,8 +181,8 @@ public:
uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims
uint8 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way)
uint8 bWarnedPeds : 1; // Has scan and warn peds of danger been processed?
- uint8 m_veh_flagB40 : 1;
- uint8 m_veh_flagB80 : 1;
+ uint8 bCraneMessageDone : 1; // A crane message has been printed for this car allready
+ uint8 bExtendedRange : 1; // This vehicle needs to be a bit further away to get deleted
uint8 bTakeLessDamage : 1; // This vehicle is stronger (takes about 1/4 of damage)
uint8 bIsDamaged : 1; // This vehicle has been damaged and is displaying all its components
@@ -186,7 +191,7 @@ public:
uint8 m_veh_flagC10 : 1;
uint8 m_veh_flagC20 : 1;
uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions
- uint8 m_veh_flagC80 : 1;
+ uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure
uint8 m_veh_flagD1 : 1;
uint8 m_veh_flagD2 : 1;
@@ -253,7 +258,7 @@ public:
virtual void BlowUpCar(CEntity *ent) {}
virtual bool SetUpWheelColModel(CColModel *colModel) { return false; }
virtual void BurstTyre(uint8 tyre) {}
- virtual bool IsRoomForPedToLeaveCar(uint32, CVector *) { return false;}
+ virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false;}
virtual float GetHeightAboveRoad(void);
virtual void PlayCarHorn(void) {}