diff options
Diffstat (limited to 'src/vehicles/Automobile.cpp')
-rw-r--r-- | src/vehicles/Automobile.cpp | 2320 |
1 files changed, 1692 insertions, 628 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index d2fedbd5..3f329251 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -17,8 +17,10 @@ #include "Explosion.h" #include "Particle.h" #include "ParticleObject.h" +#include "Glass.h" #include "Antennas.h" #include "Skidmarks.h" +#include "WindModifiers.h" #include "Shadows.h" #include "PointLights.h" #include "Coronas.h" @@ -35,8 +37,10 @@ #include "Population.h" #include "CarCtrl.h" #include "CarAI.h" +#include "Stats.h" #include "Garages.h" #include "PathFind.h" +#include "Replay.h" #include "AnimManager.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" @@ -45,7 +49,9 @@ #include "Object.h" #include "Automobile.h" -bool bAllCarCheat; // unused +//--MIAMI: file done except TODOs + +bool bAllCarCheat; RwObject *GetCurrentAtomicObjectCB(RwObject *object, void *data); @@ -67,7 +73,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); m_fFireBlowUpTimer = 0.0f; - m_auto_unk1 = 0; + m_doingBurnout = 0; bTaxiLight = m_sAllTaxiLights; bFixedColour = false; bBigWheels = false; @@ -75,7 +81,26 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) SetModelIndex(id); + // Already done in CVehicle... + switch(GetModelIndex()){ + case MI_HUNTER: + case MI_ANGEL: + case MI_FREEWAY: + m_nRadioStation = V_ROCK; + break; + case MI_RCBARON: + case MI_RCBANDIT: + case MI_RCRAIDER: + case MI_RCGOBLIN: + case MI_TOPFUN: + case MI_CADDY: + case MI_BAGGAGE: + m_nRadioStation = RADIO_OFF; + break; + } + pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((eHandlingId)mi->m_handlingId); m_auto_unused1 = 20.0f; m_auto_unused2 = 0; @@ -129,6 +154,8 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fElasticity = 0.05f; m_fBuoyancy = pHandling->fBuoyancy; + m_fOrientation = m_auto_unk4 = 0.0f; + m_nBusDoorTimerEnd = 0; m_nBusDoorTimerStart = 0; @@ -139,6 +166,9 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fGasPedalAudio = 0.0f; bNotDamagedUpsideDown = false; bMoreResistantToDamage = false; + bTankDetonateCars = true; + bStuckInSand = false; + bHeliDestroyed = false; m_fVelocityChangeForAudio = 0.0f; m_hydraulicState = 0; @@ -150,7 +180,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_aWheelRotation[i] = 0.0f; m_aWheelSpeed[i] = 0.0f; m_aWheelState[i] = WHEEL_STATE_NORMAL; - m_aWheelSkidmarkMuddy[i] = false; + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; m_aWheelSkidmarkBloody[i] = false; } @@ -159,6 +189,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_nDriveWheelsOnGroundPrev = 0; m_fHeightAboveRoad = 0.0f; m_fTraction = 1.0f; + m_fTireTemperature = 1.0f; CColModel *colModel = mi->GetColModel(); if(colModel->lines == nil){ @@ -173,10 +204,6 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_nNumPassengers = 0; - m_bombType = CARBOMB_NONE; - bDriverLastFrame = false; - m_pBombRigger = nil; - if(m_nDoorLock == CARLOCK_UNLOCKED && (id == MI_POLICE || id == MI_ENFORCER || id == MI_RHINO)) m_nDoorLock = CARLOCK_LOCKED_INITIALLY; @@ -184,6 +211,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fCarGunLR = 0.0f; m_fCarGunUD = 0.05f; m_fPropellerRotation = 0.0f; + m_fHeliOrientation = -1.0f; m_weaponDoorTimerLeft = 0.0f; m_weaponDoorTimerRight = m_weaponDoorTimerLeft; @@ -194,7 +222,10 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) CMatrix mat2(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); mat1.GetPosition() += CVector(mat2.GetPosition().x + 0.1f, 0.0f, mat2.GetPosition().z); mat1.UpdateRW(); - }else if(GetModelIndex() == MI_MIAMI_SPARROW || GetModelIndex() == MI_MIAMI_RCRAIDER){ + }else if(GetModelIndex() == MI_HUNTER){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(IsRealHeli()){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); @@ -205,7 +236,6 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) } } - void CAutomobile::SetModelIndex(uint32 id) { @@ -213,6 +243,11 @@ CAutomobile::SetModelIndex(uint32 id) SetupModelNodes(); } +#define SAND_SLOWDOWN (0.01f) +float CAR_BALANCE_MULT = 0.3f; +CVector vecSeaSparrowGunPos(-0.5f, 2.4f, -0.785f); +CVector vecHunterGunPos(0.0f, 4.8f, -1.3f); +CVector vecHunterRocketPos(2.5f, 1.0f, -0.5f); CVector vecDAMAGE_ENGINE_POS_SMALL(-0.1f, -0.1f, 0.0f); CVector vecDAMAGE_ENGINE_POS_BIG(-0.5f, -0.3f, 0.0f); @@ -227,12 +262,25 @@ CAutomobile::ProcessControl(void) colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; else colModel = GetColModel(); + bool drivingInSand = false; bWarnedPeds = false; + m_doingBurnout = 0; + bStuckInSand = false; + bRestingOnPhysical = false; + + bool carHasNitro = bAllTaxisHaveNitro && GetStatus() == STATUS_PLAYER && IsTaxi(); - // skip if the collision isn't for the current level - if(colModel->level > LEVEL_GENERIC && colModel->level != CCollision::ms_collisionInMemory) + if(CReplay::IsPlayingBack()) return; + // Heli wind + if(IsRealHeli()) + if((GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS) && m_aWheelSpeed[1] > 0.075f || + GetStatus() == STATUS_SIMPLE) + CWindModifiers::RegisterOne(GetPosition(), 1); + + UpdatePassengerList(); + // Improve grip of vehicles in certain cases bool strongGrip1 = false; bool strongGrip2 = false; @@ -247,7 +295,8 @@ CAutomobile::ProcessControl(void) else if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) strongGrip2 = true; } - } + }else if(GetModelIndex() == MI_RCBANDIT && GetStatus() != STATUS_PLAYER_REMOTE) + strongGrip1 = true; if(bIsBus) ProcessAutoBusDoors(); @@ -258,7 +307,7 @@ CAutomobile::ProcessControl(void) if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED && GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PLAYER_DISABLED){ switch(GetModelIndex()) - case MI_FBICAR: + case MI_FBIRANCH: case MI_POLICE: case MI_ENFORCER: case MI_SECURICA: @@ -268,25 +317,15 @@ CAutomobile::ProcessControl(void) } // Process driver - if(pDriver){ - if(!bDriverLastFrame && m_bombType == CARBOMB_ONIGNITIONACTIVE){ - // If someone enters the car and there is a bomb, detonate - m_nBombTimer = 1000; - m_pBlowUpEntity = m_pBombRigger; - if(m_pBlowUpEntity) - m_pBlowUpEntity->RegisterReference((CEntity**)&m_pBlowUpEntity); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TICK, 1.0f); - } - bDriverLastFrame = true; - + if(pDriver) if(IsUpsideDown() && CanPedEnterCar()){ if(!pDriver->IsPlayer() && !(pDriver->m_leader && pDriver->m_leader->bInVehicle) && pDriver->CharCreatedBy != MISSION_CHAR) pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); } - }else - bDriverLastFrame = false; + + ActivateBombWhenEntered(); // Process passengers if(m_nNumPassengers != 0 && IsUpsideDown() && CanPedEnterCar()){ @@ -300,18 +339,7 @@ CAutomobile::ProcessControl(void) CRubbish::StirUp(this); - // blend in clump - int clumpAlpha = CVisibilityPlugins::GetClumpAlpha((RpClump*)m_rwObject); - if(bFadeOut){ - clumpAlpha -= 8; - if(clumpAlpha < 0) - clumpAlpha = 0; - }else if(clumpAlpha < 255){ - clumpAlpha += 16; - if(clumpAlpha > 255) - clumpAlpha = 255; - } - CVisibilityPlugins::SetClumpAlpha((RpClump*)m_rwObject, clumpAlpha); + UpdateClumpAlpha(); AutoPilot.m_bSlowedDownBecauseOfCars = false; AutoPilot.m_bSlowedDownBecauseOfPeds = false; @@ -322,24 +350,33 @@ CAutomobile::ProcessControl(void) else if(pHandling->Flags & HANDLING_NONPLAYER_STABILISER && GetStatus() == STATUS_PHYSICS) m_vecCentreOfMass.z = pHandling->CentreOfMass.z - 0.2f*pHandling->Dimension.z; else - m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + m_vecCentreOfMass = pHandling->CentreOfMass; + + // Park car + if(bCanPark && !bParking && VehicleCreatedBy != MISSION_VEHICLE && AutoPilot.m_nCarMission == MISSION_CRUISE && + ((CTimer::GetFrameCounter() + m_randomSeed)&0xF) == 0 && !IsTaxi()){ + CVector parkPosition = GetPosition() + 3.0f*GetRight() + 10.0f*GetForward(); + CEntity *ent = nil; + CColPoint colpoint; + if(!CWorld::ProcessLineOfSight(GetPosition(), parkPosition, colpoint, ent, true, true, true, false, false, false) || + ent == this) + CCarAI::GetCarToParkAtCoors(this, &parkPosition); + } // Process depending on status bool playerRemote = false; switch(GetStatus()){ case STATUS_PLAYER_REMOTE: - if(CPad::GetPad(0)->WeaponJustDown()){ + if(CPad::GetPad(0)->WeaponJustDown() && !bDisableRemoteDetonation){ BlowUpCar(FindPlayerPed()); CRemote::TakeRemoteControlledCarFromPlayer(); } - if(GetModelIndex() == MI_RCBANDIT){ - CVector pos = GetPosition(); + if(GetModelIndex() == MI_RCBANDIT && !bDisableRemoteDetonationOnContact){ + //CVector pos = GetPosition(); // FindPlayerCoors unused - if(RcbanditCheckHitWheels() || bIsInWater || CPopulation::IsPointInSafeZone(&pos)){ - if(CPopulation::IsPointInSafeZone(&pos)) - CGarages::TriggerMessage("HM2_5", -1, 5000, -1); + if(RcbanditCheckHitWheels() || bIsInWater){ CRemote::TakeRemoteControlledCarFromPlayer(); BlowUpCar(FindPlayerPed()); } @@ -350,7 +387,7 @@ CAutomobile::ProcessControl(void) // fall through case STATUS_PLAYER: if(playerRemote || - pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR){ + pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR && pDriver->GetPedState() != PED_ARRESTED){ // process control input if controlled by player if(playerRemote || pDriver->m_nPedType == PEDTYPE_PLAYER1) ProcessControlInputs(0); @@ -359,7 +396,59 @@ CAutomobile::ProcessControl(void) if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) DoDriveByShootings(); + + // Tweak center on mass when driving on two wheels + int twoWheelTime = CWorld::Players[CWorld::PlayerInFocus].m_nTimeNotFullyOnGround; + if(twoWheelTime > 500 && !IsRealHeli() && !IsRealPlane()){ + float tweak = Min(twoWheelTime-500, 1000)/500.0f; + if(GetUp().z > 0.0f){ + // positive when on left wheels, negative on right wheels + if(GetRight().z <= 0.0f) + tweak *= -1.0f; + m_vecCentreOfMass.z = pHandling->CentreOfMass.z + + CPad::GetPad(0)->GetSteeringLeftRight()/128.0f * + CAR_BALANCE_MULT * tweak * colModel->boundingBox.max.z; + } + }else + m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + + if(bHoverCheat) + DoHoverSuspensionRatios(); + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_RHINO){ + float slowdown; + CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); + float fSpeed = parallelSpeed.MagnitudeSqr(); + if(fSpeed > SQR(0.3f)){ + fSpeed = Sqrt(fSpeed); + parallelSpeed *= 0.3f / fSpeed; + slowdown = SAND_SLOWDOWN * Max(1.0f - 2.0f*fSpeed, 0.2f); + }else{ + bStuckInSand = true; + slowdown = SAND_SLOWDOWN; + } + if(pHandling->Flags & HANDLING_GOOD_INSAND) + slowdown *= 0.5f; + if(CWeather::WetRoads > 0.2f) + slowdown *= (1.2f - CWeather::WetRoads); + ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*slowdown*m_fMass); + drivingInSand = true; + } + } + }else if(pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar())){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; } + if(CPad::GetPad(0)->WeaponJustDown()) + ActivateBomb(); break; case STATUS_SIMPLE: @@ -380,6 +469,7 @@ CAutomobile::ProcessControl(void) PlayHornIfNecessary(); ReduceHornCounter(); bVehicleColProcessed = false; + bAudioChangingGear = false; // that's all we do for simple vehicles return; @@ -387,15 +477,44 @@ CAutomobile::ProcessControl(void) CCarAI::UpdateCarAI(this); CCarCtrl::SteerAICarWithPhysics(this); PlayHornIfNecessary(); + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_SANDKING && GetModelIndex() != MI_BFINJECT){ + bStuckInSand = true; + if(CWeather::WetRoads > 0.0f) + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass * (1.0f-CWeather::WetRoads)); + else + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); + } + } break; case STATUS_ABANDONED: - m_fBrakePedal = 0.2f; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)) + m_fBrakePedal = 0.2f; + else + m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } break; case STATUS_WRECKED: @@ -404,26 +523,32 @@ CAutomobile::ProcessControl(void) m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; break; case STATUS_PLAYER_DISABLED: - m_fBrakePedal = 1.0f; - bIsHandbrakeOn = true; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || + (pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar()))){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; + }else{ + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + } m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; break; default: break; } - // what's going on here? - if(GetPosition().z < -0.6f && - Abs(m_vecMoveSpeed.x) < 0.05f && - Abs(m_vecMoveSpeed.y) < 0.05f) - m_vecTurnSpeed *= Pow(0.95f, CTimer::GetTimeStep()); - // Skip physics if object is found to have been static recently bool skipPhysics = false; if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){ @@ -451,7 +576,8 @@ CAutomobile::ProcessControl(void) if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && - m_fDistanceTravelled < distanceLimit || + m_fDistanceTravelled < distanceLimit && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity && m_pDamageEntity->IsPed()) || makeStatic){ m_nStaticFrames++; @@ -467,15 +593,27 @@ CAutomobile::ProcessControl(void) } }else m_nStaticFrames = 0; + if(IsRealHeli() && m_aWheelSpeed[1] > 0.0f){ + skipPhysics = false; + m_nStaticFrames = 0; + } } // Postpone for(i = 0; i < 4; i++) - if(m_aGroundPhysical[i] && !CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ - bWasPostponed = true; - return; + if(m_aGroundPhysical[i]){ + bRestingOnPhysical = true; + if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } } + if(bRestingOnPhysical){ + skipPhysics = false; + m_nStaticFrames = 0; + } + VehicleDamage(0.0f, 0); // special control @@ -487,12 +625,11 @@ CAutomobile::ProcessControl(void) TankControl(); BlowUpCarsInPath(); break; - case MI_YARDIE: - // beta also had esperanto here it seems + case MI_VOODOO: HydraulicControl(); break; default: - if(CVehicle::bCheat3){ + if(CVehicle::bCheat3 || carHasNitro){ // Make vehicle jump when horn is sounded if(GetStatus() == STATUS_PLAYER && m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f) && // BUG: game checks [0] four times, instead of all wheels @@ -531,12 +668,17 @@ CAutomobile::ProcessControl(void) 1.2f*m_vecMoveSpeed, nil, 2.0f); ApplyMoveForce(CVector(0.0f, 0.0f, 1.0f)*m_fMass*0.4f); - ApplyTurnForce(GetUp()*m_fMass*0.035f, GetForward()*1.0f); + ApplyTurnForce(GetUp()*m_fTurnMass*0.01f, GetForward()*1.0f); } } break; } + if(GetStatus() == STATUS_PHYSICS || GetStatus() == STATUS_SIMPLE) + if(AutoPilot.m_nCarMission == MISSION_HELI_FLYTOCOORS || + AutoPilot.m_nCarMission == MISSION_PLANE_FLYTOCOORS) + skipPhysics = true; + float brake; if(skipPhysics){ bHasContacted = false; @@ -546,11 +688,13 @@ CAutomobile::ProcessControl(void) m_nCollisionRecords = 0; bHasCollided = false; bVehicleColProcessed = false; + bAudioChangingGear = false; m_nDamagePieceType = 0; m_fDamageImpulse = 0.0f; m_pDamageEntity = nil; m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_fTireTemperature = 1.0f; }else{ // This has to be done if ProcessEntityCollision wasn't called @@ -603,6 +747,14 @@ CAutomobile::ProcessControl(void) if(m_aSuspensionSpringRatio[i] > 1.0f) m_aSuspensionSpringRatio[i] = 1.0f; } + }else if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[i].surfaceB) == ADHESIVE_SAND && + GetModelIndex() != MI_RHINO){ + fwdSpeed *= 0.7f; + float f = 1.0f - fwdSpeed/0.3f - 0.7f*CWeather::WetRoads; + f = Max(f, 0.4f); + m_aSuspensionSpringRatio[i] += 0.35f*f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; } // get points and directions if spring is compressed @@ -620,13 +772,20 @@ CAutomobile::ProcessControl(void) if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) bias = 1.0f - bias; - ApplySpringCollision(pHandling->fSuspensionForceLevel, + ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, springDirections[i], contactPoints[i], - m_aSuspensionSpringRatio[i], bias); - m_aWheelSkidmarkMuddy[i] = - m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || - m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY || - m_aWheelColPoints[i].surfaceB == SURFACE_SAND; + m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); + + m_aWheelSkidmarkUnk[i] = false; + if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY) + m_aWheelSkidmarkType[i] = SKIDMARK_MUDDY; + else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ + m_aWheelSkidmarkType[i] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[i] = true; + }else + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; }else{ contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); } @@ -643,6 +802,8 @@ CAutomobile::ProcessControl(void) m_aGroundPhysical[i] = nil; #endif } + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].normal.z > 0.35f) + springDirections[i] = -m_aWheelColPoints[i].normal; } // dampen springs @@ -661,7 +822,6 @@ CAutomobile::ProcessControl(void) } } - bool gripCheat = true; fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); if(!strongGrip1 && !CVehicle::bCheat3) @@ -669,14 +829,25 @@ CAutomobile::ProcessControl(void) float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); acceleration /= m_fForceMultiplier; - // unused - if(GetModelIndex() == MI_MIAMI_RCBARON || - GetModelIndex() == MI_MIAMI_RCRAIDER || - GetModelIndex() == MI_MIAMI_SPARROW) + if(IsRealHeli() || IsRealPlane()) acceleration = 0.0f; + if(bAudioChangingGear && m_fGasPedal > 0.4f && m_fBrakePedal < 0.1f && fwdSpeed > 0.15f && + this == FindPlayerVehicle() && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ + if(GetStatus() == STATUS_PLAYER && pHandling->Flags & HANDLING_IS_BUS){ + if(m_nBusDoorTimerEnd == 0) + m_nBusDoorTimerEnd = 1000; + else if(m_nBusDoorTimerEnd > CTimer::GetTimeStepInMilliseconds()) + m_nBusDoorTimerEnd -= CTimer::GetTimeStepInMilliseconds(); + } + + if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f) + ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); + } + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); - bool neutralHandling = !!(pHandling->Flags & HANDLING_NEUTRALHANDLING); + bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias); float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; @@ -722,21 +893,7 @@ CAutomobile::ProcessControl(void) if(CVehicle::bCheat3) traction *= 4.0f; - if(FindPlayerVehicle() && FindPlayerVehicle() == this){ - if(CPad::GetPad(0)->WeaponJustDown()){ - if(m_bombType == CARBOMB_TIMED){ - m_bombType = CARBOMB_TIMEDACTIVE; - m_nBombTimer = 7000; - m_pBlowUpEntity = FindPlayerPed(); - CGarages::TriggerMessage("GA_12", -1, 3000, -1); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TIMED_ACTIVATED, 1.0f); - }else if(m_bombType == CARBOMB_ONIGNITION){ - m_bombType = CARBOMB_ONIGNITIONACTIVE; - CGarages::TriggerMessage("GA_12", -1, 3000, -1); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_ONIGNITION_ACTIVATED, 1.0f); - } - } - }else if(strongGrip1 || CVehicle::bCheat3){ + if(FindPlayerVehicle() != this && (strongGrip1 || CVehicle::bCheat3)){ traction *= 1.2f; acceleration *= 1.4f; if(strongGrip2 || CVehicle::bCheat3){ @@ -748,13 +905,16 @@ CAutomobile::ProcessControl(void) static float fThrust; static tWheelState WheelState[4]; - // Process front wheels on ground + bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); + // Process front wheels on ground - first try + + if(!rearWheelsFirst){ if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ float s = Sin(m_fSteerAngle); float c = Cos(m_fSteerAngle); - CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-s, c, 0.0f)); - CVector wheelRight = Multiply3x3(GetMatrix(), CVector(c, s, 0.0f)); + + CVector wheelFwd, wheelRight, tmp; if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) @@ -762,6 +922,15 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; if(GetStatus() == STATUS_PLAYER) @@ -796,6 +965,15 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; if(GetStatus() == STATUS_PLAYER) @@ -827,33 +1005,36 @@ CAutomobile::ProcessControl(void) // Process front wheels off ground - if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ - if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } - m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; - } - if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ - if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; } - m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } } // Process rear wheels @@ -862,14 +1043,28 @@ CAutomobile::ProcessControl(void) CVector wheelFwd = GetForward(); CVector wheelRight = GetRight(); + float rearBrake = brake; + float rearTraction = traction; + if(bIsHandbrakeOn){ #ifdef FIX_BUGS - // Not sure if this is needed, but brake usually has timestep as a factor - if(bIsHandbrakeOn) - brake = 20000.0f * CTimer::GetTimeStepFix(); + // Not sure if this is needed, but brake usually has timestep as a factor + rearBrake = 20000.0f * CTimer::GetTimeStepFix(); #else - if(bIsHandbrakeOn) - brake = 20000.0f; + rearBrake = 20000.0f; #endif + if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ + m_fTireTemperature += 0.005*CTimer::GetTimeStep(); + if(m_fTireTemperature > 2.0f) + m_fTireTemperature = 2.0f; + } + }else if(m_doingBurnout && !mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)){ + rearBrake = 0.0f; + rearTraction = 0.0f; + // BUG: missing timestep + ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); + }else if(m_fTireTemperature > 1.0f){ + rearTraction *= m_fTireTemperature; + } if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) @@ -877,8 +1072,14 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal)*m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal); + wheelRight.Normalise(); + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceA = SURFACE_WHEELBASE; - float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*traction; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB); WheelState[CARWHEEL_REAR_LEFT] = m_aWheelState[CARWHEEL_REAR_LEFT]; @@ -887,7 +1088,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, CARWHEEL_REAR_LEFT, &m_aWheelSpeed[CARWHEEL_REAR_LEFT], @@ -897,7 +1098,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_LEFT, &m_aWheelSpeed[CARWHEEL_REAR_LEFT], @@ -905,14 +1106,26 @@ CAutomobile::ProcessControl(void) WHEEL_STATUS_OK); } +#ifdef FIX_BUGS + // Shouldn't we reset these after the left wheel? + wheelFwd = GetForward(); + wheelRight = GetRight(); +#endif + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = 0.0f; else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal); + wheelRight.Normalise(); + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceA = SURFACE_WHEELBASE; - float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*traction; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB); WheelState[CARWHEEL_REAR_RIGHT] = m_aWheelState[CARWHEEL_REAR_RIGHT]; @@ -921,7 +1134,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, CARWHEEL_REAR_RIGHT, &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], @@ -931,7 +1144,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_RIGHT, &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], @@ -940,58 +1153,203 @@ CAutomobile::ProcessControl(void) } } + if(m_doingBurnout && !mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && + (m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING || m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING)){ + m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); + if(m_fTireTemperature > 3.0f) + m_fTireTemperature = 3.0f; + }else if(m_fTireTemperature > 1.0f){ + m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; + } + // Process rear wheels off ground - if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ - if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; } - m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; - } - if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ - if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + } + } + + // Process front wheels on ground - second try + + if(rearWheelsFirst){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + float s = Sin(m_fSteerAngle); + float c = Cos(m_fSteerAngle); + + CVector wheelFwd, wheelRight, tmp; + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); + WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); + WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process front wheels off ground + + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } - m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } + } } for(i = 0; i < 4; i++){ float wheelPos = colModel->lines[i].p0.z; if(m_aSuspensionSpringRatio[i] > 0.0f) wheelPos -= m_aSuspensionSpringRatio[i]*m_aSuspensionSpringLength[i]; - m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; + if(GetModelIndex() == MI_VOODOO && bUsingSpecialColModel) + m_aWheelPosition[i] = wheelPos; + else + m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; } for(i = 0; i < 4; i++) m_aWheelState[i] = WheelState[i]; + if(m_fGasPedal < 0.0f){ + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_LEFT] = WHEEL_STATE_NORMAL; + if(m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_RIGHT] = WHEEL_STATE_NORMAL; + } // Process horn if(GetStatus() != STATUS_PLAYER){ - ReduceHornCounter(); + if(!IsAlarmOn()) + ReduceHornCounter(); }else{ - if(GetModelIndex() == MI_MRWHOOP){ - if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory] && - !Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5]){ - m_bSirenOrAlarm = !m_bSirenOrAlarm; - printf("m_bSirenOrAlarm toggled to %d\n", m_bSirenOrAlarm); - } - }else if(UsesSiren(GetModelIndex())){ + if(UsesSiren()){ if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory]){ if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5] && Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+3) % 5]) @@ -1004,43 +1362,137 @@ CAutomobile::ProcessControl(void) m_bSirenOrAlarm = !m_bSirenOrAlarm; }else m_nCarHornTimer = 0; - }else if(GetModelIndex() != MI_YARDIE && !CVehicle::bCheat3){ - if(Pads[0].GetHorn()) - m_nCarHornTimer = 1; - else - m_nCarHornTimer = 0; + }else if(GetModelIndex() != MI_VOODOO && !CVehicle::bCheat3 && !carHasNitro){ + if(!IsAlarmOn()){ + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } } } // Flying + bool playRotorSound = false; if(GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PHYSICS){ - if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW) - m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.0005f, 0.0f); + if(IsRealHeli()){ + bEngineOn = false; + m_aWheelSpeed[1] = Max(m_aWheelSpeed[1]-0.0005f, 0.0f); + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } }else if((GetModelIndex() == MI_DODO || CVehicle::bAllDodosCheat) && m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ #ifdef ALT_DODO_CHEAT if (bAltDodoCheat) - FlyingControl(FLIGHT_MODEL_SEAPLANE); + FlyingControl(FLIGHT_MODEL_PLANE); else #endif + if(GetModelIndex() == MI_DODO) FlyingControl(FLIGHT_MODEL_DODO); - }else if(GetModelIndex() == MI_MIAMI_RCBARON){ - FlyingControl(FLIGHT_MODEL_RCPLANE); - }else if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW || bAllCarCheat){ -#ifdef ALLCARSHELI_CHEAT - if (bAllCarCheat) - FlyingControl(FLIGHT_MODEL_HELI); else -#endif - { - if (CPad::GetPad(0)->GetCircleJustDown()) - m_aWheelSpeed[0] = Max(m_aWheelSpeed[0] - 0.03f, 0.0f); - if (m_aWheelSpeed[0] < 0.22f) - m_aWheelSpeed[0] += 0.0001f; - if (m_aWheelSpeed[0] > 0.15f) + FlyingControl(FLIGHT_MODEL_PLANE); + }else if(GetModelIndex() == MI_RCBARON){ + FlyingControl(FLIGHT_MODEL_RCPLANE); + }else if(IsRealHeli() || bAllCarCheat){ + // Speed up rotor + if(m_aWheelSpeed[1] < 0.22f && !bIsInWater){ + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + m_aWheelSpeed[1] += 0.003f; + else + m_aWheelSpeed[1] += 0.001f; + } + + // Fly + if(m_aWheelSpeed[1] > 0.15f){ + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + FlyingControl(FLIGHT_MODEL_RCHELI); + else if(m_nWheelsOnGround < 4 && !(GetModelIndex() == MI_SEASPAR && bTouchingWater) || + CPad::GetPad(0)->GetAccelerate() != 0 || CPad::GetPad(0)->GetCarGunUpDown() > 1.0f || + Abs(m_vecMoveSpeed.x) > 0.02f || + Abs(m_vecMoveSpeed.y) > 0.02f || + Abs(m_vecMoveSpeed.z) > 0.02f) FlyingControl(FLIGHT_MODEL_HELI); } + + // Blade collision + if(m_aWheelSpeed[1] > 0.015f && m_aCarNodes[CAR_BONNET]){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 0.72f, 0.9f); + else if(GetModelIndex() == MI_SPARROW || GetModelIndex() == MI_SEASPAR) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 5.15f, 0.8f); + else if(GetModelIndex() == MI_HUNTER) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 0.5f); + else + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 1.0f); + } + + // Heli weapons + if(GetModelIndex() == MI_HUNTER && GetStatus() == STATUS_PLAYER){ + // Hunter rockets + if(CPad::GetPad(0)->CarGunJustDown() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+350){ + CWeapon gun(WEAPONTYPE_ROCKETLAUNCHER, 100); + CVector source = vecHunterRocketPos; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + source = vecHunterRocketPos; + source.x = -source.x; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + // Hunter gun + }else if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+60){ + CWeapon gun(WEAPONTYPE_HELICANNON, 5000); + CVector source = vecHunterGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + }else if(GetModelIndex() == MI_SEASPAR && GetStatus() == STATUS_PLAYER){ + // Sea sparrow gun + if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+40){ + CWeapon gun(WEAPONTYPE_M4, 5000); + CVector source = vecSeaSparrowGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + } + + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } + + // Play rotor sound + if(playRotorSound && m_aCarNodes[CAR_BONNET]){ + CVector camDist = TheCamera.GetPosition() - GetPosition(); + float distSq = camDist.MagnitudeSqr(); + if(distSq < SQR(20.0f) && Abs(m_fPropellerRotation - m_aWheelRotation[1]) > DEGTORAD(30.0f)){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + CVector blade = mat.GetRight(); + blade = GetMatrix() * blade; + camDist /= Max(Sqrt(distSq), 0.01f); + if(Abs(DotProduct(camDist, blade)) > 0.95f){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_31, 0.0f); + m_fPropellerRotation = m_aWheelRotation[1]; + } + } } } @@ -1082,7 +1534,7 @@ CAutomobile::ProcessControl(void) CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)), - nil, 0.9f); + nil, 0.63f); CVector coors = damagePos; coors.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), @@ -1094,10 +1546,8 @@ CAutomobile::ProcessControl(void) // Blow up car after 5 seconds m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); - if(m_fFireBlowUpTimer > 5000.0f){ - CWorld::Players[CWorld::PlayerInFocus].AwardMoneyForExplosion(this); + if(m_fFireBlowUpTimer > 5000.0f) BlowUpCar(m_pSetOnFireEntity); - } }else m_fFireBlowUpTimer = 0.0f; @@ -1109,7 +1559,7 @@ CAutomobile::ProcessControl(void) if(m_bSirenOrAlarm && (CTimer::GetFrameCounter()&7) == 5 && - UsesSiren(GetModelIndex()) && GetModelIndex() != MI_MRWHOOP) + UsesSiren() && GetModelIndex() != MI_MRWHOOP) CCarAI::MakeWayForCarWithSiren(this); @@ -1117,10 +1567,14 @@ CAutomobile::ProcessControl(void) float suspShake = 0.0f; float surfShake = 0.0f; + float speedsq = m_vecMoveSpeed.MagnitudeSqr(); for(i = 0; i < 4; i++){ float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; - if(suspChange > 0.3f){ - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); + if(suspChange > 0.3f && !drivingInSand && speedsq > 0.04f){ + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); + else + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); if(suspChange > suspShake) suspShake = suspChange; } @@ -1129,7 +1583,7 @@ CAutomobile::ProcessControl(void) if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ if(surfShake < 0.2f) surfShake = 0.3f; - }else if(surf == SURFACE_MUD_DRY || surf == SURFACE_SAND){ + }else if(surf == SURFACE_MUD_DRY){ if(surfShake < 0.1f) surfShake = 0.2f; }else if(surf == SURFACE_GRASS){ @@ -1137,13 +1591,17 @@ CAutomobile::ProcessControl(void) surfShake = 0.1f; } + if(this == FindPlayerVehicle()) +// BUG: this only observes one of the wheels + TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; m_aSuspensionSpringRatio[i] = 1.0f; } // Shake pad - if((suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ + if(!drivingInSand && (suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ float speed = m_vecMoveSpeed.MagnitudeSqr(); if(speed > sq(0.1f)){ speed = Sqrt(speed); @@ -1158,8 +1616,9 @@ CAutomobile::ProcessControl(void) } bVehicleColProcessed = false; + bAudioChangingGear = false; - if(!bWarnedPeds) + if(!bWarnedPeds && GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) CCarCtrl::ScanForPedDanger(this); @@ -1167,20 +1626,20 @@ CAutomobile::ProcessControl(void) // TODO: make the numbers defines float heading; - if(GetPosition().x > 1900.0f){ + if(GetPosition().x > 1950.0f-400.0f){ if(m_vecMoveSpeed.x > 0.0f) m_vecMoveSpeed.x *= -1.0f; heading = GetForward().Heading(); if(heading > 0.0f) // going west SetHeading(-heading); - }else if(GetPosition().x < -1900.0f){ + }else if(GetPosition().x < -1950.0f-400.0f){ if(m_vecMoveSpeed.x < 0.0f) m_vecMoveSpeed.x *= -1.0f; heading = GetForward().Heading(); if(heading < 0.0f) // going east SetHeading(-heading); } - if(GetPosition().y > 1900.0f){ + if(GetPosition().y > 1950.0f){ if(m_vecMoveSpeed.y > 0.0f) m_vecMoveSpeed.y *= -1.0f; heading = GetForward().Heading(); @@ -1188,7 +1647,7 @@ CAutomobile::ProcessControl(void) SetHeading(PI-heading); else if(heading > -HALFPI && heading < 0.0f) SetHeading(-PI-heading); - }else if(GetPosition().y < -1900.0f){ + }else if(GetPosition().y < -1950.0f){ if(m_vecMoveSpeed.y < 0.0f) m_vecMoveSpeed.y *= -1.0f; heading = GetForward().Heading(); @@ -1207,11 +1666,31 @@ CAutomobile::ProcessControl(void) (m_fGasPedal == 0.0f && brake == 0.0f || GetStatus() == STATUS_WRECKED)){ if(Abs(m_vecMoveSpeed.x) < 0.005f && Abs(m_vecMoveSpeed.y) < 0.005f && - Abs(m_vecMoveSpeed.z) < 0.005f){ + Abs(m_vecMoveSpeed.z) < 0.005f && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity == FindPlayerPed()) && + (m_aSuspensionSpringRatioPrev[0] < 1.0f && m_aSuspensionSpringRatioPrev[1] < 1.0f && + m_aSuspensionSpringRatioPrev[2] < 1.0f && m_aSuspensionSpringRatioPrev[3] < 1.0f)){ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed.z = 0.0f; } } + + if(IsRealHeli() && bHeliDestroyed && !bRenderScorched){ + ApplyMoveForce(0.0f, 0.0f, -2.0f*CTimer::GetTimeStep()); + m_vecTurnSpeed.z += -0.002f*CTimer::GetTimeStep(); + m_vecTurnSpeed.x += -0.0002f*CTimer::GetTimeStep(); + + RwRGBA col = { 84, 84, 84, 255 }; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, GetMatrix()*CVector(0.0f, 0.0f, -10.0f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.7f, col, 0, 0, 0, 3000); + + if(CWorld::TestSphereAgainstWorld(GetPosition(), 10.0f, this, true, false, false, false, false, false) || + GetPosition().z < 6.0f) + if(!bRenderScorched){ // we already know this is true... + CExplosion::AddExplosion(this, nil, EXPLOSION_CAR, GetPosition(), 0); + bRenderScorched = true; + } + } } void @@ -1235,6 +1714,17 @@ CAutomobile::PreRender(void) int i, j, n; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){ + // Rotate Rhino turret + CMatrix m; + CVector p; + m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + p = m.GetPosition(); + m.SetRotateZ(m_fCarGunLR); + m.Translate(p); + m.UpdateRW(); + } + if(GetModelIndex() == MI_RCBANDIT){ CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f); CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f); @@ -1245,7 +1735,7 @@ CAutomobile::PreRender(void) // Wheel particles - if(GetModelIndex() == MI_DODO){ + if(GetModelIndex() == MI_DODO || GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR){ ; // nothing }else if(GetModelIndex() == MI_RCBANDIT){ for(i = 0; i < 4; i++){ @@ -1300,6 +1790,7 @@ CAutomobile::PreRender(void) rearSkidding = true; for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatioPrev[i] < 1.0f && m_aWheelColPoints[i].surfaceB != SURFACE_WATER) switch(m_aWheelState[i]){ case WHEEL_STATE_SPINNING: if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){ @@ -1319,46 +1810,79 @@ CAutomobile::PreRender(void) 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]); + m_aWheelSkidmarkType[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); + if(Abs(fwdSpeed) > 5.0f){ + 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)); + 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]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); } break; case WHEEL_STATE_FIXED: - AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if(Abs(fwdSpeed) > 5.0f){ + 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)); + 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]); + m_aWheelSkidmarkType[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) + if((m_aWheelSkidmarkBloody[i] || m_aWheelSkidmarkUnk[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]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + } + + // Sparks for friction of burst wheels + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[i] < 1.0f){ + static float speedSq; + speedSq = m_vecMoveSpeed.MagnitudeSqr(); + if(speedSq > SQR(0.1f) && + m_aWheelColPoints[i].surfaceB != SURFACE_GRASS && + m_aWheelColPoints[i].surfaceB != SURFACE_MUD_DRY && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND_BEACH && + m_aWheelColPoints[i].surfaceB != SURFACE_WATER){ + CVector normalSpeed = m_aWheelColPoints[i].normal * DotProduct(m_aWheelColPoints[i].normal, m_vecMoveSpeed); + CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_REAR_LEFT) + frictionSpeed -= 0.05f*GetRight(); + else + frictionSpeed += 0.05f*GetRight(); + CVector unusedRight = 0.15f*GetRight(); + CVector sparkDir = 0.25f*frictionSpeed; + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + + if(speedSq > 0.04f) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + if(speedSq > 0.16f){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + } + } } } } @@ -1381,7 +1905,7 @@ CAutomobile::PreRender(void) 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]); + m_aWheelSkidmarkType[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); break; default: break; } @@ -1399,7 +1923,7 @@ CAutomobile::PreRender(void) 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]); + m_aWheelSkidmarkType[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); break; default: break; } @@ -1434,21 +1958,22 @@ CAutomobile::PreRender(void) AddDamagedVehicleParticles(); // Exhaust smoke - if(bEngineOn && fwdSpeed < 90.0f){ + if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; - CVector pos1, pos2, dir; + CVector pos1, pos2, dir1, dir2; if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ - dir.z = 0.0f; + dir1.z = 0.0f; + dir2.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; + dir1.x = steerFwd.x * r; + dir1.y = steerFwd.y * r; }else{ - dir.x = m_vecMoveSpeed.x; - dir.y = m_vecMoveSpeed.y; + dir1.x = m_vecMoveSpeed.x; + dir1.y = m_vecMoveSpeed.y; } bool dblExhaust = false; @@ -1458,17 +1983,62 @@ CAutomobile::PreRender(void) pos2 = exhaustPos; pos2.x = -pos2.x; pos2 = GetMatrix() * pos2; + dir2 = dir1; } - 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); + static float fumesLimit = 2.0f; + if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit) + for(i = 0; i < 4;){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + + static float extraFumesLimit = 0.5f; + if(m_fGasPedal > extraFumesLimit && m_nCurrentGear < 3){ + if(CGeneral::GetRandomNumber() & 1) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + else if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + } + + // Fire on Cuban hermes + if(GetModelIndex() == MI_CUBAN && i == 1 && m_fGasPedal > 0.9f){ + if(m_nCurrentGear == 1 || m_nCurrentGear == 3 && (CTimer::GetTimeInMilliseconds()%1500) > 750){ + if(CGeneral::GetRandomNumber() & 1){ + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + }else{ + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + } + } + } + + if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && + CWeather::Rain == 0.0f && i == 0){ + CVector camDist = GetPosition() - TheCamera.GetPosition(); + if(DotProduct(GetForward(), camDist) > 0.0f || + TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT){ + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + } + } + + if(GetModelIndex() == MI_CUBAN && i < 1){ + i = 1; + pos1 = GetMatrix() * CVector(1.134f, -1.276f, -0.56f); + pos2 = GetMatrix() * CVector(-1.134f, -1.276f, -0.56f); + dir1 += 0.05f*GetRight(); + dir2 -= 0.05f*GetRight(); + }else + i = 99; } - else - for(i = 0; i <= n; i++) - CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); } } @@ -1540,8 +2110,8 @@ CAutomobile::PreRender(void) 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); + //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, @@ -1579,17 +2149,26 @@ CAutomobile::PreRender(void) } break; - case MI_FBICAR: + case MI_FBIRANCH: + case MI_VICECHEE: 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); + if(GetModelIndex() == MI_VICECHEE) + CCoronas::RegisterCorona((uintptr)this + 21, + 255, 70, 70, 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::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); } @@ -1597,7 +2176,8 @@ CAutomobile::PreRender(void) case MI_TAXI: case MI_CABBIE: - case MI_BORGNINE: + case MI_ZEBRA: + case MI_KAUFMAN: if(bTaxiLight){ CVector pos = GetPosition() + GetUp()*0.95f; CCoronas::RegisterCorona((uintptr)this + 21, @@ -1614,7 +2194,8 @@ CAutomobile::PreRender(void) } if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_DODO && - GetModelIndex() != MI_RHINO) { + GetModelIndex() != MI_RHINO && GetModelIndex() != MI_RCBARON && + GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI) { // Process lights // Turn lights on/off @@ -1653,7 +2234,6 @@ CAutomobile::PreRender(void) 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 @@ -1668,7 +2248,8 @@ CAutomobile::PreRender(void) lightL -= GetRight()*2.0f*headLightPos.x; // Headlight coronas - if(behindness < 0.0f){ + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) < 0.0f && + (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON || this != FindPlayerVehicle())){ // In front of car float intensity = -0.5f*behindness + 0.3f; float size = 1.0f - behindness; @@ -1758,7 +2339,7 @@ CAutomobile::PreRender(void) lightL -= GetRight()*2.0f*tailLightPos.x; // Taillight coronas - if(behindness > 0.0f){ + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) > 0.0f){ // Behind car float intensity = 0.4f*behindness + 0.4f; float size = (behindness + 1.0f)/2.0f; @@ -1832,7 +2413,7 @@ CAutomobile::PreRender(void) 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); + 7.0f*fwd.x, 7.0f*fwd.y, 5.5f*fwd.y, -5.5f*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); @@ -1924,26 +2505,39 @@ CAutomobile::PreRender(void) // end of lights } +//TODO(MIAMI): StoreShadowForVehicle once we have it CShadows::StoreShadowForCar(this); -} -void -CAutomobile::Render(void) -{ - int i; + DoSunGlare(); + + // Heli dust + if(IsRealHeli() && m_aWheelSpeed[1] > 0.1125f && GetPosition().z < 30.0f){ + bool foundGround = false; + float waterZ = -1000.0f; + float groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, GetPosition().z, &foundGround); + if(!CWaterLevel::GetWaterLevel(GetPosition(), &waterZ, false)) + waterZ = 0.0f; + groundZ = Max(groundZ, waterZ); + float rnd = (m_aWheelSpeed[1]-0.1125f)*((int)Max(16.0f-4.0f*CTimer::GetTimeStep(),2.0f))*400.0f/43.0f; + float radius = 10.0f; + if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) + radius = 3.0f; + if(GetPosition().z - groundZ < radius) + HeliDustGenerate(this, radius-(GetPosition().z - groundZ), groundZ, Ceil(rnd)); + } + CMatrix mat; CVector pos; - CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); - if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){ - // Rotate Rhino turret - CMatrix m; - CVector p; - m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); - p = m.GetPosition(); - m.SetRotateZ(m_fCarGunLR); - m.Translate(p); - m.UpdateRW(); + bool onlyFrontWheels = false; + if(IsRealHeli()){ + // top rotor + m_aWheelRotation[1] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); + if(m_aWheelRotation[1] > TWOPI) m_aWheelRotation[1] -= TWOPI; + // rear rotor + m_aWheelRotation[3] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); + if(m_aWheelRotation[3] > TWOPI) m_aWheelRotation[3] -= TWOPI; + onlyFrontWheels = true; } CVector contactPoints[4]; // relative to model @@ -1951,7 +2545,7 @@ CAutomobile::Render(void) CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); CVector rearWheelFwd = GetForward(); for(i = 0; i < 4; i++){ - if (m_aWheelTimer[i] > 0.0f) { + if (m_aWheelTimer[i] > 0.0f && (!onlyFrontWheels || i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT)) { contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); contactSpeeds[i] = GetSpeed(contactPoints[i]); if (i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) @@ -1962,75 +2556,146 @@ CAutomobile::Render(void) } } + RwRGBA hoverParticleCol = { 255, 255, 255, 32 }; + // Rear right wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); - pos.x = mat.GetPosition().x; - pos.y = mat.GetPosition().y; + pos = mat.GetPosition(); 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_REAR_RIGHT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, + 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, + 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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 = mat.GetPosition(); 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])); + 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_REAR_LEFT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, + 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, + 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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 = mat.GetPosition(); 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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 = mat.GetPosition(); 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])); + 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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 = mat.GetPosition(); 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])); @@ -2039,8 +2704,6 @@ CAutomobile::Render(void) 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]){ @@ -2070,59 +2733,130 @@ CAutomobile::Render(void) }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 = mat.GetPosition(); 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 = mat.GetPosition(); 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 if(IsRealHeli()){ + // Top rotor + if(m_aCarNodes[CAR_BONNET]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_aWheelRotation[1]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Blurred top rotor + if(m_aCarNodes[CAR_WINDSCREEN]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateZ(-m_aWheelRotation[1]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Rear rotor + if(m_aCarNodes[CAR_BOOT]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + pos = mat.GetPosition(); + mat.SetRotateX(m_aWheelRotation[3]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Blurred rear rotor + if(m_aCarNodes[CAR_BUMP_REAR]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BUMP_REAR])); + pos = mat.GetPosition(); + mat.SetRotateX(-m_aWheelRotation[3]); + mat.Translate(pos); + mat.UpdateRW(); + } }else{ // Front right wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); - pos.x = mat.GetPosition().x; - pos.y = mat.GetPosition().y; + pos = mat.GetPosition(); 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_RIGHT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, + 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, + 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_NARROW_FRONTW) + mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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 = mat.GetPosition(); 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])); + 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); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_LEFT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, + 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, + 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_NARROW_FRONTW) + mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + 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); @@ -2130,11 +2864,104 @@ CAutomobile::Render(void) ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT); ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET); ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT); + } + + if((GetModelIndex() == MI_PHEONIX || GetModelIndex() == MI_BFINJECT) && + GetStatus() == STATUS_PLAYER && m_aCarNodes[CAR_WING_LR]){ + float rotation = 0.0f; + + if(GetModelIndex() == MI_BFINJECT) + if(m_fPropellerRotation > TWOPI) m_fPropellerRotation -= TWOPI; + + if(Abs(m_fGasPedal) > 0.0f){ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.2f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation < 1.3f){ + m_fPropellerRotation = Min(m_fPropellerRotation+0.1f*CTimer::GetTimeStep(), 1.3f); + rotation = m_fPropellerRotation; + }else{ + float wave = Sin((CTimer::GetTimeInMilliseconds()%10000)/70.0f); + rotation = m_fPropellerRotation + 0.13*wave; + } + } + }else{ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.1f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation > 0.0f){ + m_fPropellerRotation = Max(m_fPropellerRotation-0.05f*CTimer::GetTimeStep(), 0.0f); + rotation = m_fPropellerRotation; + } + } + } + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WING_LR])); + pos = mat.GetPosition(); + if(GetModelIndex() == MI_BFINJECT) + mat.SetRotateY(rotation); + else + mat.SetRotateX(rotation); + mat.Translate(pos); + mat.UpdateRW(); + } +} - mi->SetVehicleColour(m_currentColour1, m_currentColour2); +void +CAutomobile::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + + if(IsRealHeli()){ + RpAtomic *atomic = nil; + int rotorAlpha = (1.5f - Min(1.7f*Max(m_aWheelSpeed[1],0.0f)/0.22f, 1.5f))*255.0f; + rotorAlpha = Min(rotorAlpha, 255); + int blurAlpha = Max(1.5f*m_aWheelSpeed[1]/0.22f - 0.4f, 0.0f)*150.0f; + blurAlpha = Min(blurAlpha, 150); + + // Top rotor + if(m_aCarNodes[CAR_BONNET]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BONNET], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Rear rotor + if(m_aCarNodes[CAR_BOOT]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BOOT], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Blurred top rotor + if(m_aCarNodes[CAR_WINDSCREEN]){ + RwFrameForAllObjects(m_aCarNodes[CAR_WINDSCREEN], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } + atomic = nil; + // Blurred rear rotor + if(m_aCarNodes[CAR_BUMP_REAR]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BUMP_REAR], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } } - if(!CVehicle::bWheelsOnlyCheat) + if(CVehicle::bWheelsOnlyCheat){ + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + if(m_aCarNodes[CAR_WHEEL_RM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); + if(m_aCarNodes[CAR_WHEEL_LM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); + }else CEntity::Render(); } @@ -2157,6 +2984,10 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) for(i = 0; i < 4; i++) prevRatios[i] = m_aSuspensionSpringRatio[i]; + if(m_bIsVehicleBeingShifted || bSkipLineCol || ent->IsPed() || + GetModelIndex() == MI_DODO && ent->IsVehicle()) + colModel->numLines = 0; + int numCollisions = CCollision::ProcessColModels(GetMatrix(), *colModel, ent->GetMatrix(), *ent->GetColModel(), colpoints, @@ -2165,12 +2996,7 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) // m_aSuspensionSpringRatio are now set to the point where the tyre touches ground. // In ProcessControl these will be re-normalized to ignore the tyre radius. - if(m_bIsVehicleBeingShifted || bSkipLineCol || - GetModelIndex() == MI_DODO && (ent->IsPed() || ent->IsVehicle())){ - // don't do line collision - for(i = 0; i < 4; i++) - m_aSuspensionSpringRatio[i] = prevRatios[i]; - }else{ + if(colModel->numLines){ for(i = 0; i < 4; i++) if(m_aSuspensionSpringRatio[i] < 1.0f && m_aSuspensionSpringRatio[i] < prevRatios[i]){ numWheelCollisions++; @@ -2182,30 +3008,14 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) m_aGroundPhysical[i] = phys; phys->RegisterReference((CEntity**)&m_aGroundPhysical[i]); m_aGroundOffset[i] = m_aWheelColPoints[i].point - phys->GetPosition(); - - if(phys->GetModelIndex() == MI_BODYCAST && GetStatus() == STATUS_PLAYER){ - // damage body cast - float speed = m_vecMoveSpeed.MagnitudeSqr(); - if(speed > 0.1f){ - CObject::nBodyCastHealth -= 0.1f*m_fMass*speed; - DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_BODYCAST_HIT, 0.0f); - } - - // move body cast - if(phys->IsStatic()){ - phys->bIsStatic = false; - phys->m_nStaticFrames = 0; - phys->ApplyMoveForce(m_vecMoveSpeed / Sqrt(speed)); - phys->AddToMovingList(); - } - } } m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; if(ent->IsBuilding()) m_pCurGroundEntity = ent; } - } + }else + colModel->numLines = 4; if(numCollisions > 0 || numWheelCollisions > 0){ AddCollisionRecord(ent); @@ -2231,10 +3041,12 @@ CAutomobile::ProcessControlInputs(uint8 pad) { float speed = DotProduct(m_vecMoveSpeed, GetForward()); - if(CPad::GetPad(pad)->GetExitVehicle()) - bIsHandbrakeOn = true; - else + if(!CPad::GetPad(pad)->GetExitVehicle() || + pDriver && pDriver->m_pVehicleAnim && (pDriver->m_pVehicleAnim->animId == ANIM_CAR_ROLLOUT_LHS || + pDriver->m_pVehicleAnim->animId == ANIM_CAR_ROLLOUT_RHS)) bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + else + bIsHandbrakeOn = true; // Steer left/right if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ @@ -2262,8 +3074,14 @@ CAutomobile::ProcessControlInputs(uint8 pad) acceleration *= 0.3f; if(Abs(speed) < 0.01f){ // standing still, go into direction we want - m_fGasPedal = acceleration; - m_fBrakePedal = 0.0f; + if(CPad::GetPad(pad)->GetAccelerate() > 150.0f && CPad::GetPad(pad)->GetBrake() > 150.0f){ + m_fGasPedal = CPad::GetPad(pad)->GetAccelerate()/255.0f; + m_fBrakePedal = CPad::GetPad(pad)->GetBrake()/255.0f; + m_doingBurnout = 1; + }else{ + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } }else{ #if 1 // simpler than the code below @@ -2312,17 +3130,6 @@ CAutomobile::ProcessControlInputs(uint8 pad) m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; if(bComedyControls){ -#if 0 // old comedy controls from PS2 - same as bike's - if(((CTimer::GetTimeInMilliseconds() >> 10) & 0xF) < 12) - m_fGasPedal = 1.0f; - if((((CTimer::GetTimeInMilliseconds() >> 10)+6) & 0xF) < 12) - m_fBrakePedal = 0.0f; - bIsHandbrakeOn = false; - if(CTimer::GetTimeInMilliseconds() & 0x800) - m_fSteerAngle += 0.08f; - else - m_fSteerAngle -= 0.03f; -#else int rnd = CGeneral::GetRandomNumber() % 10; switch(m_comedyControlState){ case 0: @@ -2342,10 +3149,8 @@ CAutomobile::ProcessControlInputs(uint8 pad) m_comedyControlState = 0; break; } - }else{ + }else m_comedyControlState = 0; -#endif - } // Brake if player isn't in control // BUG: game always uses pad 0 here @@ -2524,22 +3329,10 @@ CAutomobile::TankControl(void) 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(); - } } -#define HYDRAULIC_UPPER_EXT (-0.12f) -#define HYDRAULIC_LOWER_EXT (0.14f) +#define HYDRAULIC_UPPER_EXT (-0.16f) +#define HYDRAULIC_LOWER_EXT (0.16f) void CAutomobile::HydraulicControl(void) @@ -2647,8 +3440,6 @@ CAutomobile::HydraulicControl(void) 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{ @@ -2722,7 +3513,6 @@ CAutomobile::HydraulicControl(void) } setPrevRatio = true; - m_aWheelPosition[i] -= 0.05f; } DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); } @@ -2753,7 +3543,6 @@ CAutomobile::HydraulicControl(void) 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; @@ -2773,15 +3562,11 @@ CAutomobile::HydraulicControl(void) 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){ + if(m_hydraulicState < 104) m_hydraulicState++; - for(i = 0; i < 4; i++) - m_aWheelPosition[i] -= 0.1f; - } if(m_fVelocityChangeForAudio < 0.1f){ normalUpperLimit += HYDRAULIC_UPPER_EXT; @@ -2847,19 +3632,47 @@ CAutomobile::ProcessBuoyancy(void) CVector impulse, point; if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ - bTouchingWater = 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){ + bool heliHitWaterHard = false; + if(IsRealHeli() && m_aWheelSpeed[1] > 0.15f){ + if(GetModelIndex() == MI_SEASPAR){ + if(impulseRatio > 3.0f){ + m_aWheelSpeed[1] = 0.0f; + heliHitWaterHard = true; + } + }else{ + float strength = 1.0f/Max(8.0f*impulseRatio, 1.0f); + ApplyMoveForce(-2.0f*impulse/strength); + ApplyTurnForce(-impulse/strength, point); + if(impulseRatio > 0.9f){ + m_aWheelSpeed[1] = 0.0f; + heliHitWaterHard = true; + }else + return; + } + } + + bTouchingWater = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + CVector initialSpeed = m_vecMoveSpeed; + + if(m_modelIndex == MI_SEASPAR && impulseRatio < 3.0f && (GetUp().z > -0.5f || impulseRatio < 0.6f) || + CVehicle::bHoverCheat && GetStatus() == STATUS_PLAYER && GetUp().z > 0.1f){ + bIsInWater = false; + bIsDrowning = false; + }else if(heliHitWaterHard || impulseRatio > 1.0f || + impulseRatio > 0.6f && (m_aSuspensionSpringRatio[0] == 1.0f || + m_aSuspensionSpringRatio[1] == 1.0f || + m_aSuspensionSpringRatio[2] == 1.0f || + m_aSuspensionSpringRatio[3] == 1.0f)){ bIsInWater = true; + bIsDrowning = true; if(m_vecMoveSpeed.z < -0.1f) m_vecMoveSpeed.z = -0.1f; @@ -2874,49 +3687,35 @@ CAutomobile::ProcessBuoyancy(void) if(pPassengers[i]->IsPlayer() || !bWaterTight) pPassengers[i]->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); } - }else + }else{ bIsInWater = false; + bIsDrowning = false; + } static uint32 nGenerateRaindrops = 0; static uint32 nGenerateWaterCircles = 0; - if(initialSpeed.z < -0.3f && impulse.z > 0.3f){ -#if defined(PC_PARTICLE) || defined (PS2_ALTERNATIVE_CARSPLASH) + if(initialSpeed.z < -0.1f && impulse.z > 0.3f || heliHitWaterHard){ 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.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*0.45f*255; + color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*0.45f*255; + color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*0.45f*255; color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128; + CVector target = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.45f)); CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(), - CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.3f)), - 0.0f, 75, color, true); -#else - CVector pos = (initialSpeed * 2.0f) + (GetPosition() + point); - - for ( int32 i = 0; i < 360; i += 4 ) - { - float fSin = Sin(float(i)); - float fCos = Cos(float(i)); - - CVector dir(fSin*0.01f, fCos*0.01f, CGeneral::GetRandomNumberInRange(0.25f, 0.45f)); - - CParticle::AddParticle(PARTICLE_CAR_SPLASH, - pos + CVector(fSin*4.5f, fCos*4.5f, 0.0f), - dir, NULL, 0.0f, CRGBA(225, 225, 255, 180)); - - for ( int32 j = 0; j < 3; j++ ) - { - float fMul = 1.5f * float(j + 1); - - CParticle::AddParticle(PARTICLE_CAR_SPLASH, - pos + CVector(fSin * fMul, fCos * fMul, 0.0f), - dir, NULL, 0.0f, CRGBA(225, 225, 255, 180)); - } - } -#endif + target, 0.0f, 75, color, true); nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300; nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60; + + if(heliHitWaterHard){ + CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() + right, + target, 0.0f, 75, color, true); + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() - right, + target, 0.0f, 75, color, true); + } + if(m_vecMoveSpeed.z < -0.2f) m_vecMoveSpeed.z = -0.2f; DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f); @@ -2957,6 +3756,7 @@ CAutomobile::ProcessBuoyancy(void) } }else{ bIsInWater = false; + bIsDrowning = false; bTouchingWater = false; static RwRGBA splashCol = {155, 155, 185, 196}; @@ -2967,13 +3767,7 @@ CAutomobile::ProcessBuoyancy(void) CVector pos = m_aWheelColPoints[i].point + 0.3f*GetUp() - GetPosition(); CVector vSpeed = GetSpeed(pos); vSpeed.z = 0.0f; -#ifdef GTA_PS2_STUFF - // ps2 puddle physics - CVector moveForce = CTimer::GetTimeStep() * (m_fMass * (vSpeed * -0.003f)); - ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); -#endif float fSpeed = vSpeed.MagnitudeSqr(); -#ifdef PC_PARTICLE if(fSpeed > sq(0.05f)){ fSpeed = Sqrt(fSpeed); @@ -2993,35 +3787,6 @@ CAutomobile::ProcessBuoyancy(void) if((CTimer::GetFrameCounter() & 0xF) == 0) DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed); } -#else - if ( ( (CTimer::GetFrameCounter() + i) & 3 ) == 0 ) - { - if(fSpeed > sq(0.05f)) - { - fSpeed = Sqrt(fSpeed); - CRGBA color(155, 185, 155, 255); - float boxY = GetColModel()->boundingBox.max.y; - CVector right = 0.5f * GetRight(); - - if ( i == 2 ) - { - CParticle::AddParticle(PARTICLE_PED_SPLASH, - GetPosition() + (boxY * GetForward()) + right, - 0.75f*m_vecMoveSpeed, NULL, 0.0f, color); - - } - else if ( i == 0 ) - { - CParticle::AddParticle(PARTICLE_PED_SPLASH, - GetPosition() + (boxY * GetForward()) - right, - 0.75f*m_vecMoveSpeed, NULL, 0.0f, color); - } - - if((CTimer::GetFrameCounter() & 0xF) == 0) - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed); - } - } -#endif } } } @@ -3030,16 +3795,21 @@ CAutomobile::ProcessBuoyancy(void) void CAutomobile::DoDriveByShootings(void) { - CAnimBlendAssociation *anim; + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + CWeapon *weapon = pDriver->GetWeapon(); - if(weapon->m_eWeaponType != WEAPONTYPE_UZI) + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) return; - weapon->Update(pDriver->m_audioEntityId); + weapon->Update(pDriver->m_audioEntityId, nil); bool lookingLeft = false; bool lookingRight = false; - if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN){ + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.m_bObbeCinematicCarCamOn){ if(CPad::GetPad(0)->GetLookLeft()) lookingLeft = true; if(CPad::GetPad(0)->GetLookRight()) @@ -3051,37 +3821,42 @@ CAutomobile::DoDriveByShootings(void) lookingRight = true; } + AnimationId rightAnim = ANIM_DRIVEBY_R; + AnimationId leftAnim = ANIM_DRIVEBY_L; + if (pDriver->m_pMyVehicle->bLowVehicle) { + rightAnim = ANIM_DRIVEBY_LOW_R; + leftAnim = ANIM_DRIVEBY_LOW_L; + } + if(lookingLeft || lookingRight){ if(lookingLeft){ - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim == nil || anim->blendDelta < 0.0f) - CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_L); - else - anim->SetRun(); + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, leftAnim); }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim == nil || anim->blendDelta < 0.0f) - CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_R); - else - anim->SetRun(); + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, rightAnim); } - if(CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer){ - weapon->FireFromCar(this, lookingLeft); - weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, true); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } } }else{ weapon->Reload(); - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim) anim->blendDelta = -1000.0f; } @@ -3089,11 +3864,49 @@ CAutomobile::DoDriveByShootings(void) // TODO: what is this? if(!lookingLeft && m_weaponDoorTimerLeft > 0.0f){ m_weaponDoorTimerLeft = Max(m_weaponDoorTimerLeft - CTimer::GetTimeStep()*0.1f, 0.0f); - ProcessOpenDoor(CAR_DOOR_LF, NUM_ANIMS, m_weaponDoorTimerLeft); + ProcessOpenDoor(CAR_DOOR_LF, NUM_STD_ANIMS, m_weaponDoorTimerLeft); } if(!lookingRight && m_weaponDoorTimerRight > 0.0f){ m_weaponDoorTimerRight = Max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); - ProcessOpenDoor(CAR_DOOR_RF, NUM_ANIMS, m_weaponDoorTimerRight); + ProcessOpenDoor(CAR_DOOR_RF, NUM_STD_ANIMS, m_weaponDoorTimerRight); + } +} + +void +CAutomobile::DoHoverSuspensionRatios(void) +{ + int i; + + if(GetUp().z < 0.1f) + return; + + CColModel *colmodel = GetColModel(); + for(i = 0; i < 4; i++){ + float z, waterZ; + CVector upper = GetMatrix() * colmodel->lines[i].p0; + CVector lower = GetMatrix() * colmodel->lines[i].p1; + if(m_aSuspensionSpringRatio[i] < 1.0f) + z = m_aWheelColPoints[i].point.z; + else + z = -100.0f; + // see if touching water + if(CWaterLevel::GetWaterLevel(lower, &waterZ, false) && + waterZ > z && lower.z-1.0f < waterZ){ + // compress spring + if(lower.z < waterZ){ + if(upper.z < waterZ) + m_aSuspensionSpringRatio[i] = 0.0f; + else + m_aSuspensionSpringRatio[i] = (upper.z - waterZ)/(upper.z - lower.z); + }else + m_aSuspensionSpringRatio[i] = 0.99999f; + + m_aWheelColPoints[i].point.x = (lower.x - upper.x)*m_aSuspensionSpringRatio[i] + upper.x; + m_aWheelColPoints[i].point.y = (lower.y - upper.y)*m_aSuspensionSpringRatio[i] + upper.y; + m_aWheelColPoints[i].point.z = waterZ; + m_aWheelColPoints[i].normal = CVector(0.01f, 0.0f, 1.0f); + m_aWheelColPoints[i].surfaceB = SURFACE_WATER; + } } } @@ -3136,7 +3949,8 @@ CAutomobile::RcbanditCheck1CarWheels(CPtrList &list) for(node = list.first; node; node = node->next){ car = (CAutomobile*)node->item; - if(this != car && car->IsCar() && car->m_scanCode != CWorld::GetCurrentScanCode()){ + if(this != car && car->IsCar() && car->GetModelIndex() != MI_RCBANDIT && + car->m_scanCode != CWorld::GetCurrentScanCode()){ car->m_scanCode = CWorld::GetCurrentScanCode(); if(Abs(this->GetPosition().x - car->GetPosition().x) < 10.0f && @@ -3210,8 +4024,7 @@ void CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) { int i; - float damageMultiplier = 0.2f; - bool doubleMoney = false; + float damageMultiplier = 0.333f; if(impulse == 0.0f){ impulse = m_fDamageImpulse; @@ -3219,19 +4032,30 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) damageMultiplier = 1.0f; } + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + impulse *= 0.5f; + CVector pos(0.0f, 0.0f, 0.0f); if(!bCanBeDamaged) return; + if(m_pDamageEntity && m_pDamageEntity->IsPed() && ((CPed*)m_pDamageEntity)->bIsStanding){ + float speed = ((CPed*)m_pDamageEntity)->m_vecAnimMoveDelta.y * DotProduct(GetForward(), m_vecDamageNormal); + if(speed < 0.0f) + impulse = Max(impulse + ((CPed*)m_pDamageEntity)->m_fMass * speed, 0.0f); + } + // damage flipped over car if(GetUp().z < 0.0f && this != FindPlayerVehicle()){ if(bNotDamagedUpsideDown || GetStatus() == STATUS_PLAYER_REMOTE || bIsInWater) return; - m_fHealth -= 4.0f*CTimer::GetTimeStep(); + if(GetStatus() != STATUS_WRECKED) + m_fHealth = Max(m_fHealth - 4.0f*CTimer::GetTimeStep(), 0.0f); } - if(impulse > 25.0f && GetStatus() != STATUS_WRECKED){ + float minImpulse = GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN ? 1.0f : 25.0f; + if(impulse > minImpulse && GetStatus() != STATUS_WRECKED){ if(bIsLawEnforcer && FindPlayerVehicle() && FindPlayerVehicle() == m_pDamageEntity && GetStatus() != STATUS_ABANDONED && @@ -3244,12 +4068,17 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) CPad::GetPad(0)->StartShake(40000/freq, freq); } - if(bOnlyDamagedByPlayer){ + if(GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer){ if(m_pDamageEntity != FindPlayerPed() && m_pDamageEntity != FindPlayerVehicle()) return; } + if(m_pDamageEntity && m_pDamageEntity->IsVehicle()){ + m_nLastWeaponDamage = WEAPONTYPE_RAMMEDBYCAR; + m_pLastDamageEntity = m_pDamageEntity; + } + if(bCollisionProof) return; @@ -3269,109 +4098,81 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) switch(damagedPiece){ case CAR_PIECE_BUMP_FRONT: GetComponentWorldPosition(CAR_BUMP_FRONT, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); - doubleMoney = true; - } if(m_aCarNodes[CAR_BONNET] && Damage.GetPanelStatus(VEHBUMPER_FRONT) == PANEL_STATUS_MISSING){ case CAR_PIECE_BONNET: GetComponentWorldPosition(CAR_BONNET, pos); - dmgDrawCarCollidingParticles(pos, impulse); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(GetModelIndex() != MI_DODO) - if(Damage.ApplyDamage(COMPONENT_DOOR_BONNET, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + if(Damage.ApplyDamage(COMPONENT_DOOR_BONNET, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_BONNET, DOOR_BONNET); - doubleMoney = true; - } } break; case CAR_PIECE_BUMP_REAR: GetComponentWorldPosition(CAR_BUMP_REAR, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_BUMPER_REAR, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_BUMPER_REAR, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); - doubleMoney = true; - } if(m_aCarNodes[CAR_BOOT] && Damage.GetPanelStatus(VEHBUMPER_REAR) == PANEL_STATUS_MISSING){ case CAR_PIECE_BOOT: GetComponentWorldPosition(CAR_BOOT, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_DOOR_BOOT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_BOOT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_BOOT, DOOR_BOOT); - doubleMoney = true; - } } break; case CAR_PIECE_DOOR_LF: GetComponentWorldPosition(CAR_DOOR_LF, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && - Damage.ApplyDamage(COMPONENT_DOOR_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); - doubleMoney = true; - } break; case CAR_PIECE_DOOR_RF: GetComponentWorldPosition(CAR_DOOR_RF, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && - Damage.ApplyDamage(COMPONENT_DOOR_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); - doubleMoney = true; - } break; case CAR_PIECE_DOOR_LR: GetComponentWorldPosition(CAR_DOOR_LR, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && - Damage.ApplyDamage(COMPONENT_DOOR_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); - doubleMoney = true; - } break; case CAR_PIECE_DOOR_RR: GetComponentWorldPosition(CAR_DOOR_RR, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && - Damage.ApplyDamage(COMPONENT_DOOR_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); - doubleMoney = true; - } break; case CAR_PIECE_WING_LF: GetComponentWorldPosition(CAR_WING_LF, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); - doubleMoney = true; - } break; case CAR_PIECE_WING_RF: GetComponentWorldPosition(CAR_WING_RF, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); - doubleMoney = true; - } break; case CAR_PIECE_WING_LR: GetComponentWorldPosition(CAR_WING_LR, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); - doubleMoney = true; - } break; case CAR_PIECE_WING_RR: GetComponentWorldPosition(CAR_WING_RR, pos); - dmgDrawCarCollidingParticles(pos, impulse); - if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); - doubleMoney = true; - } break; case CAR_PIECE_WHEEL_LF: @@ -3385,37 +4186,43 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) uint8 oldStatus = Damage.GetPanelStatus(VEHPANEL_WINDSCREEN); SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN); if(oldStatus != Damage.GetPanelStatus(VEHPANEL_WINDSCREEN)){ - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); - doubleMoney = true; + // DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); } } break; } - - if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle() && impulse > 10.0f){ - int money = (doubleMoney ? 2 : 1) * impulse*pHandling->nMonetaryValue/1000000.0f; - money = Min(money, 40); - if(money > 2){ - sprintf(gString, "$%d", money); - CWorld::Players[CWorld::PlayerInFocus].m_nMoney += money; - } - } } - float damage = (impulse-25.0f)*pHandling->fCollisionDamageMultiplier*0.6f*damageMultiplier; + float damage = (impulse-minImpulse)*pHandling->fCollisionDamageMultiplier*0.6f*damageMultiplier; if(GetModelIndex() == MI_SECURICA && m_pDamageEntity && m_pDamageEntity->GetStatus() == STATUS_PLAYER) damage *= 7.0f; + if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) + damage *= 30.0f; + if(damage > 0.0f){ + if(damage > 5.0f && + pDriver && + m_pDamageEntity && m_pDamageEntity->IsVehicle() && + (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && +// TODO(MIAMI): enum + ((CVehicle*)m_pDamageEntity)->pDriver){ + if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) + pDriver->Say(145); + else + pDriver->Say(144); + } + int oldHealth = m_fHealth; - if(this == FindPlayerVehicle()){ + if(this == FindPlayerVehicle()) m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; - }else{ - if(damage > 35.0f && pDriver) - pDriver->Say(SOUND_PED_CAR_COLLISION); - m_fHealth -= bTakeLessDamage ? damage/12.0f : damage/4.0f; - } + else if(bTakeLessDamage) + m_fHealth -= damage/12.0f; + else if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle()) + m_fHealth -= damage/1.5f; + else + m_fHealth -= damage/4.0f; if(m_fHealth <= 0.0f && oldHealth > 0) m_fHealth = 1.0f; } @@ -3490,15 +4297,16 @@ CAutomobile::dmgDrawCarCollidingParticles(const CVector &pos, float amount) void CAutomobile::AddDamagedVehicleParticles(void) { + int i, n; + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) return; - - uint8 engineStatus = Damage.GetEngineStatus(); - if(engineStatus < ENGINE_STATUS_STEAM1) + if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + return; + if(m_fHealth >= 650.0f) return; - float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()) * 180.0f; - CVector direction = 0.5f*m_vecMoveSpeed; + CVector direction = 0.85f*m_vecMoveSpeed; CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; switch(Damage.GetDoorStatus(DOOR_BONNET)){ @@ -3516,26 +4324,72 @@ CAutomobile::AddDamagedVehicleParticles(void) if(GetModelIndex() == MI_BFINJECT) damagePos = CVector(0.3f, -1.5f, -0.1f); - + else if(GetModelIndex() == MI_CADDY) + damagePos = CVector(0.6f, -1.0f, -0.25f); + else if(IsRealHeli()){ + damagePos.x = 0.4f*GetColModel()->boundingBox.max.x; + damagePos.y = 0.2f*GetColModel()->boundingBox.min.y; + damagePos.z = 0.3f*GetColModel()->boundingBox.max.z; + }else + damagePos.z += 0.4f*(GetColModel()->boundingBox.max.z-damagePos.z) * DotProduct(GetForward(), m_vecMoveSpeed); 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); + bool electric = pHandling->Transmission.nEngineType == 'E'; + + if(electric && m_fHealth < 320.0f && m_fHealth > 1.0f){ + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 7) + 2; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else if(electric && m_fHealth < 460.0f){ + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 3) + 2; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else if(m_fHealth < 250.0f){ + // nothing + }else if(m_fHealth < 320.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.8f*direction); + }else if(m_fHealth < 390.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.75f*direction); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.85f*direction); + }else if(m_fHealth < 460.0f){ + int rnd = CTimer::GetFrameCounter() + m_randomSeed; + if(rnd < 10 || + rnd < 70 && rnd > 25 || + rnd < 160 && rnd > 100 || + rnd < 200 && rnd > 175 || + rnd > 235) + return; + direction.z += 0.05f*Max(1.0f - 1.6f*m_vecMoveSpeed.Magnitude(), 0.0f); + if(electric){ + // BUG. we had that case already + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 2) + 2; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else{ + if(TheCamera.GetLookDirection() != LOOKING_FORWARD) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.75f*direction); + else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.85f*m_vecMoveSpeed); + } + }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.9f*direction); } } @@ -3547,9 +4401,11 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) static RwRGBA grassCol = { 8, 24, 8, 255 }; static RwRGBA gravelCol = { 64, 64, 64, 255 }; static RwRGBA mudCol = { 64, 32, 16, 255 }; + static RwRGBA sandCol = { 170, 165, 140, 255 }; static RwRGBA waterCol = { 48, 48, 64, 0 }; - if(!belowEffectSpeed) + if(!belowEffectSpeed && + colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) return 0; switch(colpoint->surfaceB){ @@ -3568,7 +4424,7 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) 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), gravelCol); + CGeneral::GetRandomNumberInRange(0.05f, 0.09f), gravelCol); } return 1; case SURFACE_MUD_DRY: @@ -3580,30 +4436,30 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) CGeneral::GetRandomNumberInRange(0.02f, 0.06f), mudCol); } return 0; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + if(CTimer::GetFrameCounter() & 2 || + CGeneral::GetRandomNumberInRange(CWeather::WetRoads, 1.01f) > 0.5f) + return 0; + dir.x = 0.5f*m_vecMoveSpeed.x; + dir.y = 0.5f*m_vecMoveSpeed.y; + for(i = 0; i < 1; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); + CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, + 2.0f*m_vecMoveSpeed.Magnitude(), sandCol); + } + return 0; default: - if ( CWeather::WetRoads > 0.01f -#ifdef PC_PARTICLE - && CTimer::GetFrameCounter() & 1 -#endif - ) - { - CParticle::AddParticle( -#if defined(FIX_BUGS) && !defined(PC_PARTICLE) // looks wrong on PC particles - PARTICLE_WHEEL_WATER, -#else - PARTICLE_WATERSPRAY, -#endif - colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), -#ifdef PC_PARTICLE - CVector(0.0f, 0.0f, 1.0f), -#else - CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), -#endif - nil, - CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + if(CWeather::WetRoads > 0.01f){ + if(CTimer::GetFrameCounter() & 1) + CParticle::AddParticle( + PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); return 0; } - return 1; } } @@ -3714,6 +4570,7 @@ inline void ProcessDoorOpenCloseAnimation(CAutomobile *car, uint32 component, eD car->OpenDoor(component, door, 0.0f); } } + void CAutomobile::ProcessOpenDoor(uint32 component, uint32 anim, float time) { @@ -3734,13 +4591,13 @@ CAutomobile::ProcessOpenDoor(uint32 component, uint32 anim, float time) case ANIM_CAR_QJACK: case ANIM_CAR_OPEN_LHS: case ANIM_CAR_OPEN_RHS: - ProcessDoorOpenAnimation(this, component, door, time, 0.66f, 0.8f); + ProcessDoorOpenAnimation(this, component, door, time, 0.41f, 0.89f); break; case ANIM_CAR_CLOSEDOOR_LHS: case ANIM_CAR_CLOSEDOOR_LOW_LHS: case ANIM_CAR_CLOSEDOOR_RHS: case ANIM_CAR_CLOSEDOOR_LOW_RHS: - ProcessDoorCloseAnimation(this, component, door, time, 0.2f, 0.63f); + ProcessDoorCloseAnimation(this, component, door, time, 0.2f, 0.45f); break; case ANIM_CAR_ROLLDOOR: case ANIM_CAR_ROLLDOOR_LOW: @@ -3779,7 +4636,7 @@ CAutomobile::ProcessOpenDoor(uint32 component, uint32 anim, float time) case ANIM_VAN_GETOUT: ProcessDoorOpenAnimation(this, component, door, time, 0.5f, 0.6f); break; - case NUM_ANIMS: + case NUM_STD_ANIMS: OpenDoor(component, door, time); break; } @@ -3819,6 +4676,41 @@ CAutomobile::IsDoorMissing(eDoors door) return Damage.GetDoorStatus(door) == DOOR_STATUS_MISSING; } +bool +CAutomobile::IsDoorReady(uint32 door) +{ + switch(door){ + case CAR_DOOR_RF: return IsDoorReady(DOOR_FRONT_RIGHT); + case CAR_DOOR_RR: return IsDoorReady(DOOR_REAR_RIGHT); + case CAR_DOOR_LF: return IsDoorReady(DOOR_FRONT_LEFT); + case CAR_DOOR_LR: return IsDoorReady(DOOR_REAR_LEFT); + default: + return false; + } +} + +bool +CAutomobile::IsDoorMissing(uint32 door) +{ + switch(door){ + case CAR_DOOR_RF: return IsDoorMissing(DOOR_FRONT_RIGHT); + case CAR_DOOR_RR: return IsDoorMissing(DOOR_REAR_RIGHT); + case CAR_DOOR_LF: return IsDoorMissing(DOOR_FRONT_LEFT); + case CAR_DOOR_LR: return IsDoorMissing(DOOR_REAR_LEFT); + default: + return false; + } +} + +bool +CAutomobile::IsOpenTopCar(void) +{ + return GetModelIndex() == MI_STINGER || + // component 0 is assumed to be a roof + GetModelIndex() == MI_COMET && m_aExtras[0] != 0 && m_aExtras[1] != 0 || + GetModelIndex() == MI_STALLION && m_aExtras[0] != 0 && m_aExtras[1] != 0; +} + void CAutomobile::RemoveRefsToVehicle(CEntity *ent) { @@ -3831,12 +4723,17 @@ CAutomobile::RemoveRefsToVehicle(CEntity *ent) void CAutomobile::BlowUpCar(CEntity *culprit) { - int i; RpAtomic *atomic; if(!bCanBeDamaged) return; + if(culprit == FindPlayerPed() || culprit == FindPlayerVehicle()){ + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 20; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 10.0f; + CStats::PropertyDestroyed += CGeneral::GetRandomNumber()%6000 + 4000; + } + // explosion pushes vehicle up m_vecMoveSpeed.z += 0.13f; SetStatus(STATUS_WRECKED); @@ -3866,27 +4763,7 @@ CAutomobile::BlowUpCar(CEntity *culprit) TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); - // kill driver and passengers - if(pDriver){ - CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); - if(pDriver->GetPedState() == PED_DRIVING){ - pDriver->SetDead(); - if(!pDriver->IsPlayer()) - pDriver->FlagToDestroyWhenNextProcessed(); - }else - pDriver->SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); - } - for(i = 0; i < m_nNumMaxPassengers; i++){ - if(pPassengers[i]){ - CDarkel::RegisterKillByPlayer(pPassengers[i], WEAPONTYPE_EXPLOSION); - if(pPassengers[i]->GetPedState() == PED_DRIVING){ - pPassengers[i]->SetDead(); - if(!pPassengers[i]->IsPlayer()) - pPassengers[i]->FlagToDestroyWhenNextProcessed(); - }else - pPassengers[i]->SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); - } - } + KillPedsInVehicle(); bEngineOn = false; bLightsOn = false; @@ -3916,6 +4793,10 @@ CAutomobile::SetUpWheelColModel(CColModel *colModel) CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); CColModel *vehColModel = mi->GetColModel(); + if(GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) + return false; + colModel->boundingSphere = vehColModel->boundingSphere; colModel->boundingBox = vehColModel->boundingBox; @@ -3941,10 +4822,12 @@ CAutomobile::SetUpWheelColModel(CColModel *colModel) return true; } -// this probably isn't used in III yet void -CAutomobile::BurstTyre(uint8 wheel) +CAutomobile::BurstTyre(uint8 wheel, bool applyForces) { + if(GetModelIndex() == MI_RHINO || bTyresDontBurst) + return; + switch(wheel){ case CAR_PIECE_WHEEL_LF: wheel = VEHWHEEL_FRONT_LEFT; break; case CAR_PIECE_WHEEL_LR: wheel = VEHWHEEL_REAR_LEFT; break; @@ -3955,14 +4838,18 @@ CAutomobile::BurstTyre(uint8 wheel) int status = Damage.GetWheelStatus(wheel); if(status == WHEEL_STATUS_OK){ Damage.SetWheelStatus(wheel, WHEEL_STATUS_BURST); + CStats::TyresPopped++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 0.0f); if(GetStatus() == STATUS_SIMPLE){ SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(this); } - ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f)); - ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f), GetForward()); + if(applyForces){ + ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f)); + ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f), GetForward()); + } } } @@ -4008,7 +4895,7 @@ CAutomobile::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) CVector dist = doorPos - seatPos; - // Removing that makes this func. return false for van doors. + // Removing that makes thiProcessEntityCollisions func. return false for van doors. doorPos.z += 0.5f; float length = dist.Magnitude(); CVector pedPos = seatPos + dist*((length+0.6f)/length); @@ -4039,10 +4926,16 @@ CAutomobile::PlayCarHorn(void) { int r; - if(m_nCarHornTimer != 0) + if (IsAlarmOn() || m_nCarHornTimer != 0) + return; + + if (m_nCarHornDelay) { + m_nCarHornDelay--; return; + } - r = CGeneral::GetRandomNumber() & 7; + m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; + r = m_nCarHornDelay & 7; if(r < 2){ m_nCarHornTimer = 45; }else if(r < 4){ @@ -4064,7 +4957,6 @@ CAutomobile::PlayHornIfNecessary(void) PlayCarHorn(); } - void CAutomobile::ResetSuspension(void) { @@ -4107,8 +4999,8 @@ CAutomobile::SetupSuspensionLines(void) } // Compress spring somewhat to get normal height on road - m_fHeightAboveRoad = -(colModel->lines[0].p0.z + (colModel->lines[0].p1.z - colModel->lines[0].p0.z)* - (1.0f - 1.0f/(8.0f*pHandling->fSuspensionForceLevel))); + m_fHeightAboveRoad = m_aSuspensionSpringLength[0]*(1.0f - 1.0f/(4.0f*pHandling->fSuspensionForceLevel)) + - colModel->lines[0].p0.z + mi->m_wheelScale*0.5f; for(i = 0; i < 4; i++) m_aWheelPosition[i] = mi->m_wheelScale*0.5f - m_fHeightAboveRoad; @@ -4142,13 +5034,16 @@ CAutomobile::BlowUpCarsInPath(void) { int i; - if(m_vecMoveSpeed.Magnitude() > 0.1f) + if(m_vecMoveSpeed.Magnitude() > 0.1f && bTankDetonateCars) for(i = 0; i < m_nCollisionRecords; i++) if(m_aCollisionRecords[i] && m_aCollisionRecords[i]->IsVehicle() && m_aCollisionRecords[i]->GetModelIndex() != MI_RHINO && - !m_aCollisionRecords[i]->bRenderScorched) + !m_aCollisionRecords[i]->bRenderScorched){ + if(this == FindPlayerVehicle()) + CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, this, FindPlayerPed(), 2000); ((CVehicle*)m_aCollisionRecords[i])->BlowUpCar(this); + } } bool @@ -4241,6 +5136,9 @@ CAutomobile::ProcessSwingingDoor(int32 component, eDoors door) if(Damage.GetDoorStatus(door) != DOOR_STATUS_SWINGING) return; + if (m_aCarNodes[component] == nil) + return; + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); CVector pos = mat.GetPosition(); float axes[3] = { 0.0f, 0.0f, 0.0f }; @@ -4250,6 +5148,27 @@ CAutomobile::ProcessSwingingDoor(int32 component, eDoors door) mat.SetRotate(axes[0], axes[1], axes[2]); mat.Translate(pos); mat.UpdateRW(); + + // make wind rip off bonnet + if(door == DOOR_BONNET && Doors[door].m_nDoorState == DOORST_OPEN && + DotProduct(m_vecMoveSpeed, GetForward()) > 0.4f){ +#ifdef FIX_BUGS + CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_BONNET); +#else + CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); +#endif + // make both doors invisible on car + SetComponentVisibility(m_aCarNodes[CAR_BONNET], ATOMIC_FLAG_NONE); + Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); + + if(comp){ + if(CGeneral::GetRandomNumber() & 1) + comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed + 0.1f*GetRight() + 0.5f*GetUp(); + else + comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed - 0.1f*GetRight() + 0.5f*GetUp(); + comp->ApplyTurnForce(10.0f*GetUp(), GetForward()); + } + } } void @@ -4276,6 +5195,19 @@ CAutomobile::Fix(void) mat.UpdateRW(); } } + + for(component = 0; component < 4; component++) + Damage.SetWheelStatus(component, WHEEL_STATUS_OK); + + if(GetModelIndex() == MI_HUNTER){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(IsRealHeli()){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + } } void @@ -4317,8 +5249,6 @@ GetCurrentAtomicObjectCB(RwObject *object, void *data) return object; } -static CColPoint aTempPedColPts[MAX_COLLISION_POINTS]; - CObject* CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) { @@ -4351,6 +5281,7 @@ CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) case COMPGROUP_DOOR: obj->SetModelIndexNoCreate(MI_CAR_DOOR); obj->SetCenterOfMass(0.0f, -0.5f, 0.0f); + obj->bDrawLast = true; break; case COMPGROUP_BONNET: obj->SetModelIndexNoCreate(MI_CAR_BONNET); @@ -4377,6 +5308,7 @@ CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; // init object obj->m_fMass = 10.0f; @@ -4435,9 +5367,16 @@ CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) obj->m_fAirResistance = 0.99f; } + if(GetStatus() == STATUS_WRECKED && IsVisible() && DotProduct(dist, TheCamera.GetPosition() - GetPosition()) > -0.5f){ + dist = TheCamera.GetPosition() - GetPosition(); + dist.Normalise(); + dist.z += 0.3f; + ApplyMoveForce(5.0f*dist); + } + if(CCollision::ProcessColModels(obj->GetMatrix(), *obj->GetColModel(), this->GetMatrix(), *this->GetColModel(), - aTempPedColPts, nil, nil) > 0) + CWorld::m_aTempColPts, nil, nil) > 0) obj->m_pCollidingEntity = this; if(bRenderScorched) @@ -4475,11 +5414,14 @@ CAutomobile::SetPanelDamage(int32 component, ePanels panel, bool noFlyingCompone if(m_aCarNodes[component] == nil) return; if(status == PANEL_STATUS_SMASHED1){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); // show damaged part SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); }else if(status == PANEL_STATUS_MISSING){ if(!noFlyingComponents) SpawnFlyingComponent(component, COMPGROUP_PANEL); + else + CGlass::CarWindscreenShatters(this, false); // hide both SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); } @@ -4515,6 +5457,11 @@ CAutomobile::SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents return; } + if(!CanDoorsBeDamaged() && status > DOOR_STATUS_SMASHED && door != DOOR_BONNET && door != DOOR_BOOT){ + Damage.SetDoorStatus(door, DOOR_STATUS_SMASHED); + status = DOOR_STATUS_SMASHED; + } + if(door == DOOR_BOOT && status == DOOR_STATUS_SWINGING && pHandling->Flags & HANDLING_NOSWING_BOOT){ Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); status = DOOR_STATUS_MISSING; @@ -4611,6 +5558,123 @@ CAutomobile::SetAllTaxiLights(bool set) m_sAllTaxiLights = set; } +void +CAutomobile::TellHeliToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_HELI_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f){ + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y) + PI; + while(m_fOrientation > TWOPI) m_fOrientation -= TWOPI; + } +} + +void +CAutomobile::TellPlaneToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_PLANE_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f) + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); +} + +void +CAutomobile::PopBoot(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Doors[DOOR_BOOT].m_fAngle = Doors[DOOR_BOOT].m_fMinAngle; + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[DOOR_BOOT])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + axes[Doors[DOOR_BOOT].m_nAxis] = Doors[DOOR_BOOT].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); + } +} + +void +CAutomobile::PopBootUsingPhysics(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)) + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_SWINGING); + Doors[DOOR_BOOT].m_fAngle = -2.0f; +} + +void +CAutomobile::CloseAllDoors(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(!IsDoorMissing(DOOR_FRONT_LEFT)) + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + if(mi->m_numDoors > 1){ + if(!IsDoorMissing(DOOR_FRONT_RIGHT)) + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + if(mi->m_numDoors > 2){ + if(!IsDoorMissing(DOOR_REAR_LEFT)) + OpenDoor(CAR_DOOR_LR, DOOR_REAR_LEFT, 0.0f); + if(!IsDoorMissing(DOOR_REAR_RIGHT)) + OpenDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT, 0.0f); + } + } +} + +void +CAutomobile::KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped) +{ + AnimationId anim = ANIM_KO_SHOT_FRONT1; + if(ped == nil) + return; + + ped->m_vehEnterType = door; + ped->SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_IDLE_STANCE, 100.0f); + CPed::PedSetOutCarCB(nil, ped); + ped->SetMoveState(PEDMOVE_STILL); + if(GetUp().z < 0.0f) + ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); + else + ped->SetHeading(GetForward().Heading()); + + switch(weapon){ + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_UNIDENTIFIED: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->m_pCollidingEntity = this; + anim = NUM_STD_ANIMS; + break; + + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_RAMMEDBYCAR: + case WEAPONTYPE_FALL: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + anim = ANIM_KD_LEFT; + ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); + break; + } + + if(weapon != WEAPONTYPE_UNARMED){ + ped->SetFall(1000, anim, 0); + ped->bIsStanding = false; + ped->m_headingRate = 0.0f; + } + ped->m_pMyVehicle = nil; +} + #ifdef COMPATIBLE_SAVES void CAutomobile::Save(uint8*& buf) |