diff options
Diffstat (limited to 'src/vehicles')
-rw-r--r-- | src/vehicles/Automobile.cpp | 2290 | ||||
-rw-r--r-- | src/vehicles/Automobile.h | 46 | ||||
-rw-r--r-- | src/vehicles/Bike.cpp | 1983 | ||||
-rw-r--r-- | src/vehicles/Bike.h | 121 | ||||
-rw-r--r-- | src/vehicles/Boat.cpp | 114 | ||||
-rw-r--r-- | src/vehicles/Boat.h | 19 | ||||
-rw-r--r-- | src/vehicles/CarGen.cpp | 134 | ||||
-rw-r--r-- | src/vehicles/CarGen.h | 5 | ||||
-rw-r--r-- | src/vehicles/Cranes.cpp | 5 | ||||
-rw-r--r-- | src/vehicles/DamageManager.cpp | 20 | ||||
-rw-r--r-- | src/vehicles/HandlingMgr.cpp | 312 | ||||
-rw-r--r-- | src/vehicles/HandlingMgr.h | 160 | ||||
-rw-r--r-- | src/vehicles/Heli.cpp | 72 | ||||
-rw-r--r-- | src/vehicles/Heli.h | 3 | ||||
-rw-r--r-- | src/vehicles/Plane.cpp | 4 | ||||
-rw-r--r-- | src/vehicles/Plane.h | 2 | ||||
-rw-r--r-- | src/vehicles/Train.cpp | 26 | ||||
-rw-r--r-- | src/vehicles/Train.h | 2 | ||||
-rw-r--r-- | src/vehicles/Vehicle.cpp | 1617 | ||||
-rw-r--r-- | src/vehicles/Vehicle.h | 131 |
20 files changed, 5919 insertions, 1147 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index eb1bad97..d0157c44 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_NONE && colModel->level != CCollision::ms_collisionInMemory) + if(CReplay::IsPlayingBack()) return; + UpdatePassengerList(); + + // Heli wind + if(IsRealHeli()) + if((GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS) && m_aWheelSpeed[1] > 0.075f || + GetStatus() == STATUS_SIMPLE) + CWindModifiers::RegisterOne(GetPosition(), 1); + // 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_VEHICLE, 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())*m_vecMoveSpeed; + 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_VEHICLE) && !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_VEHICLE) && !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] += 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]; + } + 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_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_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(GetModelIndex())){ 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; @@ -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 > 0.01f && + 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, ceilf(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(); + } +} + +void +CAutomobile::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); - 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*)this)->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); + 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,6 +4436,19 @@ 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 @@ -3594,11 +4463,7 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) 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); return 0; @@ -3714,6 +4579,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 +4600,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 +4645,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 +4685,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 +4732,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 +4772,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 +4802,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 +4831,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 +4847,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 +4904,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 +4935,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 +4966,6 @@ CAutomobile::PlayHornIfNecessary(void) PlayCarHorn(); } - void CAutomobile::ResetSuspension(void) { @@ -4107,8 +5008,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 +5043,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 @@ -4250,6 +5154,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 +5201,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 +5255,6 @@ GetCurrentAtomicObjectCB(RwObject *object, void *data) return object; } -static CColPoint aTempPedColPts[MAX_COLLISION_POINTS]; - CObject* CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) { @@ -4351,6 +5287,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 +5314,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 +5373,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 +5420,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 +5463,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 +5564,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) + TWOPI; + while(m_fOrientation > TWOPI) m_fOrientation -= TWOPI; + } +} + +void +CAutomobile::TellPlaneToGoToCoors(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); +} + +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) diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h index 604bed17..186ed646 100644 --- a/src/vehicles/Automobile.h +++ b/src/vehicles/Automobile.h @@ -3,6 +3,7 @@ #include "Vehicle.h" #include "DamageManager.h" #include "Door.h" +#include "Skidmarks.h" class CObject; @@ -39,16 +40,6 @@ enum { CARWHEEL_REAR_RIGHT }; -enum eBombType -{ - CARBOMB_NONE, - CARBOMB_TIMED, - CARBOMB_ONIGNITION, - CARBOMB_REMOTE, - CARBOMB_TIMEDACTIVE, - CARBOMB_ONIGNITIONACTIVE, -}; - enum { CAR_DOOR_FLAG_UNKNOWN = 0x0, CAR_DOOR_FLAG_LF = 0x1, @@ -60,7 +51,6 @@ enum { class CAutomobile : public CVehicle { public: - // 0x288 CDamageManager Damage; CDoor Doors[6]; RwFrame *m_aCarNodes[NUM_CAR_NODES]; @@ -69,22 +59,23 @@ public: float m_aSuspensionSpringRatioPrev[4]; float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented float m_auto_unused1; - bool m_aWheelSkidmarkMuddy[4]; + eSkidmarkType m_aWheelSkidmarkType[4]; bool m_aWheelSkidmarkBloody[4]; + bool m_aWheelSkidmarkUnk[4]; float m_aWheelRotation[4]; float m_aWheelPosition[4]; float m_aWheelSpeed[4]; uint8 m_auto_unused2; - uint8 m_bombType : 3; uint8 bTaxiLight : 1; - uint8 bDriverLastFrame : 1; // for bombs uint8 bFixedColour : 1; uint8 bBigWheels : 1; uint8 bWaterTight : 1; // no damage for non-player peds uint8 bNotDamagedUpsideDown : 1; uint8 bMoreResistantToDamage : 1; - CEntity *m_pBombRigger; - int16 m_auto_unk1; + uint8 bTankDetonateCars : 1; + uint8 bStuckInSand : 1; + uint8 bHeliDestroyed : 1; + int16 m_doingBurnout; uint16 m_hydraulicState; uint32 m_nBusDoorTimerEnd; uint32 m_nBusDoorTimerStart; @@ -92,6 +83,9 @@ public: float m_aSuspensionLineLength[4]; float m_fHeightAboveRoad; float m_fTraction; + float m_fTireTemperature; + float m_fOrientation; // for heli and plane go-to + float m_auto_unk4; // related to the above float m_fVelocityChangeForAudio; float m_randomValues[6]; // used for what? float m_fFireBlowUpTimer; @@ -102,6 +96,7 @@ public: float m_weaponDoorTimerRight; float m_fCarGunLR; float m_fCarGunUD; + float m_fHeliOrientation; float m_fPropellerRotation; uint8 stuff4[4]; uint8 m_nWheelsOnGround; @@ -135,10 +130,13 @@ public: bool IsDoorFullyOpen(eDoors door); bool IsDoorClosed(eDoors door); bool IsDoorMissing(eDoors door); + bool IsDoorReady(uint32 door); + bool IsDoorMissing(uint32 door); + bool IsOpenTopCar(void); void RemoveRefsToVehicle(CEntity *ent); void BlowUpCar(CEntity *ent); bool SetUpWheelColModel(CColModel *colModel); - void BurstTyre(uint8 tyre); + void BurstTyre(uint8 tyre, bool applyForces); bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); float GetHeightAboveRoad(void); void PlayCarHorn(void); @@ -149,6 +147,7 @@ public: void VehicleDamage(float impulse, uint16 damagedPiece); void ProcessBuoyancy(void); void DoDriveByShootings(void); + void DoHoverSuspensionRatios(void); int32 RcbanditCheckHitWheels(void); int32 RcbanditCheck1CarWheels(CPtrList &list); void PlaceOnRoadProperly(void); @@ -171,6 +170,11 @@ public: void SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents = false); void SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents = false); + void TellHeliToGoToCoors(float x, float y, float z, uint8 speed); + void TellPlaneToGoToCoors(float x, float y, float z, uint8 speed); + void SetHeliOrientation(float orient) { m_fHeliOrientation = orient; } + void ClearHeliOrientation(void) { m_fHeliOrientation = -1.0f; } + void Fix(void); void SetComponentVisibility(RwFrame *frame, uint32 flags); void SetupModelNodes(void); @@ -179,6 +183,12 @@ public: void HideAllComps(void); void ShowAllComps(void); void ReduceHornCounter(void); + + void PopBoot(void); + void PopBootUsingPhysics(void); + void CloseAllDoors(void); + void KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped); + #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); @@ -188,8 +198,6 @@ public: static void SetAllTaxiLights(bool set); }; -VALIDATE_SIZE(CAutomobile, 0x5A8); - inline uint8 GetCarDoorFlag(int32 carnode) { switch (carnode) { case CAR_DOOR_LF: diff --git a/src/vehicles/Bike.cpp b/src/vehicles/Bike.cpp new file mode 100644 index 00000000..a6fb2b82 --- /dev/null +++ b/src/vehicles/Bike.cpp @@ -0,0 +1,1983 @@ +#include "common.h" +#include "General.h" +#include "Pad.h" +#include "DMAudio.h" +#include "Camera.h" +#include "Darkel.h" +#include "Rubbish.h" +#include "Explosion.h" +#include "Particle.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Record.h" +#include "CarCtrl.h" +#include "CarAI.h" +#include "Stats.h" +#include "Replay.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "DamageManager.h" +#include "Vehicle.h" +#include "Automobile.h" +#include "Bike.h" +#include "Debug.h" + +#define FAKESUSPENSION (99999.992f) + +CBike::CBike(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + switch(id){ + case MI_ANGEL: + case MI_FREEWAY: + m_bikeAnimType = ASSOCGRP_BIKE_HARLEY; + break; + case MI_PIZZABOY: + case MI_FAGGIO: + m_bikeAnimType = ASSOCGRP_BIKE_VESPA; + break; + case MI_PCJ600: + m_bikeAnimType = ASSOCGRP_BIKE_STANDARD; + break; + case MI_SANCHEZ: + m_bikeAnimType = ASSOCGRP_BIKE_DIRT; + break; + default: assert(0 && "invalid bike model ID"); + } + m_vehType = VEHICLE_TYPE_BIKE; + + m_fFireBlowUpTimer = 0.0f; + m_doingBurnout = 0; + m_bike_flag01 = false; + + SetModelIndex(id); + + pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); + pBikeHandling = mod_HandlingManager.GetBikePointer((eHandlingId)mi->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((eHandlingId)mi->m_handlingId); + + m_bike_unused1 = 20.0f; + m_bike_unused2 = 0; + + mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); + + m_fRearForkLength = 0.0f; + m_fFrontForkY = 0.0; + m_fFrontForkZ = 0.0; + m_fFrontForkSlope = Tan(DEGTORAD(mi->m_bikeSteerAngle)); + + m_fMass = pHandling->fMass; + m_fTurnMass = pHandling->fTurnMass; + m_vecCentreOfMass = pHandling->CentreOfMass; + m_vecCentreOfMass.z = 0.1f; + m_fAirResistance = pHandling->Dimension.x*pHandling->Dimension.z/m_fMass; + m_fElasticity = 0.05f; + m_fBuoyancy = pHandling->fBuoyancy; + + m_fSteerAngle = 0.0f; + m_fWheelAngle = 0.0f; + m_fLeanLRAngle = 0.0f; + m_fLeanLRAngle2 = 0.0f; + m_fGasPedal = 0.0f; + m_fBrakePedal = 0.0f; + m_fLeanInput = 0.0f; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + m_pSetOnFireEntity = nil; + m_pBombRigger = nil; + m_fGasPedalAudio = 0.0f; + m_bike_flag02 = false; + bWaterTight = false; + m_bike_flag08 = false; + bIsStanding = false; + bExtraSpeed = false; + bIsOnFire = false; + m_bike_flag80 = false; + + m_fTireTemperature = 0.0f; + m_fBrakeDestabilization = 0.0f; + field_490 = 0; + + for(i = 0; i < 2; i++){ + m_aWheelRotation[i] = 0.0f; + m_aWheelSpeed[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; + m_aWheelSkidmarkBloody[i] = false; + m_aWheelSkidmarkUnk[0] = false; + m_wheelStatus[i] = WHEEL_STATUS_OK; + } + + for(i = 0; i < 4; i++){ + m_aGroundPhysical[i] = nil; + m_aGroundOffset[i] = CVector(0.0f, 0.0f, 0.0f); + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + } + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = 0; + m_fHeightAboveRoad = 0.0f; + m_fTraction = 1.0f; + + CColModel *colModel = mi->GetColModel(); + if(colModel->lines == nil){ + colModel->lines = (CColLine*)RwMalloc(4*sizeof(CColLine)); + colModel->numLines = 4; + } + // BUG? this would make more sense in the if above + colModel->lines[0].p0.z = FAKESUSPENSION; + + SetupSuspensionLines(); + + AutoPilot.m_nCarMission = MISSION_NONE; + AutoPilot.m_nTempAction = TEMPACT_NONE; + AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + AutoPilot.m_bStayInCurrentLevel = false; + + SetStatus(STATUS_SIMPLE); + bUseCollisionRecords = true; + m_nNumPassengers = 0; + bIsVan = false; + bIsBus = false; + bIsBig = false; + bLowVehicle = false; + bPedPhysics = false; + + bLeanMatrixClean = false; + m_leanMatrix = GetMatrix(); +} + +void +CBike::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); + SetupModelNodes(); +} + +#define SAND_SLOWDOWN (0.02f) +CVector vecTestResistance(0.9995f, 0.9f, 0.95f); +float fDAxisX = 1.0f; +float fDAxisXExtra = 100.0f; +float fDAxisY = 1000.0f; +float fInAirXRes = 0.88f; +float fFlySpeedMult = -0.6f; + +void +CBike::ProcessControl(void) +{ + int i; + float wheelRot; + float acceleration = 0.0f; + bool bCanStand = false; + bool bStuckInSand = false; + float brake = 0.0f; + CColModel *colModel = GetColModel(); + float wheelScale = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_wheelScale; + bWarnedPeds = false; + bLeanMatrixClean = false; + m_doingBurnout = 0; + bExtraSpeed = false; + bRestingOnPhysical = false; + + if(CReplay::IsPlayingBack()) + return; + + ProcessCarAlarm(); + + ActivateBombWhenEntered(); + + CRubbish::StirUp(this); + + UpdateClumpAlpha(); + + AutoPilot.m_bSlowedDownBecauseOfCars = false; + AutoPilot.m_bSlowedDownBecauseOfPeds = false; + + switch(GetStatus()){ + case STATUS_PLAYER: + bCanStand = true; + m_bike_flag08 = false; + if(FindPlayerPed()->GetPedState() != PED_EXIT_CAR && FindPlayerPed()->GetPedState() != PED_DRAG_FROM_CAR){ + ProcessControlInputs(0); + + if(m_fLeanInput < 0.0f){ + m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanBakCOM*m_fLeanInput; + CVector com = m_vecCentreOfMass; +#ifdef FIX_BUGS + // center of mass has to have world space orientation. unfortunately we can't do wheelies + // at high speed then, flipping y here is like riding south without this fix where wheelies work + com.y = -com.y; + com = Multiply3x3(GetMatrix(), com); +#endif + if(m_fBrakePedal == 0.0f && !bIsHandbrakeOn || m_nWheelsOnGround == 0){ + if(GetModelIndex() == MI_SANCHEZ){ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + force *= 0.7f*m_fGasPedal + 0.3f; + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + }else{ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + force *= 0.5f*m_fGasPedal + 0.5f; + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + } + } + }else{ + m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanFwdCOM*m_fLeanInput; + CVector com = m_vecCentreOfMass; +#ifdef FIX_BUGS + // see above + com.y = -com.y; + com = Multiply3x3(GetMatrix(), com); +#endif + if(m_fBrakePedal < 0.0f || m_nWheelsOnGround == 0){ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanFwdForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + } + } + + PruneReferences(); + + if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) + DoDriveByShootings(); + + 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){ + CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); + if(m_fGasPedal > 0.3f){ + if(parallelSpeed.MagnitudeSqr() < SQR(0.3f)) + bStuckInSand = true; + parallelSpeed -= DotProduct(parallelSpeed, GetForward())*GetForward(); + } + ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); + } + } + if(CPad::GetPad(0)->WeaponJustDown()) + ActivateBomb(); + break; + + case STATUS_PLAYER_PLAYBACKFROMBUFFER: + bCanStand = true; + break; + + case STATUS_SIMPLE: + CCarAI::UpdateCarAI(this); + CPhysical::ProcessControl(); + CCarCtrl::UpdateCarOnRails(this); + + m_nWheelsOnGround = 2; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 2; + + pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); + + wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.5f*wheelScale); + for(i = 0; i < 2; i++) + m_aWheelRotation[i] += wheelRot; + + PlayHornIfNecessary(); + ReduceHornCounter(); + bVehicleColProcessed = false; + bAudioChangingGear = false; + m_bike_flag80 = false; + // that's all we do for simple vehicles + return; + + case STATUS_PHYSICS: + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + PlayHornIfNecessary(); + + bCanStand = true; + m_bike_flag80 = false; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + }else + m_bike_flag08 = false; + break; + + case STATUS_ABANDONED: + m_fBrakePedal = 0.0f; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || bIsStanding) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = false; + + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bCanStand = (pDriver || pPassengers[0] || bIsBeingCarJacked) && !bIsStanding; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + m_bike_flag80 = false; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + break; + + case STATUS_WRECKED: + m_fBrakePedal = 0.05f; + bIsHandbrakeOn = true; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bCanStand = false; + m_bike_flag80 = false; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + break; + + case STATUS_PLAYER_DISABLED: + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + }else{ + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + } + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bCanStand = true; + m_bike_flag80 = false; + break; + } + + if(bIsStanding) + if(Abs(GetRight().z) > 0.35f || Abs(GetForward().z) > 0.5f) + bIsStanding = false; + + if(bCanStand || m_bike_flag08 || bIsStanding){ + float fDx = fDAxisX; + CVector res = vecTestResistance; + CVector localTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); + + if(GetStatus() == STATUS_PLAYER){ + if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f){ + fDx = fDAxisXExtra; + if(!(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f) && + GetForward().z > 0.0f) + res.x -= Max(0.25f*Abs(pBikeHandling->fWheelieAng-GetForward().z), 0.07f); + else + res.x = fInAirXRes; + }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f){ + fDx = fDAxisXExtra; + if(GetForward().z < 0.0f) + res.x *= Max(0.3f*Abs(pBikeHandling->fStoppieAng-GetForward().z), 0.1f) + 0.9f; + } + } + + res.x *= 1.0f/(fDx*SQR(localTurnSpeed.x) + 1.0f); + res.y *= 1.0f/(fDAxisY*SQR(localTurnSpeed.y) + 1.0f); + res.x = Pow(res.x, CTimer::GetTimeStep()); + res.y = Pow(res.y, CTimer::GetTimeStep()); + float turnX = localTurnSpeed.x*(res.x - 1.0f); + float turnY = localTurnSpeed.y*(res.y - 1.0f); + + res = -GetUp() * turnY * m_fTurnMass; + ApplyTurnForce(res, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + res = GetUp() * turnX * m_fTurnMass; + ApplyTurnForce(res, GetForward() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + if(GetStatus() != STATUS_PLAYER) + m_vecCentreOfMass = pHandling->CentreOfMass; + }else{ + m_vecCentreOfMass = pHandling->CentreOfMass; + m_vecCentreOfMass.z = pBikeHandling->fNoPlayerCOMz; + } + + // Skip physics if object is found to have been static recently + bool skipPhysics = false; + if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED) && !m_bike_flag08){ + bool makeStatic = false; + float moveSpeedLimit, turnSpeedLimit, distanceLimit; + + if(!bVehicleColProcessed && + m_vecMoveSpeed.IsZero() && + // BUG? m_aSuspensionSpringRatioPrev[3] is checked twice in the game. also, why 3? + m_aSuspensionSpringRatioPrev[3] != 1.0f) + makeStatic = true; + + if(GetStatus() == STATUS_WRECKED){ + moveSpeedLimit = 0.006f; + turnSpeedLimit = 0.0015f; + distanceLimit = 0.015f; + }else{ + moveSpeedLimit = 0.003f; + turnSpeedLimit = 0.0009f; + distanceLimit = 0.005f; + } + + m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; + m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; + + if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && + m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && + m_fDistanceTravelled < distanceLimit && + makeStatic){ + m_nStaticFrames++; + + if(m_nStaticFrames > 10 || makeStatic) + if(!CCarCtrl::MapCouldMoveInThisArea(GetPosition().x, GetPosition().y)){ + if(!makeStatic || m_nStaticFrames > 10) + m_nStaticFrames = 10; + + skipPhysics = true; + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + }else + m_nStaticFrames = 0; + } + + // Postpone + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i]){ + bRestingOnPhysical = true; + if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } + } + + if(bRestingOnPhysical){ + skipPhysics = false; + m_nStaticFrames = 0; + } + + VehicleDamage(); + + if(skipPhysics){ + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + 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); +// missing. BUG? +// m_fTireTemperature = 1.0f; + + if(bIsStanding && m_fWheelAngle < DEGTORAD(20.0f)) + m_fWheelAngle += DEGTORAD(1.0f)*CTimer::GetTimeStep(); + if(bIsStanding){ + float f = Pow(0.97f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(clamp(GetRight().z,-1.0f,1.0f))+DEGTORAD(15.0f))*(1.0f-f); + m_fLeanLRAngle = m_fLeanLRAngle2; + } + }else{ + + // This has to be done if ProcessEntityCollision wasn't called + if(!bVehicleColProcessed){ + CMatrix mat(GetMatrix()); + bIsStuck = false; + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_fDistanceTravelled = 0.0f; + m_bIsVehicleBeingShifted = false; + bSkipLineCol = false; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + for(i = 0; CheckCollision() && i < 5; i++){ + GetMatrix() = mat; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + } + bIsInSafePosition = true; + bIsStuck = false; + } + + if(!(bCanStand || m_bike_flag08 || bIsStanding)){ + if(GetRight().z < 0.0f){ + if(m_fSteerAngle > -DEGTORAD(25.0f)) + m_fSteerAngle -= DEGTORAD(0.5f)*CTimer::GetTimeStep(); + }else{ + if(m_fSteerAngle < DEGTORAD(25.0f)) + m_fSteerAngle += DEGTORAD(0.5f)*CTimer::GetTimeStep(); + } + } + + // Lean forward speed up + float savedAirResistance = m_fAirResistance; + if(GetStatus() == STATUS_PLAYER && pDriver){ + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_FWD); + if(assoc && assoc->blendAmount > 0.5f && + assoc->currentTime > 0.06f && assoc->currentTime < 0.14f){ + m_fAirResistance *= 0.6f; + if(m_fGasPedal > 0.5f && DotProduct(m_vecMoveSpeed, GetForward()) > 0.25f){ + ApplyMoveForce(0.2f*m_fMass*GRAVITY*CTimer::GetTimeStep()*GetForward()); + bExtraSpeed = true; + } + } + } + + CPhysical::ProcessControl(); + m_fAirResistance = savedAirResistance; + + ProcessBuoyancy(); + // Rescale spring ratios, i.e. subtract wheel radius + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + // rescale such that 0.0 is fully compressed and 1.0 is fully extended + m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } + + int rnd = 0; + float fwdSpeed = Abs(DotProduct(m_vecMoveSpeed, GetForward())); + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector springDirections[4]; // normalized, in model space + + for(i = 0; i < 4; i++){ + // Set spring under certain circumstances + if(m_wheelStatus[i/2] == WHEEL_STATUS_MISSING) + m_aSuspensionSpringRatio[i] = 1.0f; + else if(m_wheelStatus[i/2] == WHEEL_STATUS_BURST){ + // wheel more bumpy the faster we are + if(i == BIKESUSP_F1 || BIKESUSP_R1) + rnd = CGeneral::GetRandomNumberInRange(0, (uint16)(40*fwdSpeed) + 98) < 100; + if(rnd){ + m_aSuspensionSpringRatio[i] += 0.3f*(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 + if(m_aSuspensionSpringRatio[i] < 1.0f){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + springDirections[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1 - colModel->lines[i].p0); + springDirections[i].Normalise(); + } + } + + m_aWheelSkidmarkType[0] = m_aWheelSkidmarkType[1] = SKIDMARK_NORMAL; + m_aWheelSkidmarkUnk[0] = m_aWheelSkidmarkUnk[1] = false; + + // Make springs push up vehicle + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f){ + float bias = pHandling->fSuspensionBias; + if(i == BIKESUSP_R1 || i == BIKESUSP_R2) + bias = 1.0f - bias; + + if(m_aWheelColPoints[i].normal.z > 0.35f) + ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); + else + ApplySpringCollision(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias); + + if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY){ + if(i < 2) + m_aWheelSkidmarkType[0] = SKIDMARK_MUDDY; + else + m_aWheelSkidmarkType[1] = SKIDMARK_MUDDY; + }else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ + if(i < 2){ + m_aWheelSkidmarkType[0] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[0] = true; + }else{ + m_aWheelSkidmarkType[1] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[1] = true; + } + } + }else{ + contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); + } + } + + // Get speed at contact points + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); +#ifndef FIX_BUGS + // this shouldn't be reset because we still need it below + m_aGroundPhysical[i] = nil; +#endif + } + } + + CVector normal; + if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f){ + normal = m_aSuspensionSpringRatio[0] < 1.0f ? m_aWheelColPoints[0].normal : m_aWheelColPoints[1].normal; + if(normal.z > 0.35f) + springDirections[0] = -normal; + normal = m_aSuspensionSpringRatio[1] < 1.0f ? m_aWheelColPoints[1].normal : m_aWheelColPoints[0].normal; + if(normal.z > 0.35f) + springDirections[1] = -normal; + } + if(m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f){ + normal = m_aSuspensionSpringRatio[2] < 1.0f ? m_aWheelColPoints[2].normal : m_aWheelColPoints[3].normal; + if(normal.z > 0.35f) + springDirections[2] = -normal; + normal = m_aSuspensionSpringRatio[3] < 1.0f ? m_aWheelColPoints[3].normal : m_aWheelColPoints[2].normal; + if(normal.z > 0.35f) + springDirections[3] = -normal; + } + + // game has dead code here if m_vecMoveSpeed.Magnitude() < 0.01f + + // dampen springs + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 1.0f) + ApplySpringDampening(pHandling->fSuspensionDampingLevel, + springDirections[i], contactPoints[i], contactSpeeds[i]); + + // Get speed at contact points again + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); + m_aGroundPhysical[i] = nil; + } + } + + bool gripCheat = true; + fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(!CVehicle::bCheat3) + gripCheat = false; + float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); + acceleration /= m_fForceMultiplier; + + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); + 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; + float tractionBiasRear = neutralHandling ? 1.0f : 2.0f-tractionBiasFront; + + // Count how many wheels are touching the ground + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 0; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f) + m_aWheelTimer[i] = 4.0f; + else + m_aWheelTimer[i] = Max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); + + if(m_aWheelTimer[i] > 0.0f){ + m_nWheelsOnGround++; + if(i == BIKESUSP_R1 || i == BIKESUSP_R2) + m_nDriveWheelsOnGround = 1; + if(m_nWheelsOnGround == 1) + m_vecAvgSurfaceNormal = m_aWheelColPoints[i].normal; + else + m_vecAvgSurfaceNormal += m_aWheelColPoints[i].normal; + } + } + + if(m_nWheelsOnGround == 0) + m_vecAvgSurfaceNormal = CVector(0.0f, 0.0f, 1.0f); + else{ + m_vecAvgSurfaceNormal /= m_nWheelsOnGround; + if(DotProduct(m_vecAvgSurfaceNormal, GetUp()) < -0.5f) + m_vecAvgSurfaceNormal *= -1.0f; + } + + // Find contact points for wheel processing + int frontLine = m_aSuspensionSpringRatio[BIKESUSP_F1] < m_aSuspensionSpringRatio[BIKESUSP_F2] ? + BIKESUSP_F1 : BIKESUSP_F2; + CVector frontContact(0.0f, + colModel->lines[BIKESUSP_F1].p0.y, + colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[BIKESUSP_F1] - 0.5f*wheelScale); + frontContact = Multiply3x3(GetMatrix(), frontContact); + + int rearLine = m_aSuspensionSpringRatio[BIKESUSP_R1] < m_aSuspensionSpringRatio[BIKESUSP_R2] ? + BIKESUSP_R1 : BIKESUSP_R2; + CVector rearContact(0.0f, + colModel->lines[BIKESUSP_R1].p0.y, + colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[BIKESUSP_R1] - 0.5f*wheelScale); + rearContact = Multiply3x3(GetMatrix(), rearContact); + + float traction = 0.004f * m_fTraction; + traction *= pHandling->fTractionMultiplier / 4.0f; + + // Turn wheel + if(GetStatus() == STATUS_PLAYER || !bIsStanding || m_bike_flag08){ + if(Abs(m_vecMoveSpeed.x) < 0.01f && Abs(m_vecMoveSpeed.y) < 0.01f && m_fSteerAngle == 0.0f){ + m_fWheelAngle *= Pow(0.96f, CTimer::GetTimeStep()); + }else{ + float f; + if(fwdSpeed > 0.01f && m_aWheelTimer[BIKESUSP_F1] > 0.0f && m_aWheelTimer[BIKESUSP_F2] > 0.0f && GetStatus() == STATUS_PLAYER){ + CColPoint point; + point.surfaceA = SURFACE_WHEELBASE; + point.surfaceB = SURFACE_TARMAC; + float steer = CSurfaceTable::GetAdhesiveLimit(point)*4.0f*pBikeHandling->fSpeedSteer*traction; + if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_LOOSE || + CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_SAND) + steer *= pBikeHandling->fSlipSteer; + f = Asin(Min(steer/SQR(fwdSpeed), 1.0))/DEGTORAD(pHandling->fSteeringLock); + if(m_fSteerAngle < 0.0f && m_fLeanLRAngle < 0.0f && + m_fSteerAngle > 0.0f && m_fLeanLRAngle > 0.0f) + f *= 2.0f; + f = Min(f, 1.0f); + }else{ + f = 1.0f; + } + if(GetStatus() != STATUS_PLAYER) + f = 1.0f; + m_fWheelAngle = m_fSteerAngle*f; + } + }else if(m_fWheelAngle < DEGTORAD(20.0f)) + m_fWheelAngle += DEGTORAD(1.5f)*CTimer::GetTimeStep(); + + static float fThrust; + static tWheelState WheelState[2]; + CVector initialMoveSpeed = m_vecMoveSpeed; + bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); + + // Process front wheel - first try + + if(!rearWheelsFirst){ + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + // Wheel on ground + eBikeWheelSpecial spec; + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) + spec = BIKE_WHEELSPEC_0; + else + spec = BIKE_WHEELSPEC_2; + CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f)); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; + wheelFwd.Normalise(); + CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); + wheelRight.Normalise(); + + fThrust = 0.0f; + m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; + CVector contactSpeed = GetSpeed(frontContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, frontContact, + 2, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, adhesionDestab, + BIKEWHEEL_FRONT, + &m_aWheelSpeed[BIKEWHEEL_FRONT], + &WheelState[BIKEWHEEL_FRONT], + spec, + m_wheelStatus[BIKEWHEEL_FRONT]); + if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + } + + // Process rear wheel + + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + // Wheel on ground + float rearBrake = brake; + float rearTraction = traction; + + CVector wheelFwd = GetForward(); + CVector wheelRight = GetRight(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[rearLine].normal)*m_aWheelColPoints[rearLine].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[rearLine].normal); + wheelRight.Normalise(); + + if(bIsHandbrakeOn){ +#ifdef FIX_BUGS + // Not sure if this is needed, but brake usually has timestep as a factor + rearBrake = 20000.0f * CTimer::GetTimeStepFix(); +#else + rearBrake = 20000.0f; +#endif + m_fTireTemperature = 1.0f; + }else if(m_doingBurnout){ + rearBrake = 0.0f; + rearTraction = 0.0f; + ApplyTurnForce(contactPoints[BIKESUSP_R1], -0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); + }else if(m_fTireTemperature < 1.0f && m_fGasPedal > 0.75f){ + rearTraction *= m_fTireTemperature; + ApplyTurnForce(contactPoints[BIKESUSP_R1], (1.0f-m_fTireTemperature)*-0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); + } + + if(fThrust > 0.0f && brake > 0.0f) + brake = 0.0f; // only affects next front wheel. is this intended? + fThrust = acceleration; + m_aWheelColPoints[rearLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[rearLine])*rearTraction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[rearLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_REAR] = m_aWheelState[BIKEWHEEL_REAR]; + CVector contactSpeed = GetSpeed(rearContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, rearContact, + 2, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear, adhesionDestab, + BIKEWHEEL_REAR, + &m_aWheelSpeed[BIKEWHEEL_REAR], + &WheelState[BIKEWHEEL_REAR], + BIKE_WHEELSPEC_1, + m_wheelStatus[BIKEWHEEL_REAR]); + if(bStuckInSand && (WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + if(bIsHandbrakeOn) + m_aWheelSpeed[BIKEWHEEL_REAR] = 0.0f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[BIKEWHEEL_REAR] < 2.0f) + m_aWheelSpeed[BIKEWHEEL_REAR] -= 0.2f; + }else{ + if(m_aWheelSpeed[BIKEWHEEL_REAR] > -2.0f) + m_aWheelSpeed[BIKEWHEEL_REAR] += 0.1f; + } + } + m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; + } + + if(m_doingBurnout && m_aWheelState[BIKEWHEEL_REAR] == 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 front wheel - second try + + if(rearWheelsFirst){ + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + // Wheel on ground + eBikeWheelSpecial spec; + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) + spec = BIKE_WHEELSPEC_0; + else + spec = BIKE_WHEELSPEC_2; + CVector wheelFwd = GetMatrix() * CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; + wheelFwd.Normalise(); + CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); + wheelRight.Normalise(); + + fThrust = 0.0f; + m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; + CVector contactSpeed = GetSpeed(frontContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, frontContact, + 2, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, adhesionDestab, + BIKEWHEEL_FRONT, + &m_aWheelSpeed[BIKEWHEEL_FRONT], + &WheelState[BIKEWHEEL_FRONT], + spec, + m_wheelStatus[BIKEWHEEL_FRONT]); + if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + } + + // Process leaning + float idleAngle = 0.0f; + if(pDriver){ + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_STILL); + if(assoc) + idleAngle = DEGTORAD(10.0f) * assoc->blendAmount; + } + if(bCanStand || m_bike_flag08){ + m_vecAvgSurfaceRight = CrossProduct(GetForward(), m_vecAvgSurfaceNormal); + m_vecAvgSurfaceRight.Normalise(); + float lean; + if(m_nWheelsOnGround == 0) + lean = -m_fSteerAngle/DEGTORAD(pHandling->fSteeringLock)*0.5f*GRAVITY*CTimer::GetTimeStep(); + else + lean = DotProduct(m_vecMoveSpeed-initialMoveSpeed, m_vecAvgSurfaceRight); + lean /= GRAVITY*Max(CTimer::GetTimeStep(), 0.01f); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + lean = clamp(lean, -0.4f*pBikeHandling->fMaxLean, 0.4f*pBikeHandling->fMaxLean); + else + lean = clamp(lean, -pBikeHandling->fMaxLean, pBikeHandling->fMaxLean); + float f = Pow(pBikeHandling->fDesLean, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = (Asin(lean) - idleAngle)*(1.0f-f) + m_fLeanLRAngle2*f; + }else{ + if(bIsStanding){ + float f = Pow(0.97f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(GetRight().z) + DEGTORAD(15.0f) + idleAngle)*(1.0f-f); + }else{ + float f = Pow(0.95f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f; + } + } + m_fLeanLRAngle = m_fLeanLRAngle2; + + // Destabilize steering when braking + if((m_aSuspensionSpringRatio[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_F2] < 1.0f) && + m_fBrakePedal - m_fGasPedal > 0.9f && + fwdSpeed > 0.02f && + !bIsHandbrakeOn){ + m_fBrakeDestabilization += CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*0.2f*CTimer::GetTimeStep(); + if(m_aSuspensionSpringRatio[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_R2] < 1.0f){ + // BUG: this clamp makes no sense and the arguments seem swapped too + ApplyTurnForce(contactPoints[BIKESUSP_R1], + m_fTurnMass*Sin(m_fBrakeDestabilization)*clamp(fwdSpeed, 0.5f, 0.2f)*0.013f*GetRight()*CTimer::GetTimeStep()); + }else{ + // BUG: this clamp makes no sense and the arguments seem swapped too + ApplyTurnForce(contactPoints[BIKESUSP_R1], + m_fTurnMass*Sin(m_fBrakeDestabilization)*clamp(fwdSpeed, 0.5f, 0.2f)*0.003f*GetRight()*CTimer::GetTimeStep()); + } + }else + m_fBrakeDestabilization = 0.0f; + + // Update wheel positions from suspension + float frontWheelPos = colModel->lines[frontLine].p0.z; + if(m_aSuspensionSpringRatio[frontLine] > 0.0f) + frontWheelPos -= m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[frontLine]; + m_aWheelPosition[BIKEWHEEL_FRONT] += (frontWheelPos - m_aWheelPosition[BIKEWHEEL_FRONT])*0.75f; + + float rearWheelPos = colModel->lines[rearLine].p0.z; + if(m_aSuspensionSpringRatio[rearLine] > 0.0f) + rearWheelPos -= m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[rearLine]; + m_aWheelPosition[BIKEWHEEL_REAR] += (rearWheelPos - m_aWheelPosition[BIKEWHEEL_REAR])*0.75f; + + for(i = 0; i < 2; i++) + m_aWheelState[i] = WheelState[i]; + // never spin when moving backwards + if(m_fGasPedal < 0.0f && m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING) + m_aWheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; + + // Process horn + + if(GetStatus() != STATUS_PLAYER){ +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + ReduceHornCounter(); + }else{ +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + { + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } + } + } + + if(m_fHealth < 250.0f && GetStatus() != STATUS_WRECKED){ + // Car is on fire + + CVector damagePos, fireDir; + + // move fire forward if in first person + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()){ + damagePos = CVector(0.0f, 1.2f, -0.4f); + fireDir = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)); + }else{ + damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_BACKSEAT]; + damagePos.z -= 0.3f; + fireDir = CGeneral::GetRandomNumberInRange(0.02025f, 0.09f) * GetRight(); + fireDir -= CGeneral::GetRandomNumberInRange(0.02025f, 0.18f) * GetForward(); + fireDir.z = CGeneral::GetRandomNumberInRange(0.00225f, 0.018f); + } + + damagePos = GetMatrix()*damagePos; + CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, fireDir, + nil, 0.9f); + + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + damagePos.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + damagePos.y += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + damagePos.z += CGeneral::GetRandomNumberInRange(0.5625f, 2.25f); + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, damagePos, CVector(0.0f, 0.0f, 0.0f)); + + // Blow up car after 5 seconds + m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); + if(m_fFireBlowUpTimer > 5000.0f) + BlowUpCar(m_pSetOnFireEntity); + }else + m_fFireBlowUpTimer = 0.0f; + + ProcessDelayedExplosion(); + + // Find out how much to shake the pad depending on suspension and ground surface + + 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 && (i == BIKESUSP_F1 || i == BIKESUSP_R1) && speedsq > 0.04f){ + if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS){ + if(m_wheelStatus[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; + } + } + + if(this == FindPlayerVehicle()){ + uint8 surf = m_aWheelColPoints[i].surfaceB; + 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 || surf == SURFACE_SAND_BEACH){ + if(surfShake < 0.1f) + surfShake = 0.2f; + }else if(surf == SURFACE_GRASS){ + if(surfShake < 0.05f) + surfShake = 0.1f; + } + +// 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){ + float speed = m_vecMoveSpeed.MagnitudeSqr(); + if(speed > sq(0.1f)){ + speed = Sqrt(speed); + if(suspShake > 0.0f){ + uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); + }else{ + uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 150.0f); + CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); + } + } + } + + bVehicleColProcessed = false; + bAudioChangingGear = false; + + if(!bWarnedPeds) + CCarCtrl::ScanForPedDanger(this); + + if(bInfiniteMass){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + }else if(!skipPhysics && + (acceleration == 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){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed.z = 0.0f; + } + } + + // Balance bike + if(bCanStand || m_bike_flag08 || bIsStanding){ + float onSideness = clamp(DotProduct(GetRight(), m_vecAvgSurfaceNormal), -1.0f, 1.0f); + CVector worldCOM = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + // Keep bike upright + if(bCanStand){ + ApplyTurnForce(-0.07f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); + bIsStanding = false; + }else + ApplyTurnForce(-0.1f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); + + // Wheelie/Stoppie stabilization + if(GetStatus() == STATUS_PLAYER){ + if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f && GetForward().z > 0.0 && + !(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f)){ + // Wheelie + float wheelie = pBikeHandling->fWheelieAng - GetForward().z; + if(wheelie > 0.15f) + // below wheelie angle + wheelie = Max(0.3f - wheelie, 0.0f); + else if(wheelie < -0.08f) + // above wheelie angle + wheelie = Min(-0.15f - wheelie, 0.0f); + float wheelieStab = pBikeHandling->fWheelieStabMult * Min(m_vecMoveSpeed.Magnitude(), 0.1f) * wheelie; + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*wheelieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); + }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f && GetForward().z < 0.0 && + !(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f)){ + // Stoppie + float stoppie = pBikeHandling->fStoppieAng - GetForward().z; + if(stoppie > 0.15f) + // below stoppie angle + stoppie = Max(0.3f - stoppie, 0.0f); + else if(stoppie < -0.15f) + // above stoppie angle + stoppie = Min(-0.3f - stoppie, 0.0f); + float speed = m_vecMoveSpeed.Magnitude(); + float stoppieStab = pBikeHandling->fStoppieStabMult * Min(speed, 0.1f) * stoppie; + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*stoppieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); + ApplyTurnForce(0.5f*Min(5.0f*speed,1.0f)*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); + } + } + } +} + +void +CBike::Teleport(CVector pos) +{ + CWorld::Remove(this); + + SetPosition(pos); + SetOrientation(0.0f, 0.0f, 0.0f); + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + + ResetSuspension(); + + CWorld::Add(this); +} + +void +CBike::PreRender(void) +{ +// TODO: particles and lights and such + + CMatrix mat; + CVector pos; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *colModel = mi->GetColModel(); + + // Wheel rotation + CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); + CVector rearWheelFwd = GetForward(); + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_F1], m_aSuspensionSpringRatioPrev[BIKESUSP_F2]); + CVector contactPoint(0.0f, + (colModel->lines[BIKESUSP_F1].p0.y - colModel->lines[BIKESUSP_F2].p0.y)/2.0f, + colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringLength[BIKESUSP_F1]*springRatio - 0.5f*mi->m_wheelScale); + CVector contactSpeed = GetSpeed(contactPoint); + // Why is wheel state always normal? + m_aWheelSpeed[BIKEWHEEL_FRONT] = ProcessWheelRotation(WHEEL_STATE_NORMAL, frontWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_R1], m_aSuspensionSpringRatioPrev[BIKESUSP_R2]); + CVector contactPoint(0.0f, + (colModel->lines[BIKESUSP_R1].p0.y - colModel->lines[BIKESUSP_R2].p0.y)/2.0f, + colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringLength[BIKESUSP_R1]*springRatio - 0.5f*mi->m_wheelScale); + CVector contactSpeed = GetSpeed(contactPoint); + m_aWheelSpeed[BIKEWHEEL_REAR] = ProcessWheelRotation(m_aWheelState[BIKEWHEEL_REAR], rearWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); + m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; + } + + // Front fork + if(m_aBikeNodes[BIKE_FORKS_FRONT]){ + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_FRONT])); + pos = mat.GetPosition(); + + RwMatrix rwrot; + // TODO: this looks like some weird ctor we don't have + CMatrix rot; + rot.m_attachment = &rwrot; + rot.SetUnity(); + rot.UpdateRW(); + + // Make rotation matrix with front fork as axis + CVector forkAxis(0.0f, Sin(DEGTORAD(mi->m_bikeSteerAngle)), -Cos(DEGTORAD(mi->m_bikeSteerAngle))); + forkAxis.Normalise(); // as if that's not already the case + CQuaternion quat; + quat.Set((RwV3d*)&forkAxis, -m_fWheelAngle); + quat.Get(rot.m_attachment); + rot.Update(); + + // Transform fork + mat.SetUnity(); + mat = mat * rot; + mat.Translate(pos); + mat.UpdateRW(); + + if(m_aBikeNodes[BIKE_HANDLEBARS]){ + // Transform handle + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_HANDLEBARS])); + pos = mat.GetPosition(); + if(GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED){ + mat.SetUnity(); + mat = mat * rot; + mat.Translate(pos); + }else + mat.SetTranslate(mat.GetPosition()); + mat.UpdateRW(); + } + } + + // Rear fork + if(m_aBikeNodes[BIKE_FORKS_REAR]){ + float sine = (m_aWheelPosition[BIKEWHEEL_REAR] - m_aWheelBasePosition[BIKEWHEEL_REAR])/m_fRearForkLength; + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_REAR])); + pos = mat.GetPosition(); + mat.SetRotate(-Asin(sine), 0.0f, 0.0f); + mat.Translate(pos); + mat.UpdateRW(); + } + + // Front wheel + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_FRONT])); + pos.x = mat.GetPosition().x; + pos.z = m_aWheelPosition[BIKEWHEEL_FRONT] - m_fFrontForkZ; + float y = (colModel->lines[BIKESUSP_F1].p0.y+colModel->lines[BIKESUSP_F2].p0.y)/2.0f - m_fFrontForkY; + pos.y = y - (m_aWheelPosition[BIKEWHEEL_FRONT] - m_aWheelBasePosition[BIKEWHEEL_FRONT])*m_fFrontForkSlope; + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[BIKEWHEEL_FRONT], 0.0f, 0.05f*Sin(m_aWheelRotation[BIKEWHEEL_FRONT])); + else + mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_FRONT]); + mat.Translate(pos); + mat.UpdateRW(); + // and mudguard + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_MUDGUARD])); + mat.SetTranslateOnly(pos); + mat.UpdateRW(); + + // Rear wheel + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_REAR])); + pos = mat.GetPosition(); + if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[BIKEWHEEL_REAR], 0.0f, 0.07f*Sin(m_aWheelRotation[BIKEWHEEL_REAR])); + else + mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_REAR]); + mat.Translate(pos); + mat.UpdateRW(); + + // Chassis + if(m_aBikeNodes[BIKE_CHASSIS]){ + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_CHASSIS])); + pos = mat.GetPosition(); + pos.z = (1.0f - Cos(m_fLeanLRAngle)) * (0.9*colModel->boundingBox.min.z); + mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); + mat.RotateY(m_fLeanLRAngle); + mat.Translate(pos); + mat.UpdateRW(); + } + +// TODO: exhaust +} + +void +CBike::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + CEntity::Render(); +} + +int32 +CBike::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) +{ + int i; + CColModel *colModel; + + if(GetStatus() != STATUS_SIMPLE) + bVehicleColProcessed = true; + + colModel = GetColModel(); + + int numWheelCollisions = 0; + float prevRatios[4] = { 0.0f, 0.0f, 0.0f, 0.0f}; + 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, + m_aWheelColPoints, m_aSuspensionSpringRatio); + + // 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(colModel->numLines){ + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aSuspensionSpringRatio[i] < prevRatios[i]){ + numWheelCollisions++; + + // wheel is touching a physical + if(ent->IsVehicle() || ent->IsObject()){ + CPhysical *phys = (CPhysical*)ent; + + m_aGroundPhysical[i] = phys; + phys->RegisterReference((CEntity**)&m_aGroundPhysical[i]); + m_aGroundOffset[i] = m_aWheelColPoints[i].point - phys->GetPosition(); + } + + m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; + if(ent->IsBuilding()) + m_pCurGroundEntity = ent; + } + } + }else + colModel->numLines = 4; + + if(numCollisions > 0 || numWheelCollisions > 0){ + AddCollisionRecord(ent); + if(!ent->IsBuilding()) + ((CPhysical*)ent)->AddCollisionRecord(this); + + if(numCollisions > 0) + if(ent->IsBuilding() || + ent->IsObject() && ((CPhysical*)ent)->bInfiniteMass) + bHasHitWall = true; + } + + return numCollisions; +} + +static int16 nLastControlInput; +static float fMouseCentreRange = 0.35f; +static float fMouseSteerSens = -0.0035f; +static float fMouseCentreMult = 0.975f; + +void +CBike::ProcessControlInputs(uint8 pad) +{ + float speed = DotProduct(m_vecMoveSpeed, GetForward()); + + if(CPad::GetPad(pad)->GetExitVehicle()) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + + // Steer left/right +#ifdef FIX_BUGS + if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ + if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ + m_fSteerInput += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); + nLastControlInput = 2; + if(Abs(m_fSteerInput) < fMouseCentreRange) + m_fSteerInput *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); + }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ + // mouse hasn't move, steer with pad like below + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + }else +#endif + { + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + m_fSteerInput = clamp(m_fSteerInput, -1.0f, 1.0f); + + // Lean forward/backward + float updown = -CPad::GetPad(pad)->GetSteeringUpDown()/128.0f + CPad::GetPad(pad)->GetCarGunUpDown()/128.0f; + m_fLeanInput += (updown - m_fLeanInput)*0.2f*CTimer::GetTimeStep(); + m_fLeanInput = clamp(m_fLeanInput, -1.0f, 1.0f); + + // Accelerate/Brake + float acceleration = (CPad::GetPad(pad)->GetAccelerate() - CPad::GetPad(pad)->GetBrake())/255.0f; + if(GetModelIndex() == MI_DODO && acceleration < 0.0f) + acceleration *= 0.3f; + if(Abs(speed) < 0.01f){ + // standing still, go into direction we want + 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 + if(speed * acceleration < 0.0f){ + // if opposite directions, have to brake first + m_fGasPedal = 0.0f; + m_fBrakePedal = Abs(acceleration); + }else{ + // accelerating in same direction we were already going + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } +#else + if(speed < 0.0f){ + // moving backwards currently + if(acceleration < 0.0f){ + // still go backwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ + // want to go forwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = acceleration; + } + }else{ + // moving forwards currently + if(acceleration < 0.0f){ + // want to go backwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = -acceleration; + }else{ + // still go forwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + } +#endif + } + + // Actually turn wheels + static float fValue; // why static? + if(m_fSteerInput < 0.0f) + fValue = -sq(m_fSteerInput); + else + fValue = sq(m_fSteerInput); + m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; + + if(bComedyControls){ + 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; + } + + // Brake if player isn't in control + // BUG: game always uses pad 0 here + if(CPad::GetPad(pad)->ArePlayerControlsDisabled()){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + m_fGasPedal = 0.0f; + + FindPlayerPed()->KeepAreaAroundPlayerClear(); + + // slow down car immediately + speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.28f) + m_vecMoveSpeed *= 0.28f/speed; + } +} + +void +CBike::ProcessBuoyancy(void) +{ + // TODO +} + +void +CBike::DoDriveByShootings(void) +{ + // TODO +} + +void +CBike::VehicleDamage(void) +{ + // TODO +} + +void +CBike::GetComponentWorldPosition(int32 component, CVector &pos) +{ + if(m_aBikeNodes[component] == nil){ + printf("BikeNode missing: %d %d\n", GetModelIndex(), component); + return; + } + RwMatrix *ltm = RwFrameGetLTM(m_aBikeNodes[component]); + pos = *RwMatrixGetPos(ltm); +} + +bool +CBike::IsComponentPresent(int32 component) +{ + return m_aBikeNodes[component] != nil; +} + +void +CBike::SetComponentRotation(int32 component, CVector rotation) +{ + CMatrix mat(RwFrameGetMatrix(m_aBikeNodes[component])); + CVector pos = mat.GetPosition(); + // BUG: all these set the whole matrix + mat.SetRotateX(DEGTORAD(rotation.x)); + mat.SetRotateY(DEGTORAD(rotation.y)); + mat.SetRotateZ(DEGTORAD(rotation.z)); + mat.Translate(pos); + mat.UpdateRW(); +} + +bool +CBike::IsDoorReady(eDoors door) +{ + return true; +} + +bool +CBike::IsDoorFullyOpen(eDoors door) +{ + return false; +} + +bool +CBike::IsDoorClosed(eDoors door) +{ + return false; +} + +bool +CBike::IsDoorMissing(eDoors door) +{ + return true; +} + +void +CBike::RemoveRefsToVehicle(CEntity *ent) +{ + int i; + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i] == ent) + m_aGroundPhysical[i] = nil; +} + +void +CBike::BlowUpCar(CEntity *culprit) +{ + if(!bCanBeDamaged) + return; + +// TODO: property damage stuff in FIX_BUGS + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + SetStatus(STATUS_WRECKED); + bRenderScorched = true; + + m_fHealth = 0.0f; + m_nBombTimer = 0; + + TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); + + KillPedsInVehicle(); + + bEngineOn = false; + bLightsOn = false; + ChangeLawEnforcerState(false); + + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); + CDarkel::RegisterCarBlownUpByPlayer(this); +} + +bool +CBike::SetUpWheelColModel(CColModel *colModel) +{ + // TODO, but unused + return true; +} + +void +CBike::BurstTyre(uint8 wheel, bool applyForces) +{ + if(bTyresDontBurst) + return; + + switch(wheel){ + case CAR_PIECE_WHEEL_LF: wheel = BIKEWHEEL_FRONT; break; + case CAR_PIECE_WHEEL_LR: wheel = BIKEWHEEL_REAR; break; + } + + if(m_wheelStatus[wheel] == WHEEL_STATUS_OK){ + m_wheelStatus[wheel] = WHEEL_STATUS_BURST; +#ifdef FIX_BUGS + CStats::TyresPopped++; +#endif +// TODO(MIAMI) +// DMAudio.PlayOneShot(m_audioEntityId, SOUND_15, 0.0f); + + if(GetStatus() == STATUS_SIMPLE){ + SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(this); + } + + if(applyForces){ + ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f)); + ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), GetForward()); + } +// TODO: knock off driver + } +} + +bool +CBike::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) +{ + CColPoint colpoint; + CEntity *ent; + colpoint.point = CVector(0.0f, 0.0f, 0.0f); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector seatPos = mi->GetFrontSeatPosn(); + if(component == CAR_DOOR_RR || component == CAR_DOOR_LR) + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + if(component == CAR_DOOR_LF || component == CAR_DOOR_LR) + seatPos.x = -seatPos.x; + seatPos = GetMatrix() * seatPos; + + CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component); + if(doorOffset){ + CVector off = *doorOffset; + if(component == CAR_DOOR_RF || component == CAR_DOOR_RR) + off.x = -off.x; + doorPos += Multiply3x3(GetMatrix(), off); + } + + if(GetUp().z < 0.0f){ + seatPos.z += 0.5f; + doorPos.z += 0.5f; + } + + CVector dist = doorPos - seatPos; + + // 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); + + if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false)) + return false; + if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false)) + return false; + if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f) + return false; + float upperZ = colpoint.point.z; + if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + return false; + if(upperZ != 0.0f && upperZ < colpoint.point.z) + return false; + return true; +} + +float +CBike::GetHeightAboveRoad(void) +{ + return m_fHeightAboveRoad; +} + +void +CBike::PlayCarHorn(void) +{ + int r; + + if (IsAlarmOn() || m_nCarHornTimer != 0) + return; + + if (m_nCarHornDelay) { + m_nCarHornDelay--; + return; + } + + m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; + r = m_nCarHornDelay & 7; + if(r < 2){ + m_nCarHornTimer = 45; + }else if(r < 4){ + if(pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + m_nCarHornTimer = 45; + }else{ + if(pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + } +} + +void +CBike::PlayHornIfNecessary(void) +{ + if(AutoPilot.m_bSlowedDownBecauseOfPeds || + AutoPilot.m_bSlowedDownBecauseOfCars) + PlayCarHorn(); +} + +void +CBike::ResetSuspension(void) +{ + int i; + for(i = 0; i < 2; i++){ + m_aWheelRotation[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + } + for(i = 0; i < 4; i++){ + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + } +} + +// TODO: maybe put this somewhere else +inline void +GetRelativeMatrix(RwMatrix *mat, RwFrame *frm, RwFrame *end) +{ + *mat = *RwFrameGetMatrix(frm); + frm = RwFrameGetParent(frm); + while(frm){ + RwMatrixTransform(mat, RwFrameGetMatrix(frm), rwCOMBINEPOSTCONCAT); + frm = RwFrameGetParent(frm); + if(frm == end) + frm = nil; + } +} + +void +CBike::SetupSuspensionLines(void) +{ + int i; + CVector posn; + float suspOffset = 0.0f; + RwFrame *node = nil; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *colModel = mi->GetColModel(); + RwMatrix *mat = RwMatrixCreate(); + + bool initialized = colModel->lines[0].p0.z != FAKESUSPENSION; + + for(i = 0; i < 4; i++){ + if(initialized){ + posn = colModel->lines[i].p0; + if(i < 2) + posn.z = m_aWheelBasePosition[0]; + else + posn.z = m_aWheelBasePosition[1]; + }else{ + switch(i){ + case BIKESUSP_F1: + node = m_aBikeNodes[BIKE_WHEEL_FRONT]; + suspOffset = 0.25f*mi->m_wheelScale; + break; + case BIKESUSP_F2: + node = m_aBikeNodes[BIKE_WHEEL_FRONT]; + suspOffset = -0.25f*mi->m_wheelScale; + break; + case BIKESUSP_R1: + node = m_aBikeNodes[BIKE_WHEEL_REAR]; + suspOffset = 0.25f*mi->m_wheelScale; + break; + case BIKESUSP_R2: + node = m_aBikeNodes[BIKE_WHEEL_REAR]; + suspOffset = -0.25f*mi->m_wheelScale; + break; + } + + GetRelativeMatrix(mat, node, node); + posn = *RwMatrixGetPos(mat); + if(i == BIKESUSP_F1) + m_aWheelBasePosition[BIKEWHEEL_FRONT] = posn.z; + else if(i == BIKESUSP_R1){ + m_aWheelBasePosition[BIKEWHEEL_REAR] = posn.z; + + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_REAR], m_aBikeNodes[BIKE_FORKS_REAR]); + float dz = posn.z - RwMatrixGetPos(mat)->z; + float dy = posn.y - RwMatrixGetPos(mat)->y; + m_fRearForkLength = Sqrt(SQR(dy) + SQR(dz)); + assert(m_fRearForkLength != 0.0f); // we want to divide by this + } + posn.y += suspOffset; + } + + // uppermost wheel position + posn.z += pHandling->fSuspensionUpperLimit; + colModel->lines[i].p0 = posn; + + // lowermost wheel position + posn.z += pHandling->fSuspensionLowerLimit - pHandling->fSuspensionUpperLimit; + // lowest point on tyre + posn.z -= mi->m_wheelScale*0.5f; + colModel->lines[i].p1 = posn; + + // this is length of the spring at rest + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = colModel->lines[i].p0.z - colModel->lines[i].p1.z; + } + + if(!initialized){ + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_FRONT], m_aBikeNodes[BIKE_FORKS_FRONT]); + m_fFrontForkY = RwMatrixGetPos(mat)->y; + m_fFrontForkZ = RwMatrixGetPos(mat)->z; + } + + // Compress spring somewhat to get normal height on road + 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 < 2; i++) + m_aWheelPosition[i] = mi->m_wheelScale*0.5f - m_fHeightAboveRoad; + + // adjust col model to include suspension lines + if(colModel->boundingBox.min.z > colModel->lines[0].p1.z) + colModel->boundingBox.min.z = colModel->lines[0].p1.z; + float radius = Max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); + if(colModel->boundingSphere.radius < radius) + colModel->boundingSphere.radius = radius; + +#ifdef FIX_BUGS + RwMatrixDestroy(mat); +#endif +} + +void +CBike::CalculateLeanMatrix(void) +{ + if(bLeanMatrixClean) + return; + + CMatrix mat; + mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); + mat.RotateY(m_fLeanLRAngle); + m_leanMatrix = GetMatrix(); + m_leanMatrix = m_leanMatrix * mat; + // place wheel back on ground + m_leanMatrix.GetPosition() += GetUp()*(1.0f-Cos(m_fLeanLRAngle))*GetColModel()->boundingBox.min.z; + bLeanMatrixClean = true; +} + +void +CBike::GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2) +{ + CVector &fwd = GetForward(); + CVector rightWorld = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + CVector upWorld = CrossProduct(rightWorld, fwd); + CColModel *colModel = GetColModel(); + float onSide = DotProduct(GetUp(), rightWorld); + float diff = Max(colModel->boundingBox.max.z-colModel->boundingBox.max.x, 0.0f); + pos = CVector(0.0f, 0.0f, 0.0f); + float y = p2.y - p1.y; + float x = onSide*diff + p2.x + p1.x; + float z = p2.z - p1.z; + pos = x*rightWorld + y*fwd + z*upWorld + GetPosition(); +} + +void +CBike::Fix(void) +{ + bIsDamaged = false; + bIsOnFire = false; + m_wheelStatus[0] = WHEEL_STATUS_OK; + m_wheelStatus[1] = WHEEL_STATUS_OK; +} + +void +CBike::SetupModelNodes(void) +{ + int i; + for(i = 0; i < BIKE_NUM_NODES; i++) + m_aBikeNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aBikeNodes); +} + +void +CBike::ReduceHornCounter(void) +{ + if(m_nCarHornTimer != 0) + m_nCarHornTimer--; +} diff --git a/src/vehicles/Bike.h b/src/vehicles/Bike.h index 4e7e5a0e..02c63c1e 100644 --- a/src/vehicles/Bike.h +++ b/src/vehicles/Bike.h @@ -1,6 +1,7 @@ #pragma once -// some miami bike leftovers +#include "Vehicle.h" +#include "Skidmarks.h" enum eBikeNodes { BIKE_NODE_NONE, @@ -12,4 +13,120 @@ enum eBikeNodes { BIKE_MUDGUARD, BIKE_HANDLEBARS, BIKE_NUM_NODES -};
\ No newline at end of file +}; + +enum { + BIKEWHEEL_FRONT, + BIKEWHEEL_REAR, +}; + +enum { + BIKESUSP_F1, + BIKESUSP_F2, + BIKESUSP_R1, + BIKESUSP_R2, +}; + +class CBike : public CVehicle +{ +public: + RwFrame *m_aBikeNodes[BIKE_NUM_NODES]; + bool bLeanMatrixClean; + CMatrix m_leanMatrix; + CVector m_vecAvgSurfaceNormal; + CVector m_vecAvgSurfaceRight; + tBikeHandlingData *pBikeHandling; + AssocGroupId m_bikeAnimType; + uint8 m_wheelStatus[2]; + CColPoint m_aWheelColPoints[4]; + float m_aSuspensionSpringRatio[4]; + float m_aSuspensionSpringRatioPrev[4]; + float m_aWheelTimer[4]; + float m_bike_unused1; + eSkidmarkType m_aWheelSkidmarkType[2]; + bool m_aWheelSkidmarkBloody[2]; + bool m_aWheelSkidmarkUnk[2]; + float m_aWheelRotation[2]; + float m_aWheelSpeed[2]; + float m_aWheelPosition[2]; + float m_aWheelBasePosition[2]; + float m_aSuspensionSpringLength[4]; + float m_aSuspensionLineLength[4]; + float m_fHeightAboveRoad; + float m_fTraction; + float m_fRearForkLength; + float m_fFrontForkY; + float m_fFrontForkZ; + float m_fFrontForkSlope; + float m_fWheelAngle; + float m_fLeanLRAngle; + float m_fLeanLRAngle2; + float m_fLeanInput; + float m_fPedLeanAmountLR; + float m_fPedLeanAmountUD; + uint8 m_bike_unused2; + uint8 unused[3]; // looks like padding..but for what? + uint8 m_bike_flag01 : 1; + uint8 m_bike_flag02 : 1; + uint8 bWaterTight : 1; + uint8 m_bike_flag08 : 1; + uint8 bIsStanding : 1; + uint8 bExtraSpeed : 1; // leaning forward + uint8 bIsOnFire : 1; + uint8 m_bike_flag80 : 1; // doing wheelie? + int16 m_doingBurnout; + float m_fTireTemperature; + float m_fBrakeDestabilization; + uint32 field_490; + float m_fFireBlowUpTimer; + CPhysical *m_aGroundPhysical[4]; + CVector m_aGroundOffset[4]; + CEntity *m_pSetOnFireEntity; + uint8 m_nWheelsOnGround; + uint8 m_nDriveWheelsOnGround; + uint8 m_nDriveWheelsOnGroundPrev; + float m_fGasPedalAudio; + tWheelState m_aWheelState[2]; + + CBike(int32 id, uint8 CreatedBy); + + // from CEntity + void SetModelIndex(uint32 id); + void ProcessControl(void); + void Teleport(CVector v); + void PreRender(void); + void Render(void); + + // from CPhysical + int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); + + // from CVehicle + void ProcessControlInputs(uint8); + void GetComponentWorldPosition(int32 component, CVector &pos); + bool IsComponentPresent(int32 component); + void SetComponentRotation(int32 component, CVector rotation); + bool IsDoorReady(eDoors door); + bool IsDoorFullyOpen(eDoors door); + bool IsDoorClosed(eDoors door); + bool IsDoorMissing(eDoors door); + void RemoveRefsToVehicle(CEntity *ent); + void BlowUpCar(CEntity *ent); + bool SetUpWheelColModel(CColModel *colModel); + void BurstTyre(uint8 tyre, bool applyForces); + bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); + float GetHeightAboveRoad(void); + void PlayCarHorn(void); + + void VehicleDamage(void); + void ProcessBuoyancy(void); + void DoDriveByShootings(void); + void PlayHornIfNecessary(void); + void ResetSuspension(void); + void SetupSuspensionLines(void); + void CalculateLeanMatrix(void); + void GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2); + + void Fix(void); + void SetupModelNodes(void); + void ReduceHornCounter(void); +}; diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index 0ed7876a..99b6cbb8 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -3,6 +3,7 @@ #include "General.h" #include "Timecycle.h" #include "HandlingMgr.h" +#include "CarAI.h" #include "CarCtrl.h" #include "RwHelper.h" #include "ModelIndices.h" @@ -18,17 +19,20 @@ #include "Pools.h" #include "Pad.h" #include "Boat.h" +#include "AnimBlendAssociation.h" +#include "RpAnimBlend.h" +#include "Record.h" #define INVALID_ORIENTATION (-9999.99f) float fShapeLength = 0.4f; float fShapeTime = 0.05f; -float fRangeMult = 0.75f; //0.6f; // 0.75f gta 3 +float fRangeMult = 0.6f; float fTimeMult; -float MAX_WAKE_LENGTH = 50.0f; -float MIN_WAKE_INTERVAL = 1.0f; -float WAKE_LIFETIME = 400.0f; +float CBoat::MAX_WAKE_LENGTH = 50.0f; +float CBoat::MIN_WAKE_INTERVAL = 2.0f; +float CBoat::WAKE_LIFETIME = 150.0f; CBoat *CBoat::apFrameWakeGeneratingBoats[4]; @@ -96,7 +100,7 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) void CBoat::SetModelIndex(uint32 id) { - CEntity::SetModelIndex(id); + CVehicle::SetModelIndex(id); SetupModelNodes(); } @@ -109,9 +113,6 @@ CBoat::GetComponentWorldPosition(int32 component, CVector &pos) void CBoat::ProcessControl(void) { - if(m_nZoneLevel > LEVEL_NONE && m_nZoneLevel != CCollision::ms_collisionInMemory) - return; - bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f; PruneWakeTrail(); @@ -151,10 +152,14 @@ CBoat::ProcessControl(void) ProcessControlInputs(0); if(GetModelIndex() == MI_PREDATOR) DoFixedMachineGuns(); + + if (!CRecordDataForChase::IsRecording()) + DoDriveByShootings(); break; case STATUS_SIMPLE: m_bIsAnchored = false; m_fOrientation = INVALID_ORIENTATION; + CCarAI::UpdateCarAI(this); CPhysical::ProcessControl(); bBoatInWater = true; bPropellerInWater = true; @@ -163,7 +168,8 @@ CBoat::ProcessControl(void) case STATUS_PHYSICS: m_bIsAnchored = false; m_fOrientation = INVALID_ORIENTATION; - CCarCtrl::SteerAIBoatWithPhysics(this); + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); break; case STATUS_ABANDONED: case STATUS_WRECKED: @@ -267,9 +273,17 @@ CBoat::ProcessControl(void) if(0.1f * m_fMass * GRAVITY*CTimer::GetTimeStep() < buoyanceImpulse.z){ bBoatInWater = true; bIsInWater = true; + if (GetUp().z < -0.6f && Abs(GetMoveSpeed().x) < 0.05 && Abs(GetMoveSpeed().y) < 0.05) { + bIsDrowning = true; + if (pDriver) + pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + else + bIsDrowning = false; }else{ bBoatInWater = false; bIsInWater = false; + bIsDrowning = false; } m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater; @@ -295,8 +309,8 @@ CBoat::ProcessControl(void) AddWakePoint(GetPosition()); float steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward()); - if (GetModelIndex() == MI_GHOST) - steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward())*0.3f; +// if (GetModelIndex() == MI_GHOST) +// steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward())*0.3f; if(steerFactor < 0.0f) steerFactor = 0.0f; CVector propeller(0.0f, -pHandling->Dimension.y*m_fPropellerY, -pHandling->Dimension.z*m_fPropellerZ); @@ -368,8 +382,9 @@ CBoat::ProcessControl(void) CGeneral::GetRandomNumberInRange(0, 30), CGeneral::GetRandomNumberInRange(0, 90), 3); #endif - if(!cameraHack) - CParticle::AddParticle(PARTICLE_BOAT_WAKE, wakePos, wakeDir, nil, 0.0f, jetColor); + //TODO: MIAMI: + //if(!cameraHack) + // CParticle::AddParticle(PARTICLE_BOAT_WAKE, wakePos, wakeDir, nil, 0.0f, jetColor); }else if((CTimer::GetFrameCounter() + m_randomSeed) & 1){ #ifdef PC_PARTICLE jetDir.z = 0.018f; @@ -406,10 +421,10 @@ CBoat::ProcessControl(void) } // Slow down or push down boat as it approaches the world limits - m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - 1900.0f)*0.01f); // east - m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - -1515.0f)*0.01f); // west - m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - 600.0f)*0.01f); // north - m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - -1900.0f)*0.01f); // south + m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MAX_X-100.0f))*0.01f); // east + m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MIN_X+100.0f))*0.01f); // west + m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MAX_Y-100.0f))*0.01f); // north + m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MIN_Y+100.0f))*0.01f); // south if(!onLand && bBoatInWater) ApplyWaterResistance(); @@ -439,7 +454,7 @@ CBoat::ProcessControl(void) speedUp = pHandling->fBrakeDeceleration - m_vecMoveSpeed.z; if(speedUp < 0.0f) speedUp = 0.0f; float speedFwd = DotProduct(m_vecMoveSpeed, GetForward()); - speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fTractionLoss; + speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fBrakeBias; CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); CVector splashImpulse = speed * m_fMass; ApplyMoveForce(splashImpulse); @@ -513,6 +528,7 @@ CBoat::ProcessControl(void) }else{ bBoatInWater = false; bIsInWater = false; + bIsDrowning = false; } if(m_bIsAnchored){ @@ -912,6 +928,68 @@ CBoat::AddWakePoint(CVector point) } } +void +CBoat::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + + CWeapon *weapon = pDriver->GetWeapon(); + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) + return; + + weapon->Update(pDriver->m_audioEntityId, nil); + + bool lookingLeft = false; + bool lookingRight = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN){ + if(CPad::GetPad(0)->GetLookLeft()) + lookingLeft = true; + if(CPad::GetPad(0)->GetLookRight()) + lookingRight = true; + }else{ + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) + lookingLeft = true; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + lookingRight = true; + } + + if(lookingLeft || lookingRight){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_L); + }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_R); + } + + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim) + anim->blendDelta = -1000.0f; + } +} + #ifdef COMPATIBLE_SAVES void CBoat::Save(uint8*& buf) diff --git a/src/vehicles/Boat.h b/src/vehicles/Boat.h index 3cc3513d..c6f4b7ad 100644 --- a/src/vehicles/Boat.h +++ b/src/vehicles/Boat.h @@ -5,8 +5,12 @@ enum eBoatNodes { BOAT_MOVING = 1, + BOAT_WINDSCREEN, BOAT_RUDDER, - BOAT_WINDSCREEN + BOAT_FLAP_LEFT, + BOAT_FLAP_RIGHT, + BOAT_REARFLAP_LEFT, + BOAT_REARFLAP_RIGHT }; class CBoat : public CVehicle @@ -41,6 +45,10 @@ public: CVector2D m_avec2dWakePoints[32]; float m_afWakePointLifeTime[32]; + static float MAX_WAKE_LENGTH; + static float MIN_WAKE_INTERVAL; + static float WAKE_LIFETIME; + CBoat(int, uint8); virtual void SetModelIndex(uint32 id); @@ -57,6 +65,7 @@ public: void SetupModelNodes(); void PruneWakeTrail(void); void AddWakePoint(CVector point); + void DoDriveByShootings(void); static CBoat *apFrameWakeGeneratingBoats[4]; @@ -70,10 +79,4 @@ public: #endif static const uint32 nSaveStructSize; -}; - -VALIDATE_SIZE(CBoat, 0x484); - -extern float MAX_WAKE_LENGTH; -extern float MIN_WAKE_INTERVAL; -extern float WAKE_LIFETIME;
\ No newline at end of file +};
\ No newline at end of file diff --git a/src/vehicles/CarGen.cpp b/src/vehicles/CarGen.cpp index 72b6c30c..e6c3bbf3 100644 --- a/src/vehicles/CarGen.cpp +++ b/src/vehicles/CarGen.cpp @@ -3,6 +3,7 @@ #include "CarGen.h" #include "Automobile.h" +#include "Bike.h" #include "Boat.h" #include "Camera.h" #include "CarCtrl.h" @@ -12,7 +13,9 @@ #include "Streaming.h" #include "Timer.h" #include "Vehicle.h" +#include "VisibilityPlugins.h" #include "World.h" +#include "Zones.h" uint8 CTheCarGenerators::ProcessCounter; uint32 CTheCarGenerators::NumOfCarGenerators; @@ -38,21 +41,44 @@ uint32 CCarGenerator::CalcNextGen() return CTimer::GetTimeInMilliseconds() + 4; } +//TODO(MIAMI): check for more changes - so far only -1 mi is accounted for void CCarGenerator::DoInternalProcessing() { - if (CheckForBlockage()) { - m_nTimer += 4; - if (m_nUsesRemaining == 0) - --CTheCarGenerators::CurrentActiveCount; - return; - } + int mi; if (CCarCtrl::NumParkedCars >= 10) return; - CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); - if (!CStreaming::HasModelLoaded(m_nModelIndex)) + if (m_nModelIndex >= 0) { + if (CheckForBlockage(m_nModelIndex)) { + m_nTimer += 4; + return; + } + mi = m_nModelIndex; + } + else { + mi = -m_nModelIndex; + if (m_nModelIndex == -1 || !CStreaming::HasModelLoaded(mi)) { + CZoneInfo pZone; + CVector pos = FindPlayerCoors(); + CTheZones::GetZoneInfoForTimeOfDay(&pos, &pZone); + mi = CCarCtrl::ChooseCarModel(CCarCtrl::ChooseCarRating(&pZone)); + if (mi < 0) + return; + m_nModelIndex = -mi; + m_nColor1 = -1; + m_nColor2 = -1; + } + if (CheckForBlockage(mi)) { + m_nTimer += 4; + return; + } + } + CStreaming::RequestModel(mi, STREAMFLAGS_DEPENDENCY); + if (!CStreaming::HasModelLoaded(mi)) return; - if (CModelInfo::IsBoatModel(m_nModelIndex)){ - CBoat* pBoat = new CBoat(m_nModelIndex, PARKED_VEHICLE); + CVehicle* pVehicle; + if (CModelInfo::IsBoatModel(mi)){ + CBoat* pBoat = new CBoat(mi, PARKED_VEHICLE); + pVehicle = pBoat; pBoat->bIsStatic = false; pBoat->bEngineOn = false; CVector pos = m_vecPos; @@ -63,16 +89,6 @@ void CCarGenerator::DoInternalProcessing() pBoat->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); pBoat->SetStatus(STATUS_ABANDONED); pBoat->m_nDoorLock = CARLOCK_UNLOCKED; - CWorld::Add(pBoat); - if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) - pBoat->m_nAlarmState = -1; - if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) - pBoat->m_nDoorLock = CARLOCK_LOCKED; - if (m_nColor1 != -1 && m_nColor2){ - pBoat->m_currentColour1 = m_nColor1; - pBoat->m_currentColour2 = m_nColor2; - } - m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pBoat); }else{ bool groundFound = false; CVector pos = m_vecPos; @@ -88,28 +104,42 @@ void CCarGenerator::DoInternalProcessing() } if (!groundFound) { debug("CCarGenerator::DoInternalProcessing - can't find ground z for new car x = %f y = %f \n", m_vecPos.x, m_vecPos.y); - }else{ - CAutomobile* pCar = new CAutomobile(m_nModelIndex, PARKED_VEHICLE); - pCar->bIsStatic = false; - pCar->bEngineOn = false; - pos.z += pCar->GetDistanceFromCentreOfMassToBaseOfModel(); - pCar->SetPosition(pos); - pCar->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); - pCar->SetStatus(STATUS_ABANDONED); - pCar->bLightsOn = false; - pCar->m_nDoorLock = CARLOCK_UNLOCKED; - CWorld::Add(pCar); - if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) - pCar->m_nAlarmState = -1; - if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) - pCar->m_nDoorLock = CARLOCK_LOCKED; - if (m_nColor1 != -1 && m_nColor2) { - pCar->m_currentColour1 = m_nColor1; - pCar->m_currentColour2 = m_nColor2; - } - m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pCar); + return; + } + if (CModelInfo::IsBikeModel(mi)) { + CBike* pBike = new CBike(mi, PARKED_VEHICLE); + pBike->bIsStanding = true; + pVehicle = pBike; } + else { + CAutomobile* pCar = new CAutomobile(mi, PARKED_VEHICLE); + pVehicle = pCar; + } + pVehicle->bIsStatic = false; + pVehicle->bEngineOn = false; + pos.z += pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + pVehicle->SetPosition(pos); + pVehicle->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pVehicle->SetStatus(STATUS_ABANDONED); + pVehicle->bLightsOn = false; + pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + } + CWorld::Add(pVehicle); + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) + pVehicle->m_nAlarmState = -1; + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) + pVehicle->m_nDoorLock = CARLOCK_LOCKED; + if (m_nColor1 != -1 && m_nColor2 != -1) { + pVehicle->m_currentColour1 = m_nColor1; + pVehicle->m_currentColour2 = m_nColor2; + } + else if (m_nModelIndex < -1) { + m_nColor1 = pVehicle->m_currentColour1; + m_nColor2 = pVehicle->m_currentColour2; + } + CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); + m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pVehicle); if (m_nUsesRemaining < -1) /* I don't think this is a correct comparasion */ --m_nUsesRemaining; m_nTimer = CalcNextGen(); @@ -155,25 +185,33 @@ void CCarGenerator::Setup(float x, float y, float z, float angle, int32 mi, int1 m_nTimer = CTimer::GetTimeInMilliseconds() + 1; m_nUsesRemaining = 0; m_bIsBlocking = false; - m_vecInf = CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->boundingBox.min; - m_vecSup = CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->boundingBox.max; - m_fSize = Max(m_vecInf.Magnitude(), m_vecSup.Magnitude()); } -bool CCarGenerator::CheckForBlockage() +bool CCarGenerator::CheckForBlockage(int32 mi) { int16 entities; - CWorld::FindObjectsKindaColliding(CVector(m_vecPos), m_fSize, 1, &entities, 2, nil, false, true, true, false, false); - return entities > 0; + CEntity* pEntities[8]; + CColModel* pColModel = CModelInfo::GetModelInfo(mi)->GetColModel(); + CWorld::FindObjectsKindaColliding(CVector(m_vecPos), pColModel->boundingSphere.radius, 1, &entities, 8, pEntities, false, true, true, false, false); + for (int i = 0; i < entities; i++) { + if (m_vecPos.z + pColModel->boundingBox.min.z < pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.max.z + 1.0f && + m_vecPos.z + pColModel->boundingBox.max.z > pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.min.z - 1.0f) { + m_bIsBlocking = true; + return true; + } + } + return false; } bool CCarGenerator::CheckIfWithinRangeOfAnyPlayer() { CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos; float distance = direction.Magnitude(); - float farclip = 120.0f * TheCamera.GenerationDistMultiplier; + float farclip = 110.0f * TheCamera.GenerationDistMultiplier; float nearclip = farclip - 20.0f; - if (distance >= farclip){ + bool canBeRemoved = (m_nModelIndex > 0 && CModelInfo::IsBoatModel(m_nModelIndex) && 165.0f * TheCamera.GenerationDistMultiplier > distance && + TheCamera.IsPointVisible(m_vecPos, &TheCamera.GetCameraMatrix())); // TODO(MIAMI) COcclision::IsPositionOccluded(m_vecPos, 0.0f) + if (distance >= farclip || canBeRemoved){ if (m_bIsBlocking) m_bIsBlocking = false; return false; diff --git a/src/vehicles/CarGen.h b/src/vehicles/CarGen.h index 9d645318..684f93ee 100644 --- a/src/vehicles/CarGen.h +++ b/src/vehicles/CarGen.h @@ -22,9 +22,6 @@ class CCarGenerator int32 m_nVehicleHandle; uint16 m_nUsesRemaining; bool m_bIsBlocking; - CVector m_vecInf; - CVector m_vecSup; - float m_fSize; public: void SwitchOff(); void SwitchOn(); @@ -32,7 +29,7 @@ public: void DoInternalProcessing(); void Process(); void Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); - bool CheckForBlockage(); + bool CheckForBlockage(int32 mi); bool CheckIfWithinRangeOfAnyPlayer(); void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } }; diff --git a/src/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp index 757974a6..db7b514f 100644 --- a/src/vehicles/Cranes.cpp +++ b/src/vehicles/Cranes.cpp @@ -57,7 +57,8 @@ void CCranes::InitCranes(void) } } } - for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_INDUSTRIAL).first; pNode; pNode = pNode->next) { + // TODO(MIAMI): LEVEL_MAINLAND just so it compiles + for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_MAINLAND).first; pNode; pNode = pNode->next) { CEntity* pEntity = (CEntity*)pNode->item; if (MODELID_CRANE_1 == pEntity->GetModelIndex() || MODELID_CRANE_2 == pEntity->GetModelIndex() || @@ -87,6 +88,7 @@ void CCranes::AddThisOneCrane(CEntity* pEntity) if (pCrane->m_nAudioEntity >= 0) DMAudio.SetEntityStatus(pCrane->m_nAudioEntity, 1); pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex()); +#if 0 // Is this used to avoid military crane? if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) { CObject* pHook = new CObject(MI_MAGNET, false); @@ -99,6 +101,7 @@ void CCranes::AddThisOneCrane(CEntity* pEntity) pCrane->SetHookMatrix(); } else +#endif pCrane->m_pHook = nil; NumCranes++; } diff --git a/src/vehicles/DamageManager.cpp b/src/vehicles/DamageManager.cpp index c625a4e7..e4c28c95 100644 --- a/src/vehicles/DamageManager.cpp +++ b/src/vehicles/DamageManager.cpp @@ -10,14 +10,20 @@ float G_aComponentDamage[] = { 2.5f, 1.25f, 3.2f, 1.4f, 2.5f, 2.8f, 0.5f }; CDamageManager::CDamageManager(void) { ResetDamageStatus(); - m_fWheelDamageEffect = 0.75f; + m_fWheelDamageEffect = 0.5f; field_18 = 1; } void CDamageManager::ResetDamageStatus(void) { - memset(this, 0, sizeof(*this)); + int i; + m_fWheelDamageEffect = 0.0f; + m_engineStatus = 0; + for(i = 0; i < ARRAY_SIZE(m_wheelStatus); i++) m_wheelStatus[i] = 0; + for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++) m_doorStatus[i] = 0; + m_lightStatus = 0; + m_panelStatus = 0; } void @@ -55,6 +61,8 @@ CDamageManager::ApplyDamage(tComponent component, float damage, float unused) GetComponentGroup(component, &group, &subComp); damage *= G_aComponentDamage[group]; + if(component == COMPONENT_PANEL_WINDSCREEN) + damage *= 0.6f; if(damage > 150.0f){ switch(group){ case COMPGROUP_WHEEL: @@ -218,10 +226,6 @@ CDamageManager::GetEngineStatus(void) bool CDamageManager::ProgressEngineDamage(void) { - int status = GetEngineStatus(); - int newstatus = status + 32 + (CGeneral::GetRandomNumber() & 0x1F); - if(status < ENGINE_STATUS_ON_FIRE && newstatus > ENGINE_STATUS_ON_FIRE-1) - newstatus = ENGINE_STATUS_ON_FIRE-1; - SetEngineStatus(newstatus); - return true; + // gone in VC + return false; } diff --git a/src/vehicles/HandlingMgr.cpp b/src/vehicles/HandlingMgr.cpp index 5beed29e..3d5d4e77 100644 --- a/src/vehicles/HandlingMgr.cpp +++ b/src/vehicles/HandlingMgr.cpp @@ -4,6 +4,8 @@ #include "FileMgr.h" #include "HandlingMgr.h" +//--MIAMI: done + cHandlingDataMgr mod_HandlingManager; const char *HandlingFilename = "HANDLING.CFG"; @@ -21,7 +23,6 @@ const char VehicleNames[NUMHANDLINGS][14] = { "STRETCH", "MANANA", "INFERNUS", - "BLISTA", "PONY", "MULE", "CHEETAH", @@ -38,7 +39,6 @@ const char VehicleNames[NUMHANDLINGS][14] = { "ENFORCER", "SECURICA", "BANSHEE", - "PREDATOR", "BUS", "RHINO", "BARRACKS", @@ -50,22 +50,73 @@ const char VehicleNames[NUMHANDLINGS][14] = { "STALLION", "RUMPO", "RCBANDIT", - "BELLYUP", - "MRWONGS", "MAFIA", - "YARDIE", - "YAKUZA", - "DIABLOS", - "COLUMB", - "HOODS", "AIRTRAIN", "DEADDODO", - "SPEEDER", - "REEFER", - "PANLANT", "FLATBED", "YANKEE", - "BORGNINE" + "GOLFCART", + "VOODOO", + "WASHING", + "CUBAN", + "ROMERO", + "PACKER", + "ADMIRAL", + "GANGBUR", + "ZEBRA", + "TOPFUN", + "GLENDALE", + "OCEANIC", + "HERMES", + "SABRE1", + "SABRETUR", + "PHEONIX", + "WALTON", + "REGINA", + "COMET", + "DELUXO", + "BURRITO", + "SPAND", + "BAGGAGE", + "KAUFMAN", + "RANCHER", + "FBIRANCH", + "VIRGO", + "GREENWOO", + "HOTRING", + "SANDKING", + "BLISTAC", + "BOXVILLE", + "BENSON", + "DESPERAD", + "LOVEFIST", + "BLOODRA", + "BLOODRB", + "BIKE", + "MOPED", + "DIRTBIKE", + "ANGEL", + "FREEWAY", + "PREDATOR", + "SPEEDER", + "REEFER", + "RIO", + "SQUALO", + "TROPIC", + "COASTGRD", + "DINGHY", + "MARQUIS", + "CUPBOAT", + "SEAPLANE", + "SPARROW", + "SEASPAR", + "MAVERICK", + "COASTMAV", + "POLMAV", + "HUNTER", + "RCBARON", + "RCGOBLIN", + "RCCOPTER" }; cHandlingDataMgr::cHandlingDataMgr(void) @@ -94,6 +145,9 @@ cHandlingDataMgr::LoadHandlingData(void) int field, handlingId; int keepGoing; tHandlingData *handling; + tFlyingHandlingData *flyingHandling; + tBoatHandlingData *boatHandling; + tBikeHandlingData *bikeHandling; CFileMgr::SetDir("DATA"); CFileMgr::LoadFile(HandlingFilename, work_buff, sizeof(work_buff), "r"); @@ -102,6 +156,9 @@ cHandlingDataMgr::LoadHandlingData(void) start = (char*)work_buff; end = start+1; handling = nil; + flyingHandling = nil; + boatHandling = nil; + bikeHandling = nil; keepGoing = 1; while(keepGoing){ @@ -118,55 +175,157 @@ cHandlingDataMgr::LoadHandlingData(void) if(strncmp(line, ";the end", 9) == 0) keepGoing = 0; else if(line[0] != ';'){ - field = 0; - strcpy(delim, " \t"); - // FIX: game seems to use a do-while loop here - for(word = strtok(line, delim); word; word = strtok(nil, delim)){ - switch(field){ - case 0: - handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); - assert(handlingId >= 0 && handlingId < NUMHANDLINGS); - handling = &HandlingData[handlingId]; - handling->nIdentifier = (eHandlingId)handlingId; - break; - case 1: handling->fMass = strtod(word, nil); break; - case 2: handling->Dimension.x = strtod(word, nil); break; - case 3: handling->Dimension.y = strtod(word, nil); break; - case 4: handling->Dimension.z = strtod(word, nil); break; - case 5: handling->CentreOfMass.x = strtod(word, nil); break; - case 6: handling->CentreOfMass.y = strtod(word, nil); break; - case 7: handling->CentreOfMass.z = strtod(word, nil); break; - case 8: handling->nPercentSubmerged = atoi(word); break; - case 9: handling->fTractionMultiplier = strtod(word, nil); break; - case 10: handling->fTractionLoss = strtod(word, nil); break; - case 11: handling->fTractionBias = strtod(word, nil); break; - case 12: handling->Transmission.nNumberOfGears = atoi(word); break; - case 13: handling->Transmission.fMaxVelocity = strtod(word, nil); break; - case 14: handling->Transmission.fEngineAcceleration = strtod(word, nil) * 0.4f; break; - case 15: handling->Transmission.nDriveType = word[0]; break; - case 16: handling->Transmission.nEngineType = word[0]; break; - case 17: handling->fBrakeDeceleration = strtod(word, nil); break; - case 18: handling->fBrakeBias = strtod(word, nil); break; - case 19: handling->bABS = !!atoi(word); break; - case 20: handling->fSteeringLock = strtod(word, nil); break; - case 21: handling->fSuspensionForceLevel = strtod(word, nil); break; - case 22: handling->fSuspensionDampingLevel = strtod(word, nil); break; - case 23: handling->fSeatOffsetDistance = strtod(word, nil); break; - case 24: handling->fCollisionDamageMultiplier = strtod(word, nil); break; - case 25: handling->nMonetaryValue = atoi(word); break; - case 26: handling->fSuspensionUpperLimit = strtod(word, nil); break; - case 27: handling->fSuspensionLowerLimit = strtod(word, nil); break; - case 28: handling->fSuspensionBias = strtod(word, nil); break; - case 29: - sscanf(word, "%x", &handling->Flags); - handling->Transmission.Flags = handling->Flags; - break; - case 30: handling->FrontLights = atoi(word); break; - case 31: handling->RearLights = atoi(word); break; + if(line[0] == '!'){ + // Bike data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + bikeHandling = GetBikePointer(handlingId); + bikeHandling->nIdentifier = (eHandlingId)handlingId; + break; + case 2: bikeHandling->fLeanFwdCOM = atof(word); break; + case 3: bikeHandling->fLeanFwdForce = atof(word); break; + case 4: bikeHandling->fLeanBakCOM = atof(word); break; + case 5: bikeHandling->fLeanBackForce = atof(word); break; + case 6: bikeHandling->fMaxLean = atof(word); break; + case 7: bikeHandling->fFullAnimLean = atof(word); break; + case 8: bikeHandling->fDesLean = atof(word); break; + case 9: bikeHandling->fSpeedSteer = atof(word); break; + case 10: bikeHandling->fSlipSteer = atof(word); break; + case 11: bikeHandling->fNoPlayerCOMz = atof(word); break; + case 12: bikeHandling->fWheelieAng = atof(word); break; + case 13: bikeHandling->fStoppieAng = atof(word); break; + case 14: bikeHandling->fWheelieSteer = atof(word); break; + case 15: bikeHandling->fWheelieStabMult = atof(word); break; + case 16: bikeHandling->fStoppieStabMult = atof(word); break; + } + field++; + } + ConvertBikeDataToGameUnits(bikeHandling); + }else if(line[0] == '$'){ + // Flying data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + flyingHandling = GetFlyingPointer(handlingId); + flyingHandling->nIdentifier = (eHandlingId)handlingId; + break; + case 2: flyingHandling->fThrust = atof(word); break; + case 3: flyingHandling->fThrustFallOff = atof(word); break; + case 4: flyingHandling->fYaw = atof(word); break; + case 5: flyingHandling->fYawStab = atof(word); break; + case 6: flyingHandling->fSideSlip = atof(word); break; + case 7: flyingHandling->fRoll = atof(word); break; + case 8: flyingHandling->fRollStab = atof(word); break; + case 9: flyingHandling->fPitch = atof(word); break; + case 10: flyingHandling->fPitchStab = atof(word); break; + case 11: flyingHandling->fFormLift = atof(word); break; + case 12: flyingHandling->fAttackLift = atof(word); break; + case 13: flyingHandling->fMoveRes = atof(word); break; + case 14: flyingHandling->vecTurnRes.x = atof(word); break; + case 15: flyingHandling->vecTurnRes.y = atof(word); break; + case 16: flyingHandling->vecTurnRes.z = atof(word); break; + case 17: flyingHandling->vecSpeedRes.x = atof(word); break; + case 18: flyingHandling->vecSpeedRes.y = atof(word); break; + case 19: flyingHandling->vecSpeedRes.z = atof(word); break; + } + field++; + } + }else if(line[0] == '%'){ + // Boat data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + boatHandling = GetBoatPointer(handlingId); + boatHandling->nIdentifier = (eHandlingId)handlingId; + break; + case 2: boatHandling->fThrustY = atof(word); break; + case 3: boatHandling->fThrustZ = atof(word); break; + case 4: boatHandling->fThrustAppZ = atof(word); break; + case 5: boatHandling->fAqPlaneForce = atof(word); break; + case 6: boatHandling->fAqPlaneLimit = atof(word); break; + case 7: boatHandling->fAqPlaneOffset = atof(word); break; + case 8: boatHandling->fWaveAudioMult = atof(word); break; + case 9: boatHandling->vecMoveRes.x = atof(word); break; + case 10: boatHandling->vecMoveRes.y = atof(word); break; + case 11: boatHandling->vecMoveRes.z = atof(word); break; + case 12: boatHandling->vecTurnRes.x = atof(word); break; + case 13: boatHandling->vecTurnRes.y = atof(word); break; + case 14: boatHandling->vecTurnRes.z = atof(word); break; + case 15: boatHandling->fLook_L_R_BehindCamHeight = atof(word); break; + } + field++; } - field++; + }else{ + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + handling = &HandlingData[handlingId]; + handling->nIdentifier = (eHandlingId)handlingId; + break; + case 1: handling->fMass = atof(word); break; + case 2: handling->Dimension.x = atof(word); break; + case 3: handling->Dimension.y = atof(word); break; + case 4: handling->Dimension.z = atof(word); break; + case 5: handling->CentreOfMass.x = atof(word); break; + case 6: handling->CentreOfMass.y = atof(word); break; + case 7: handling->CentreOfMass.z = atof(word); break; + case 8: handling->nPercentSubmerged = atoi(word); break; + case 9: handling->fTractionMultiplier = atof(word); break; + case 10: handling->fTractionLoss = atof(word); break; + case 11: handling->fTractionBias = atof(word); break; + case 12: handling->Transmission.nNumberOfGears = atoi(word); break; + case 13: handling->Transmission.fMaxVelocity = atof(word); break; + case 14: handling->Transmission.fEngineAcceleration = atof(word) * 0.4f; break; + case 15: handling->Transmission.nDriveType = word[0]; break; + case 16: handling->Transmission.nEngineType = word[0]; break; + case 17: handling->fBrakeDeceleration = atof(word); break; + case 18: handling->fBrakeBias = atof(word); break; + case 19: handling->bABS = !!atoi(word); break; + case 20: handling->fSteeringLock = atof(word); break; + case 21: handling->fSuspensionForceLevel = atof(word); break; + case 22: handling->fSuspensionDampingLevel = atof(word); break; + case 23: handling->fSeatOffsetDistance = atof(word); break; + case 24: handling->fCollisionDamageMultiplier = atof(word); break; + case 25: handling->nMonetaryValue = atoi(word); break; + case 26: handling->fSuspensionUpperLimit = atof(word); break; + case 27: handling->fSuspensionLowerLimit = atof(word); break; + case 28: handling->fSuspensionBias = atof(word); break; + case 29: handling->fSuspensionAntidiveMultiplier = atof(word); break; + case 30: + sscanf(word, "%x", &handling->Flags); + handling->Transmission.Flags = handling->Flags; + break; + case 31: handling->FrontLights = atoi(word); break; + case 32: handling->RearLights = atoi(word); break; + } + field++; + } + ConvertDataToGameUnits(handling); } - ConvertDataToGameUnits(handling); } } } @@ -199,6 +358,7 @@ cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling) if(handling->fTurnMass < 10.0f) handling->fTurnMass *= 5.0f; handling->fInvMass = 1.0f/handling->fMass; + handling->fCollisionDamageMultiplier *= 2000.0f/handling->fMass; handling->fBuoyancy = 100.0f/handling->nPercentSubmerged * 0.008f*handling->fMass; // What the hell is going on here? @@ -214,11 +374,16 @@ cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling) if(handling->nIdentifier == HANDLING_RCBANDIT){ handling->Transmission.fUnkMaxVelocity = handling->Transmission.fMaxVelocity; + handling->Transmission.fMaxReverseVelocity = -handling->Transmission.fMaxVelocity; + }else if(handling->nIdentifier >= HANDLING_BIKE && handling->nIdentifier <= HANDLING_FREEWAY){ + handling->Transmission.fUnkMaxVelocity = velocity; + handling->Transmission.fMaxVelocity = velocity * 1.2f; + handling->Transmission.fMaxReverseVelocity = -0.05f; }else{ handling->Transmission.fUnkMaxVelocity = velocity; handling->Transmission.fMaxVelocity = velocity * 1.2f; + handling->Transmission.fMaxReverseVelocity = -0.2f; } - handling->Transmission.fMaxReverseVelocity = -0.2f; if(handling->Transmission.nDriveType == '4') handling->Transmission.fEngineAcceleration /= 4.0f; @@ -228,6 +393,15 @@ cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling) handling->Transmission.InitGearRatios(); } +void +cHandlingDataMgr::ConvertBikeDataToGameUnits(tBikeHandlingData *handling) +{ + handling->fMaxLean = Sin(DEGTORAD(handling->fMaxLean)); + handling->fFullAnimLean = DEGTORAD(handling->fFullAnimLean); + handling->fWheelieAng = Sin(DEGTORAD(handling->fWheelieAng)); + handling->fStoppieAng = Sin(DEGTORAD(handling->fStoppieAng)); +} + int32 cHandlingDataMgr::GetHandlingId(const char *name) { @@ -237,3 +411,19 @@ cHandlingDataMgr::GetHandlingId(const char *name) break; return i; } + +tFlyingHandlingData* +cHandlingDataMgr::GetFlyingPointer(uint8 id) +{ + if(id >= HANDLING_SEAPLANE && id <= HANDLING_RCCOPTER) + return &FlyingHandlingData[id-HANDLING_SEAPLANE]; + return &FlyingHandlingData[0]; +} + +tBoatHandlingData* +cHandlingDataMgr::GetBoatPointer(uint8 id) +{ + if(id >= HANDLING_PREDATOR && id <= HANDLING_SEAPLANE) + return &BoatHandlingData[id-HANDLING_PREDATOR]; + return &BoatHandlingData[0]; +} diff --git a/src/vehicles/HandlingMgr.h b/src/vehicles/HandlingMgr.h index 10e25573..e93f7879 100644 --- a/src/vehicles/HandlingMgr.h +++ b/src/vehicles/HandlingMgr.h @@ -16,7 +16,6 @@ enum eHandlingId HANDLING_STRETCH, HANDLING_MANANA, HANDLING_INFERNUS, - HANDLING_BLISTA, HANDLING_PONY, HANDLING_MULE, HANDLING_CHEETAH, @@ -33,7 +32,6 @@ enum eHandlingId HANDLING_ENFORCER, HANDLING_SECURICA, HANDLING_BANSHEE, - HANDLING_PREDATOR, HANDLING_BUS, HANDLING_RHINO, HANDLING_BARRACKS, @@ -45,24 +43,81 @@ enum eHandlingId HANDLING_STALLION, HANDLING_RUMPO, HANDLING_RCBANDIT, - HANDLING_BELLYUP, - HANDLING_MRWONGS, HANDLING_MAFIA, - HANDLING_YARDIE, - HANDLING_YAKUZA, - HANDLING_DIABLOS, - HANDLING_COLUMB, - HANDLING_HOODS, HANDLING_AIRTRAIN, HANDLING_DEADDODO, - HANDLING_SPEEDER, - HANDLING_REEFER, - HANDLING_PANLANT, HANDLING_FLATBED, HANDLING_YANKEE, - HANDLING_BORGNINE, + HANDLING_GOLFCART, + HANDLING_VOODOO, + HANDLING_WASHING, + HANDLING_CUBAN, + HANDLING_ROMERO, + HANDLING_PACKER, + HANDLING_ADMIRAL, + HANDLING_GANGBUR, + HANDLING_ZEBRA, + HANDLING_TOPFUN, + HANDLING_GLENDALE, + HANDLING_OCEANIC, + HANDLING_HERMES, + HANDLING_SABRE1, + HANDLING_SABRETUR, + HANDLING_PHEONIX, + HANDLING_WALTON, + HANDLING_REGINA, + HANDLING_COMET, + HANDLING_DELUXO, + HANDLING_BURRITO, + HANDLING_SPAND, + HANDLING_BAGGAGE, + HANDLING_KAUFMAN, + HANDLING_RANCHER, + HANDLING_FBIRANCH, + HANDLING_VIRGO, + HANDLING_GREENWOO, + HANDLING_HOTRING, + HANDLING_SANDKING, + HANDLING_BLISTAC, + HANDLING_BOXVILLE, + HANDLING_BENSON, + HANDLING_DESPERAD, + HANDLING_LOVEFIST, + HANDLING_BLOODRA, + HANDLING_BLOODRB, - NUMHANDLINGS + HANDLING_BIKE, + HANDLING_MOPED, + HANDLING_DIRTBIKE, + HANDLING_ANGEL, + HANDLING_FREEWAY, + + HANDLING_PREDATOR, + HANDLING_SPEEDER, + HANDLING_REEFER, + HANDLING_RIO, + HANDLING_SQUALO, + HANDLING_TROPIC, + HANDLING_COASTGRD, + HANDLING_DINGHY, + HANDLING_MARQUIS, + HANDLING_CUPBOAT, + HANDLING_SEAPLANE, // both boat and plane! + HANDLING_SPARROW, + HANDLING_SEASPAR, + HANDLING_MAVERICK, + HANDLING_COASTMAV, + HANDLING_POLMAV, + HANDLING_HUNTER, + HANDLING_RCBARON, + HANDLING_RCGOBLIN, + HANDLING_RCCOPTER, + + NUMHANDLINGS, + + NUMBIKEHANDLINGS = HANDLING_FREEWAY+1 - HANDLING_BIKE, + NUMFLYINGHANDLINGS = HANDLING_RCCOPTER+1 - HANDLING_SEAPLANE, + NUMBOATHANDLINGS = HANDLING_SEAPLANE+1 - HANDLING_PREDATOR, }; enum @@ -83,6 +138,18 @@ enum HANDLING_HAS_NO_ROOF = 0x2000, HANDLING_IS_BIG = 0x4000, HANDLING_HALOGEN_LIGHTS = 0x8000, + HANDLING_IS_BIKE = 0x10000, + HANDLING_IS_HELI = 0x20000, + HANDLING_IS_PLANE = 0x40000, + HANDLING_IS_BOAT = 0x80000, + HANDLING_NO_EXHAUST = 0x100000, + HANDLING_REARWHEEL_1ST = 0x200000, + HANDLING_HANDBRAKE_TYRE = 0x400000, + HANDLING_SIT_IN_BOAT = 0x800000, + HANDLING_FAT_REARW = 0x1000000, + HANDLING_NARROW_FRONTW = 0x2000000, + HANDLING_GOOD_INSAND = 0x4000000, + HANDLING_UNKNOWN = 0x8000000, // something for helis and planes }; struct tHandlingData @@ -109,6 +176,7 @@ struct tHandlingData float fSuspensionUpperLimit; float fSuspensionLowerLimit; float fSuspensionBias; + float fSuspensionAntidiveMultiplier; float fCollisionDamageMultiplier; uint32 Flags; float fSeatOffsetDistance; @@ -116,7 +184,60 @@ struct tHandlingData int8 FrontLights; int8 RearLights; }; -VALIDATE_SIZE(tHandlingData, 0xD8); + +struct tBikeHandlingData +{ + eHandlingId nIdentifier; + float fLeanFwdCOM; + float fLeanFwdForce; + float fLeanBakCOM; + float fLeanBackForce; + float fMaxLean; + float fFullAnimLean; + float fDesLean; + float fSpeedSteer; + float fSlipSteer; + float fNoPlayerCOMz; + float fWheelieAng; + float fStoppieAng; + float fWheelieSteer; + float fWheelieStabMult; + float fStoppieStabMult; +}; + +struct tBoatHandlingData +{ + eHandlingId nIdentifier; + float fThrustY; + float fThrustZ; + float fThrustAppZ; + float fAqPlaneForce; + float fAqPlaneLimit; + float fAqPlaneOffset; + float fWaveAudioMult; + float fLook_L_R_BehindCamHeight; + CVector vecMoveRes; + CVector vecTurnRes; +}; + +struct tFlyingHandlingData +{ + eHandlingId nIdentifier; + float fThrust; + float fThrustFallOff; + float fYaw; + float fYawStab; + float fSideSlip; + float fRoll; + float fRollStab; + float fPitch; + float fPitchStab; + float fFormLift; + float fAttackLift; + float fMoveRes; + CVector vecTurnRes; + CVector vecSpeedRes; +}; class cHandlingDataMgr { @@ -128,7 +249,9 @@ private: float field_C; // unused it seems float field_10; // tHandlingData HandlingData[NUMHANDLINGS]; - uint32 field_302C; // unused it seems + tBikeHandlingData BikeHandlingData[NUMBIKEHANDLINGS]; + tFlyingHandlingData FlyingHandlingData[NUMFLYINGHANDLINGS]; + tBoatHandlingData BoatHandlingData[NUMBOATHANDLINGS]; public: cHandlingDataMgr(void); @@ -136,10 +259,13 @@ public: void LoadHandlingData(void); int FindExactWord(const char *word, const char *words, int wordLen, int numWords); void ConvertDataToGameUnits(tHandlingData *handling); + void ConvertBikeDataToGameUnits(tBikeHandlingData *handling); int32 GetHandlingId(const char *name); tHandlingData *GetHandlingData(eHandlingId id) { return &HandlingData[id]; } + tBikeHandlingData *GetBikePointer(uint8 id) { return &BikeHandlingData[id-HANDLING_BIKE]; } + tFlyingHandlingData *GetFlyingPointer(uint8 id); + tBoatHandlingData *GetBoatPointer(uint8 id); bool HasRearWheelDrive(eHandlingId id) { return HandlingData[id].Transmission.nDriveType == 'R'; } bool HasFrontWheelDrive(eHandlingId id) { return HandlingData[id].Transmission.nDriveType == 'F'; } }; -VALIDATE_SIZE(cHandlingDataMgr, 0x3030); extern cHandlingDataMgr mod_HandlingManager; diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp index 4966a228..1e0a8c27 100644 --- a/src/vehicles/Heli.cpp +++ b/src/vehicles/Heli.cpp @@ -558,60 +558,8 @@ CHeli::ProcessControl(void) void CHeli::PreRender(void) { - float angle; - uint8 i; - CColPoint point; - CEntity *entity; - uint8 r, g, b; - float testLowZ = FindPlayerCoors().z - 10.0f; float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f; - int frm = CTimer::GetFrameCounter() & 7; - - i = 0; - for(angle = 0.0f; angle < TWOPI; angle += TWOPI/32){ - CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f); - CVector dir = CVector(pos.x, pos.y, 1.0f)*0.01f; - pos += GetPosition(); - - if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil)) - m_fHeliDustZ[frm] = point.point.z; - else - m_fHeliDustZ[frm] = -101.0f; - - switch(point.surfaceB){ - default: - case SURFACE_TARMAC: - r = 10; - g = 10; - b = 10; - break; - case SURFACE_GRASS: - r = 10; - g = 6; - b = 3; - break; - case SURFACE_GRAVEL: - r = 10; - g = 8; - b = 7; - break; - case SURFACE_MUD_DRY: - r = 10; - g = 6; - b = 3; - break; - } - RwRGBA col = { r, g, b, 32 }; -#ifdef FIX_BUGS - pos.z = m_fHeliDustZ[frm]; -#else - // What the hell is the point of this? - pos.z = m_fHeliDustZ[(i - (i&3))/4]; // advance every 4 iterations, why not just /4? -#endif - if(pos.z > -200.0f && GetPosition().z - pos.z < 20.0f) - CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, 0.0f, col); - i++; - } + HeliDustGenerate(this, radius, FindPlayerCoors().z, Max(16.0f - 4.0f*CTimer::GetTimeStep(), 2.0f)); } void @@ -776,7 +724,6 @@ CHeli::InitHelis(void) for(i = 0; i < NUM_HELIS; i++) pHelis[i] = nil; - ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_ESCAPE))->SetColModel(&CTempColModels::ms_colModelPed1); ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1); } @@ -787,10 +734,7 @@ GenerateHeli(bool catalina) CVector heliPos; int i; - if(catalina) - heli = new CHeli(MI_ESCAPE, PERMANENT_VEHICLE); - else - heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE); + heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE); if(catalina) heliPos = CVector(-224.0f, 201.0f, 83.0f); @@ -867,18 +811,6 @@ CHeli::UpdateHelis(void) pHelis[HELI_SCRIPT]->m_heliStatus = HELI_STATUS_FLY_AWAY; } - // Handle Catalina's heli - if(CatalinaHeliOn){ - if(CStreaming::HasModelLoaded(MI_ESCAPE) && pHelis[HELI_CATALINA] == nil){ - pHelis[HELI_CATALINA] = GenerateHeli(true); - pHelis[HELI_CATALINA]->m_heliType = HELI_TYPE_CATALINA; - }else - CStreaming::RequestModel(MI_ESCAPE, STREAMFLAGS_DONT_REMOVE); - }else{ - if(pHelis[HELI_CATALINA]) - pHelis[HELI_CATALINA]->m_heliStatus = HELI_STATUS_FLY_AWAY; - } - // Delete helis that we no longer need for(i = 0; i < NUM_HELIS; i++) if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_FLY_AWAY && pHelis[i]->GetPosition().z > 150.0f){ diff --git a/src/vehicles/Heli.h b/src/vehicles/Heli.h index cf3f791f..a8f604aa 100644 --- a/src/vehicles/Heli.h +++ b/src/vehicles/Heli.h @@ -95,6 +95,3 @@ public: static void ActivateHeli(bool activate); }; - -VALIDATE_SIZE(CHeli, 0x33C); - diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp index 71189d84..8f32f12a 100644 --- a/src/vehicles/Plane.cpp +++ b/src/vehicles/Plane.cpp @@ -735,6 +735,7 @@ CPlane::InitPlanes(void) TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED; } +/* // Mission Cesna if(pPath3Nodes == nil){ pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false); @@ -746,6 +747,7 @@ CPlane::InitPlanes(void) pPath4Nodes = LoadPath("data\\paths\\flight4.dat", NumPath4Nodes, TotalLengthOfFlightPath4, false); TotalDurationOfFlightPath4 = TotalLengthOfFlightPath4/CRUISE_SPEED; } +*/ CStreaming::LoadAllRequestedModels(false); CStreaming::RequestModel(MI_AIRTRAIN, 0); @@ -806,7 +808,7 @@ CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool for(i = 0; i < numNodes; i++){ *gString = '\0'; - for(lp = 0; work_buff[bp] != '\n'; bp++, lp++) + for(lp = 0; work_buff[bp] != '\n' && work_buff[bp] != '\0'; bp++, lp++) gString[lp] = work_buff[bp]; bp++; // BUG: game doesn't terminate string diff --git a/src/vehicles/Plane.h b/src/vehicles/Plane.h index 783c53b3..e9456bcd 100644 --- a/src/vehicles/Plane.h +++ b/src/vehicles/Plane.h @@ -64,8 +64,6 @@ public: static bool HasDropOffCesnaBeenShotDown(void); }; -VALIDATE_SIZE(CPlane, 0x29C); - extern float LandingPoint; extern float TakeOffPoint; extern float PlanePathPosition[3]; diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp index 38bb1300..e3bc8a9f 100644 --- a/src/vehicles/Train.cpp +++ b/src/vehicles/Train.cpp @@ -41,6 +41,7 @@ static bool bTrainArrivalAnnounced[3] = {false, false, false}; CTrain::CTrain(int32 id, uint8 CreatedBy) : CVehicle(CreatedBy) { +#ifdef GTA_TRAIN CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); m_vehType = VEHICLE_TYPE_TRAIN; pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); @@ -63,22 +64,28 @@ CTrain::CTrain(int32 id, uint8 CreatedBy) bUsesCollision = true; SetStatus(STATUS_TRAIN_MOVING); +#else + assert(0 && "No trains in this game"); +#endif } void CTrain::SetModelIndex(uint32 id) { +#ifdef GTA_TRAIN int i; CVehicle::SetModelIndex(id); for(i = 0; i < NUM_TRAIN_NODES; i++) m_aTrainNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes); +#endif } void CTrain::ProcessControl(void) { +#ifdef GTA_TRAIN if(gbModelViewer || m_isFarAway && (CTimer::GetFrameCounter() + m_nWagonId) & 0xF) return; @@ -281,11 +288,13 @@ CTrain::ProcessControl(void) TrainHitStuff(s->m_lists[ENTITYLIST_PEDS_OVERLAP]); } } +#endif GTA_TRAIN } void CTrain::PreRender(void) { +#ifdef GTA_TRAIN CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); if(m_bIsFirstWagon){ @@ -345,17 +354,21 @@ CTrain::PreRender(void) CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); } +#endif } void CTrain::Render(void) { +#ifdef GTA_TRAIN CEntity::Render(); +#endif } void CTrain::TrainHitStuff(CPtrList &list) { +#ifdef GTA_TRAIN CPtrNode *node; CPhysical *phys; @@ -364,11 +377,13 @@ CTrain::TrainHitStuff(CPtrList &list) if(phys != this && Abs(this->GetPosition().z - phys->GetPosition().z) < 1.5f) phys->bHitByTrain = true; } +#endif } void CTrain::AddPassenger(CPed *ped) { +#ifdef GTA_TRAIN int i = ped->m_vehEnterType; if((i == TRAIN_POS_LEFT_ENTRY || i == TRAIN_POS_MID_ENTRY || i == TRAIN_POS_RIGHT_ENTRY) && pPassengers[i] == nil){ pPassengers[i] = ped; @@ -381,11 +396,13 @@ CTrain::AddPassenger(CPed *ped) return; } } +#endif } void CTrain::OpenTrainDoor(float ratio) { +#ifdef GTA_TRAIN if(m_rwObject == nil) return; @@ -410,6 +427,7 @@ CTrain::OpenTrainDoor(float ratio) doorL.UpdateRW(); doorR.UpdateRW(); +#endif } @@ -417,6 +435,7 @@ CTrain::OpenTrainDoor(float ratio) void CTrain::InitTrains(void) { +#ifdef GTA_TRAIN int i, j; CTrain *train; @@ -483,21 +502,25 @@ CTrain::InitTrains(void) for(j = 0; pTrackNodes_S[j].t < StationDist_S[i]; j++); aStationCoors_S[i] = pTrackNodes_S[j].p; } +#endif } void CTrain::Shutdown(void) { +#ifdef GTA_TRAIN delete[] pTrackNodes; delete[] pTrackNodes_S; pTrackNodes = nil; pTrackNodes_S = nil; +#endif } void CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists, float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail) { +#ifdef GTA_TRAIN bool readingFile = false; int bp, lp; int i, tmp; @@ -619,6 +642,7 @@ CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int1 // end interpLines[j].time = *totalDuration; +#endif } void @@ -668,6 +692,7 @@ ProcessTrainAnnouncements(void) void CTrain::UpdateTrains(void) { +#ifdef GTA_TRAIN int i, j; uint32 time; float t, deltaT; @@ -731,4 +756,5 @@ CTrain::UpdateTrains(void) // time offset for each train time += 0x40000/4; } +#endif } diff --git a/src/vehicles/Train.h b/src/vehicles/Train.h index 3446eeb5..57cd28de 100644 --- a/src/vehicles/Train.h +++ b/src/vehicles/Train.h @@ -82,5 +82,3 @@ public: float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail); static void UpdateTrains(void); }; - -VALIDATE_SIZE(CTrain, 0x2E4); diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index dbb15b5d..c5537a48 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -5,6 +5,8 @@ #include "Timer.h" #include "Pad.h" #include "Vehicle.h" +#include "Bike.h" +#include "Automobile.h" #include "Pools.h" #include "HandlingMgr.h" #include "CarCtrl.h" @@ -14,10 +16,24 @@ #include "Lights.h" #include "PointLights.h" #include "Renderer.h" +#include "VisibilityPlugins.h" #include "DMAudio.h" #include "Radar.h" #include "Fire.h" #include "Darkel.h" +#include "Streaming.h" +#include "Camera.h" +#include "Stats.h" +#include "Garages.h" +#include "Wanted.h" +#include "SurfaceTable.h" +#include "Particle.h" +#include "WaterLevel.h" +#include "Timecycle.h" +#include "Weather.h" +#include "Coronas.h" + +//--MIAMI: done bool CVehicle::bWheelsOnlyCheat; bool CVehicle::bAllDodosCheat; @@ -27,7 +43,11 @@ bool CVehicle::bCheat5; #ifdef ALT_DODO_CHEAT bool CVehicle::bAltDodoCheat; #endif +bool CVehicle::bHoverCheat; +bool CVehicle::bAllTaxisHaveNitro; bool CVehicle::m_bDisableMouseSteering = true; +bool CVehicle::bDisableRemoteDetonation; +bool CVehicle::bDisableRemoteDetonationOnContact; void *CVehicle::operator new(size_t sz) { return CPools::GetVehiclePool()->New(); } void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehiclePool()->New(handle); } @@ -55,6 +75,7 @@ CVehicle::CVehicle(uint8 CreatedBy) m_fSteerInput = 0.0f; m_type = ENTITY_TYPE_VEHICLE; VehicleCreatedBy = CreatedBy; + m_nRouteSeed = 0; bIsLocked = false; bIsLawEnforcer = false; bIsAmbulanceOnDuty = false; @@ -99,6 +120,26 @@ CVehicle::CVehicle(uint8 CreatedBy) m_bSirenOrAlarm = 0; m_nCarHornTimer = 0; m_nCarHornPattern = 0; + m_nCarHornDelay = 0; + bPartOfConvoy = false; + bHeliMinimumTilt = false; + bAudioChangingGear = false; + bIsDrowning = false; + bTyresDontBurst = false; + bCreatedAsPoliceVehicle = false; + bRestingOnPhysical = false; + bParking = false; + bCanPark = CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 0.0f; // never true. probably doesn't work very well + bIsVan = false; + bIsBus = false; + bIsBig = false; + bLowVehicle = false; + + m_bombType = CARBOMB_NONE; + bDriverLastFrame = false; + m_pBombRigger = nil; + + m_nSetPieceExtendedRangeTime = 0; m_nAlarmState = 0; m_nDoorLock = CARLOCK_UNLOCKED; m_nLastWeaponDamage = -1; @@ -106,7 +147,26 @@ CVehicle::CVehicle(uint8 CreatedBy) m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, this); if(m_audioEntityId >= 0) DMAudio.SetEntityStatus(m_audioEntityId, true); - m_nRadioStation = CGeneral::GetRandomNumber() % USERTRACK; + //m_nRadioStation = CGeneral::GetRandomNumber() % USERTRACK; + 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; + default: + m_nRadioStation = CGeneral::GetRandomNumber() % USERTRACK; + break; + } m_pCurGroundEntity = nil; m_bRainAudioCounter = 0; m_bRainSamplesCounter = 0; @@ -118,6 +178,7 @@ CVehicle::CVehicle(uint8 CreatedBy) AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); AutoPilot.m_bStayInCurrentLevel = false; AutoPilot.m_bIgnorePathfinding = false; + AutoPilot.m_nSwitchDistance = 20; } CVehicle::~CVehicle() @@ -182,57 +243,30 @@ CVehicle::RemoveLighting(bool reset) CRenderer::RemoveVehiclePedLights(this, reset); } +bool +CVehicle::IsClearToDriveAway(void) +{ + CColPoint point; + float length = GetColModel()->boundingBox.GetSize().y; + CEntity *ent = nil; + CVector front = GetForward() * (length*0.5f + 3.0f); + return !CWorld::ProcessLineOfSight(GetPosition() + front, GetPosition(), + point, ent, true, true, false, false, false, true, true) || + ent == this; +} + float CVehicle::GetHeightAboveRoad(void) { return -1.0f * GetColModel()->boundingBox.min.z; } -const float fRCPropFallOff = 3.0f; -const float fRCAeroThrust = 0.003f; -const float fRCSideSlipMult = 0.1f; -const float fRCRudderMult = 0.2f; -const float fRCYawMult = -0.01f; -const float fRCRollMult = 0.02f; -const float fRCRollStabilise = -0.08f; -const float fRCPitchMult = 0.005f; -const float fRCTailMult = 0.3f; -const float fRCFormLiftMult = 0.02f; -const float fRCAttackLiftMult = 0.25f; -const CVector vecRCAeroResistance(0.998f, 0.998f, 0.9f); - -const float fSeaPropFallOff = 2.3f; -const float fSeaThrust = 0.002f; -const float fSeaSideSlipMult = 0.1f; -const float fSeaRudderMult = 0.01f; -const float fSeaYawMult = -0.0003f; -const float fSeaRollMult = 0.0015f; -const float fSeaRollStabilise = -0.01f; -const float fSeaPitchMult = 0.0002f; -const float fSeaTailMult = 0.01f; -const float fSeaFormLiftMult = 0.012f; -const float fSeaAttackLiftMult = 0.1f; -const CVector vecSeaAeroResistance(0.995f, 0.995f, 0.85f); - -const float fSpeedResistanceY = 500.0f; -const float fSpeedResistanceZ = 500.0f; - -const CVector vecHeliMoveRes(0.995f, 0.995f, 0.99f); -const CVector vecRCHeliMoveRes(0.99f, 0.99f, 0.99f); -const float fThrustVar = 0.3f; -const float fRotorFallOff = 0.75f; -const float fStabiliseVar = 0.015f; -const float fPitchBrake = 10.0f; -const float fPitchVar = 0.006f; -const float fRollVar = 0.006f; -const float fYawVar = -0.001f; -const CVector vecHeliResistance(0.81f, 0.85f, 0.99f); -const CVector vecRCHeliResistance(0.92f, 0.92f, 0.998f); -const float fSpinSpeedRes = 20.0f; - void CVehicle::FlyingControl(eFlightModel flightModel) { + if(pFlyingHandling == nil) + return; + switch(flightModel){ case FLIGHT_MODEL_DODO: { @@ -273,6 +307,8 @@ CVehicle::FlyingControl(eFlightModel flightModel) m_vecTurnSpeed.y *= Pow(0.9f, CTimer::GetTimeStep()); + + moveSpeed = m_vecMoveSpeed.MagnitudeSqr(); if(moveSpeed > SQR(1.5f)) m_vecMoveSpeed *= 1.5f/Sqrt(moveSpeed); @@ -285,38 +321,53 @@ CVehicle::FlyingControl(eFlightModel flightModel) case FLIGHT_MODEL_RCPLANE: case FLIGHT_MODEL_SEAPLANE: + case FLIGHT_MODEL_PLANE_UNUSED: + case FLIGHT_MODEL_PLANE: { + float fSteerLR = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + float fSteerUD = -CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; + float fGunUD = Abs(CPad::GetPad(0)->GetCarGunUpDown()); + if(fGunUD > 1.0f) + fSteerUD = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; + + float fSteerAngle = Atan2(fSteerUD, fSteerLR); + float fSteerMult = 1.0f; + if(fSteerAngle > -PI/4.0f && fSteerAngle <= PI/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle); + else if(fSteerAngle > PI/4.0f && fSteerAngle <= PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle - HALFPI); + else if(fSteerAngle > PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle - PI); + else if(fSteerAngle <= -PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle + PI); + else if(fSteerAngle > -PI*3.0f/4.0f && fSteerAngle < -PI/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle + HALFPI); + + fSteerLR *= fSteerMult; + fSteerUD *= -fSteerMult; + // thrust float fForwSpeed = DotProduct(GetMoveSpeed(), GetForward()); CVector vecTail = GetColModel()->boundingBox.min.y * GetForward(); float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; - if (fForwSpeed > 0.1f || (flightModel == FLIGHT_MODEL_RCPLANE && fForwSpeed > 0.02f)) - fThrust += 1.0f; - else if (fForwSpeed > 0.0f && fThrust < 0.0f) - fThrust = 0.0f; float fThrustAccel; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fThrustAccel = (fThrust - fRCPropFallOff * fForwSpeed) * fRCAeroThrust; + if(fForwSpeed > 0.0f || fThrust > 0.0f) + fThrustAccel = (fThrust - pFlyingHandling->fThrustFallOff * fForwSpeed) * pFlyingHandling->fThrust; else - fThrustAccel = (fThrust - fSeaPropFallOff * fForwSpeed) * fSeaThrust; + fThrustAccel = Min(fThrust - 8.0f * pFlyingHandling->fThrustFallOff * fForwSpeed, 0.0f) * pFlyingHandling->fThrust; + if(flightModel == FLIGHT_MODEL_PLANE_UNUSED) + fThrustAccel *= 0.3f; + else if(flightModel == FLIGHT_MODEL_PLANE) + fThrustAccel *= 0.1f; ApplyMoveForce(fThrustAccel * GetForward() * m_fMass * CTimer::GetTimeStep()); // left/right float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); - float fSteerLR = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; - float fSideSlipAccel; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fSideSlipAccel = Abs(fSideSpeed) * fSideSpeed * fRCSideSlipMult; - else - fSideSlipAccel = Abs(fSideSpeed) * fSideSpeed * fSeaSideSlipMult; + float fSideSlipAccel = pFlyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); float fYaw = -DotProduct(GetSpeed(vecTail), GetRight()); - float fYawAccel; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fYawAccel = fRCRudderMult * fYaw * Abs(fYaw) + fRCYawMult * fSteerLR * fForwSpeed; - else - fYawAccel = fSeaRudderMult * fYaw * Abs(fYaw) + fSeaYawMult * fSteerLR * fForwSpeed; + float fYawAccel = pFlyingHandling->fYawStab * fYaw * Abs(fYaw) + pFlyingHandling->fYaw * fSteerLR * fForwSpeed; ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), vecTail); float fRollAccel; @@ -324,38 +375,25 @@ CVehicle::FlyingControl(eFlightModel flightModel) float fDirectionMultiplier = CPad::GetPad(0)->GetLookRight(); if (CPad::GetPad(0)->GetLookLeft()) fDirectionMultiplier = -1; - fRollAccel = (0.5f * fDirectionMultiplier + fSteerLR) * fRCRollMult; + fRollAccel = (0.5f * fDirectionMultiplier + fSteerLR) * pFlyingHandling->fRoll; } else - fRollAccel = fSteerLR * fSeaRollMult; + fRollAccel = fSteerLR * pFlyingHandling->fRoll; ApplyTurnForce(GetRight() * fRollAccel * fForwSpeed * m_fTurnMass * CTimer::GetTimeStep(), GetUp()); CVector vecFRight = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); CVector vecStabilise = (GetUp().z > 0.0f) ? vecFRight : -vecFRight; float fStabiliseDirection = (GetRight().z > 0.0f) ? -1.0f : 1.0f; - float fStabiliseSpeed; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fStabiliseSpeed = fRCRollStabilise * fStabiliseDirection * (1.0f - DotProduct(GetRight(), vecStabilise)) * (1.0f - Abs(GetForward().z)); - else - fStabiliseSpeed = fSeaRollStabilise * fStabiliseDirection * (1.0f - DotProduct(GetRight(), vecStabilise)) * (1.0f - Abs(GetForward().z)); - ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? VC doesn't have it too + float fStabiliseSpeed = pFlyingHandling->fRollStab * fStabiliseDirection * (1.0f - DotProduct(GetRight(), vecStabilise)) * (1.0f - Abs(GetForward().z)); + ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? // up/down float fTail = -DotProduct(GetSpeed(vecTail), GetUp()); - float fSteerUD = -CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; - float fPitchAccel; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fPitchAccel = fRCTailMult * fTail * Abs(fTail) + fRCPitchMult * fSteerUD * fForwSpeed; - else - fPitchAccel = fSeaTailMult * fTail * Abs(fTail) + fSeaPitchMult * fSteerUD * fForwSpeed; + float fPitchAccel = pFlyingHandling->fPitchStab * fTail * Abs(fTail) + pFlyingHandling->fPitch * fSteerUD * fForwSpeed; ApplyTurnForce(fPitchAccel * m_fTurnMass * GetUp() * CTimer::GetTimeStep(), vecTail); float fLift = -DotProduct(GetMoveSpeed(), GetUp()) / Max(0.01f, GetMoveSpeed().Magnitude()); - float fLiftAccel; - if (flightModel == FLIGHT_MODEL_RCPLANE) - fLiftAccel = (fRCAttackLiftMult * fLift + fRCFormLiftMult) * fForwSpeed * fForwSpeed; - else - fLiftAccel = (fSeaAttackLiftMult * fLift + fSeaFormLiftMult) * fForwSpeed * fForwSpeed; + float fLiftAccel = (pFlyingHandling->fAttackLift * fLift + pFlyingHandling->fFormLift) * fForwSpeed * fForwSpeed; float fLiftImpulse = fLiftAccel * m_fMass * CTimer::GetTimeStep(); if (GRAVITY * CTimer::GetTimeStep() * m_fMass < fLiftImpulse) { if (flightModel == FLIGHT_MODEL_RCPLANE && GetPosition().z > 50.0f) @@ -366,83 +404,107 @@ CVehicle::FlyingControl(eFlightModel flightModel) ApplyMoveForce(fLiftImpulse * GetUp()); CVector vecResistance; - if (flightModel == FLIGHT_MODEL_RCPLANE) - vecResistance = vecRCAeroResistance; - else - vecResistance = vecSeaAeroResistance; + vecResistance = pFlyingHandling->vecTurnRes; float rX = Pow(vecResistance.x, CTimer::GetTimeStep()); float rY = Pow(vecResistance.y, CTimer::GetTimeStep()); float rZ = Pow(vecResistance.z, CTimer::GetTimeStep()); CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); vecTurnSpeed.x *= rX; - float fResistance = vecTurnSpeed.y * (1.0f / (fSpeedResistanceY * SQR(vecTurnSpeed.y) + 1.0f)) * rY - vecTurnSpeed.y; + float fResistance = vecTurnSpeed.y * (1.0f / (pFlyingHandling->vecSpeedRes.y * SQR(vecTurnSpeed.y) + 1.0f)) * rY - vecTurnSpeed.y; vecTurnSpeed.z *= rZ; m_vecTurnSpeed = Multiply3x3(GetMatrix(), vecTurnSpeed); ApplyTurnForce(-GetUp() * fResistance * m_fTurnMass, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + + float fMoveSpeed = m_vecMoveSpeed.MagnitudeSqr(); + if(fMoveSpeed > SQR(1.5f)) + m_vecMoveSpeed *= 1.5f/Sqrt(fMoveSpeed); + + float fTurnSpeed = m_vecTurnSpeed.MagnitudeSqr(); + if(fTurnSpeed > SQR(0.2f)) + m_vecTurnSpeed *= 0.2f/Sqrt(fTurnSpeed); break; } + case FLIGHT_MODEL_RCHELI: case FLIGHT_MODEL_HELI: { - CVector vecMoveResistance; - if (GetModelIndex() == MI_MIAMI_SPARROW) - vecMoveResistance = vecHeliMoveRes; - else - vecMoveResistance = vecRCHeliMoveRes; - float rmX = Pow(vecMoveResistance.x, CTimer::GetTimeStep()); - float rmY = Pow(vecMoveResistance.y, CTimer::GetTimeStep()); - float rmZ = Pow(vecMoveResistance.z, CTimer::GetTimeStep()); - m_vecMoveSpeed.x *= rmX; - m_vecMoveSpeed.y *= rmY; - m_vecMoveSpeed.z *= rmZ; + float rm = Pow(pFlyingHandling->fMoveRes, CTimer::GetTimeStep()); + m_vecMoveSpeed *= rm; if (GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE) return; - float fThrust; - if (bCheat5) - fThrust = CPad::GetPad(0)->GetSteeringUpDown() * fThrustVar / 128.0f + 0.95f; - else - fThrust = fThrustVar * (CPad::GetPad(0)->GetAccelerate() - 2 * CPad::GetPad(0)->GetBrake()) / 255.0f + 0.95f; - fThrust -= fRotorFallOff * DotProduct(m_vecMoveSpeed, GetUp()); -#ifdef GTA3_1_1_PATCH - if (fThrust > 0.9f && GetPosition().z > 80.0f) - fThrust = 0.9f; -#endif + float fUpSpeed = DotProduct(m_vecMoveSpeed, GetUp()); + float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; + if(fThrust < 0.0f) + fThrust *= 2.0f; + if(flightModel == FLIGHT_MODEL_RCHELI){ + fThrust = pFlyingHandling->fThrust * fThrust + 0.45f; + ApplyMoveForce(GRAVITY * CVector(0.0f, 0.0f, 0.5f) * m_fMass * CTimer::GetTimeStep()); + }else + fThrust = pFlyingHandling->fThrust * fThrust + 0.95f; + fThrust -= pFlyingHandling->fThrustFallOff * fUpSpeed; + if(flightModel == FLIGHT_MODEL_RCHELI && GetPosition().z > 40.0f) + fThrust *= 10.0f/(GetPosition().z - 30.0f); + else if(GetPosition().z > 80.0f) + fThrust *= 10.0f/(GetPosition().z - 70.0f); ApplyMoveForce(GRAVITY * GetUp() * fThrust * m_fMass * CTimer::GetTimeStep()); - if (GetUp().z > 0.0f) - ApplyTurnForce(-CVector(GetUp().x, GetUp().y, 0.0f) * fStabiliseVar * m_fTurnMass * CTimer::GetTimeStep(), GetUp()); + if (GetUp().z > 0.0f){ + float upRight = clamp(GetRight().z, -pFlyingHandling->fFormLift, pFlyingHandling->fFormLift); + float upImpulseRight = -upRight * pFlyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); + + float upFwd = clamp(GetForward().z, -pFlyingHandling->fFormLift, pFlyingHandling->fFormLift); + float upImpulseFwd = -upFwd * pFlyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); + }else{ + float upRight = GetRight().z < 0.0f ? -pFlyingHandling->fFormLift : pFlyingHandling->fFormLift; + float upImpulseRight = -upRight * pFlyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); + + float upFwd = GetForward().z < 0.0f ? -pFlyingHandling->fFormLift : pFlyingHandling->fFormLift; + float upImpulseFwd = -upFwd * pFlyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); + } float fRoll, fPitch, fYaw; if (bCheat5) { - fPitch = CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; - fRoll = -CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; - fYaw = CPad::GetPad(0)->GetCarGunLeftRight() / 128.0f; - } - else { fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; fRoll = CPad::GetPad(0)->GetLookLeft(); if (CPad::GetPad(0)->GetLookRight()) fRoll = -1.0f; fYaw = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + } else { + fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; + fRoll = -CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + fYaw = CPad::GetPad(0)->GetLookRight(); + if (CPad::GetPad(0)->GetLookLeft()) + fYaw = -1.0f; + if(Abs(CPad::GetPad(0)->GetCarGunLeftRight()) > 1.0f) + fYaw = CPad::GetPad(0)->GetCarGunLeftRight() / 128.0f; } + if(Abs(CPad::GetPad(0)->GetCarGunUpDown()) > 1.0f) + fPitch = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; if (CPad::GetPad(0)->GetHorn()) { fYaw = 0.0f; - fPitch = clamp(10.0f * DotProduct(m_vecMoveSpeed, GetForward()), -200.0f, 1.3f); - fRoll = clamp(10.0f * DotProduct(m_vecMoveSpeed, GetRight()), -200.0f, 1.3f); + fPitch = clamp(pFlyingHandling->fPitchStab * DotProduct(m_vecMoveSpeed, GetForward()), -200.0f, 1.3f); + fRoll = clamp(pFlyingHandling->fRollStab * DotProduct(m_vecMoveSpeed, GetRight()), -200.0f, 1.3f); } - ApplyTurnForce(fPitch * GetUp() * fPitchVar * m_fTurnMass * CTimer::GetTimeStep(), GetForward()); - ApplyTurnForce(fRoll * GetUp() * fRollVar * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); - ApplyTurnForce(fYaw * GetForward() * fYawVar * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); + ApplyTurnForce(fPitch * GetUp() * pFlyingHandling->fPitch * m_fTurnMass * CTimer::GetTimeStep(), GetForward()); + ApplyTurnForce(fRoll * GetUp() * pFlyingHandling->fRoll * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); - CVector vecResistance; - if (GetModelIndex() == MI_MIAMI_SPARROW) - vecResistance = vecHeliResistance; - else - vecResistance = vecRCHeliResistance; - float rX = Pow(vecResistance.x, CTimer::GetTimeStep()); - float rY = Pow(vecResistance.y, CTimer::GetTimeStep()); - float rZ = Pow(vecResistance.z, CTimer::GetTimeStep()); + float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); + float fSideSlipAccel = pFlyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); + ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); + float fYawAccel = pFlyingHandling->fYawStab * fSideSpeed * Abs(fSideSpeed) + pFlyingHandling->fYaw * fYaw; + ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), -GetForward()); + + ApplyTurnForce(fYaw * GetForward() * pFlyingHandling->fYaw * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); + + float rX = Pow(pFlyingHandling->vecTurnRes.x, CTimer::GetTimeStep()); + float rY = Pow(pFlyingHandling->vecTurnRes.y, CTimer::GetTimeStep()); + float rZ = Pow(pFlyingHandling->vecTurnRes.z, CTimer::GetTimeStep()); CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); - float fResistanceMultiplier = Pow(1.0f / (fSpinSpeedRes * SQR(vecTurnSpeed.z) + 1.0f) * rZ, CTimer::GetTimeStep()); + float fResistanceMultiplier = Pow(1.0f / (pFlyingHandling->vecSpeedRes.z * SQR(vecTurnSpeed.z) + 1.0f) * rZ, CTimer::GetTimeStep()); float fResistance = vecTurnSpeed.z * fResistanceMultiplier - vecTurnSpeed.z; vecTurnSpeed.x *= rX; vecTurnSpeed.y *= rY; @@ -454,8 +516,228 @@ CVehicle::FlyingControl(eFlightModel flightModel) } } +static CColModel rotorColModel; +static CColSphere rotorColSphere; +float ROTOR_SEMI_THICKNESS = 0.05f; +float ROTOR_TURN_SPEED = 0.2f; +float ROTOR_DISGUARD_MULT = 0.3f; +float ROTOR_COL_ELASTICITY = 1.0f; +float ROTOR_COL_TURNMULT = -0.001f; +float ROTOR_DEFAULT_DAMAGE = 100.0f; + +bool +CVehicle::DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult) +{ + CVector max(radius, radius, radius); + CVector min(-radius, -radius, -radius); + + switch(rotorType){ + case ROTOR_TOP: + case ROTOR_BOTTOM: + min.z = -ROTOR_SEMI_THICKNESS; + max.z = ROTOR_SEMI_THICKNESS; + break; + case ROTOR_FRONT: + case ROTOR_BACK: + min.y = -ROTOR_SEMI_THICKNESS; + max.y = ROTOR_SEMI_THICKNESS; + break; + case ROTOR_RIGHT: + case ROTOR_LEFT: + min.x = -ROTOR_SEMI_THICKNESS; + max.x = ROTOR_SEMI_THICKNESS; + break; + } + + min += pos; + max += pos; + rotorColModel.boundingBox.Set(min, max); + rotorColModel.boundingSphere.Set(radius, pos); + rotorColSphere.Set(radius, pos, 0, 0); + rotorColModel.spheres = &rotorColSphere; + rotorColModel.numSpheres = 1; + + pos = matrix * pos; + bool hadCollision = false; + int minX = CWorld::GetSectorIndexX(pos.x - radius); + if(minX <= 0) minX = 0; + + int minY = CWorld::GetSectorIndexY(pos.y - radius); + if(minY <= 0) minY = 0; + + int maxX = CWorld::GetSectorIndexX(pos.x + radius); +#ifdef FIX_BUGS + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(pos.y + radius); +#ifdef FIX_BUGS + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + CWorld::AdvanceCurrentScanCode(); + for(int curY = minY; curY <= maxY; curY++) { + for(int curX = minX; curX <= maxX; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS], rotorColModel, matrix, rotorType, 0.0f)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], rotorColModel, matrix, rotorType, 0.0f)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + } + } + rotorColModel.spheres = nil; + rotorColModel.numSpheres = 0; + + return hadCollision; +} + +bool +CVehicle::BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult) +{ + int i; + CVector axis; + CVector turnSpeed(0.0f, 0.0f, 0.0f); + switch(rotorType){ + case ROTOR_TOP: + turnSpeed.z = -ROTOR_TURN_SPEED; + axis = -matrix.GetUp(); + break; + case ROTOR_BOTTOM: + turnSpeed.z = ROTOR_TURN_SPEED; + axis = matrix.GetUp(); + break; + + case ROTOR_FRONT: + turnSpeed.y = -ROTOR_TURN_SPEED; + axis = -matrix.GetForward(); + break; + case ROTOR_BACK: + turnSpeed.y = ROTOR_TURN_SPEED; + axis = matrix.GetForward(); + break; + + case ROTOR_RIGHT: + turnSpeed.x = -ROTOR_TURN_SPEED; + axis = -matrix.GetRight(); + break; + case ROTOR_LEFT: + turnSpeed.x = ROTOR_TURN_SPEED; + axis = matrix.GetRight(); + break; + } + turnSpeed = Multiply3x3(matrix, turnSpeed); + CVector center = rotorColModel.boundingSphere.center; + center = matrix*center; + + for(CPtrNode *node = list.first; node; node = node->next) { + CEntity *entity = (CEntity *)node->item; + if(entity == (CEntity*)this || + !entity->bUsesCollision || + entity->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + + entity->m_scanCode = CWorld::GetCurrentScanCode(); + + int numCollisions; + CColModel *entityCol; + if(entity->IsPed()) + entityCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(entity->GetModelIndex()))->AnimatePedColModelSkinned(entity->GetClump()); + else + entityCol = entity->GetColModel(); + if(entityCol) + numCollisions = CCollision::ProcessColModels(matrix, rotorColModel, entity->GetMatrix(), *entityCol, + CWorld::m_aTempColPts, nil, nil); + else + numCollisions = 0; + + if(numCollisions > 0 && entity->IsPed()){ + CPed *ped = (CPed*)entity; + CVector2D dirToRotor = GetPosition() - entity->GetPosition(); + dirToRotor.Normalise(); + int localDir = ped->GetLocalDirection(dirToRotor); + if(ped->m_attachedTo == nil){ + ped->bIsStanding = false; + ped->ApplyMoveForce(-5.0f*dirToRotor.x, -5.0f*dirToRotor.y, 5.0f); + } + ped->InflictDamage(this, WEAPONTYPE_RUNOVERBYCAR, 1000.0f, PEDPIECE_TORSO, localDir); + + if(CGame::nastyGame && ped->GetIsOnScreen()){ + for(i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, ped->GetPosition(), CVector(dirToRotor.x, dirToRotor.y, 1.0f) * 0.01f); + CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition(), CVector(0.0f, 0.0f, 0.02f), nil, 0.1f); + CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition()+CVector(0.0f, 0.0f, 0.2f), CVector(0.0f, 0.0f, -0.01f), nil, 0.1f); + } + }else if(numCollisions > 0 && entity->GetModelIndex() != MI_MISSILE){ + float impulse = 0.0f; + bool hadCollision = false; + float savedElasticity = m_fElasticity; + m_fElasticity = ROTOR_COL_ELASTICITY; + + for(i = 0; i < numCollisions; i++){ + CVector colpos = CWorld::m_aTempColPts[i].point; + CVector localColpos = colpos - center; + float axisDir = DotProduct(axis, localColpos); + float colDir = DotProduct(CWorld::m_aTempColPts[i].normal, localColpos); + + if(2.0f*ROTOR_SEMI_THICKNESS < Abs(axisDir) && + ROTOR_DISGUARD_MULT*Abs(colDir) < Abs(axisDir)) + continue; + + hadCollision = true; + colpos -= axisDir*axis; // get rid of axis component + + CVector tangentSpeed = CrossProduct(turnSpeed, colpos - center); + + // Particles + for(int j = 0; j < 4; j++){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpos, (tangentSpeed+m_vecMoveSpeed)/2.0f); + CParticle::AddParticle(PARTICLE_SPARK, colpos, 0.1f*CWorld::m_aTempColPts[i].normal); + } + + // Apply Collision + if(IsCar()){ + CAutomobile *heli = (CAutomobile*)this; + if(heli->m_aWheelSpeed[1] > 0.15f){ + ApplyCollision(CWorld::m_aTempColPts[i], impulse); + ApplyTurnForce(m_fTurnMass*ROTOR_COL_TURNMULT*tangentSpeed, colpos - center); + heli->m_aWheelSpeed[1] = 0.15f; + }else if(heli->m_aWheelSpeed[1] < 0.075f && heli->m_aWheelSpeed[1] > 0.0f) + heli->m_aWheelSpeed[1] *= -1.0f; + } + + float damageImpulse = damageMult * Max(impulse, ROTOR_DEFAULT_DAMAGE*m_fMass/3000.0f); + if(damageImpulse > m_fDamageImpulse) + SetDamagedPieceRecord(0, damageImpulse, entity, CWorld::m_aTempColPts[i].normal); + + } + + if(hadCollision && !entity->IsPed()) + DMAudio.ReportCollision(this, entity, SURFACE_CAR_PANEL, SURFACE_TARMAC, 50.0f, 0.09f); + m_fElasticity = savedElasticity; + } + } + return false; +} + + float fBurstSpeedMax = 0.3f; -float fBurstTyreMod = 0.1f; +float fBurstTyreMod = 0.13f; void CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, @@ -527,7 +809,11 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon if(!bBraking){ if(m_fGasPedal < 0.01f){ - if(GetModelIndex() == MI_RCBANDIT) + if(IsBike()) + brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); + else if(pHandling->fMass < 500.0f) + brake = mod_HandlingManager.fWheelFriction / m_fMass; + else if(GetModelIndex() == MI_RCBANDIT) brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; else brake = mod_HandlingManager.fWheelFriction / m_fMass; @@ -567,15 +853,197 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon } if(fwd != 0.0f || right != 0.0f){ + CVector totalSpeed = fwd*wheelFwd + right*wheelRight; + + CVector turnDirection = totalSpeed; + bool separateTurnForce = false; // BUG: not initialized on PC + if(pHandling->fSuspensionAntidiveMultiplier > 0.0f){ + if(bBraking){ + separateTurnForce = true; + turnDirection = totalSpeed - pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; + }else if(bDriving){ + separateTurnForce = true; + turnDirection = totalSpeed - 0.5f*pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; + } + } + + CVector direction = totalSpeed; + + float speed = totalSpeed.Magnitude(); + float turnSpeed; + if(separateTurnForce) + turnSpeed = turnDirection.Magnitude(); + else + turnSpeed = speed; + direction.Normalise(); + if(separateTurnForce) + turnDirection.Normalise(); + else + turnDirection = direction; + + float impulse = speed*m_fMass; + float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection); + + ApplyMoveForce(impulse * direction); + ApplyTurnForce(turnImpulse * direction, wheelContactPoint); + } +} + +float fBurstBikeSpeedMax = 0.12f; +float fBurstBikeTyreMod = 0.05f; +float fTweakBikeWheelTurnForce = 2.0f; + +void +CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus) +{ + // BUG: using statics here is probably a bad idea + static bool bAlreadySkidding = false; // this is never reset + static bool bBraking; + static bool bDriving; + static bool bReversing; + +#ifdef FIX_BUGS + bAlreadySkidding = false; +#endif + + // how much force we want to apply in these axes + float fwd = 0.0f; + float right = 0.0f; + + bBraking = brake != 0.0f; + if(bBraking) + thrust = 0.0f; + bDriving = thrust != 0.0f; + bReversing = thrust < 0.0f; + + float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); + float contactSpeedRight; + + if(*wheelState != WHEEL_STATE_NORMAL) + bAlreadySkidding = true; + *wheelState = WHEEL_STATE_NORMAL; + + adhesion *= CTimer::GetTimeStep(); + if(bAlreadySkidding) + adhesion *= pHandling->fTractionLoss; + + if(special == BIKE_WHEELSPEC_2 || special == BIKE_WHEELSPEC_3) + contactSpeedRight = 0.0f; + else + contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); + + // moving sideways + if(contactSpeedRight != 0.0f){ + // exert opposing force + right = -contactSpeedRight/wheelsOnGround; +#ifdef FIX_BUGS + // contactSpeedRight is independent of framerate but right has timestep as a factor + // so we probably have to fix this + right *= CTimer::GetTimeStepFix(); +#endif + + if(wheelStatus == WHEEL_STATUS_BURST){ + float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax); + right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod); + } + } + + if(bDriving){ + fwd = thrust; + + // limit sideways force (why?) + if(right > 0.0f){ + if(right > adhesion) + right = adhesion; + }else{ + if(right < -adhesion) + right = -adhesion; + } + }else if(contactSpeedFwd != 0.0f){ + fwd = -contactSpeedFwd/wheelsOnGround; +#ifdef FIX_BUGS + // contactSpeedFwd is independent of framerate but fwd has timestep as a factor + // so we probably have to fix this + fwd *= CTimer::GetTimeStepFix(); +#endif + + if(!bBraking){ + if(m_fGasPedal < 0.01f){ + if(IsBike()) + brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); + else if(pHandling->fMass < 500.0f) + brake = mod_HandlingManager.fWheelFriction / m_fMass; + else if(GetModelIndex() == MI_RCBANDIT) + brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; + else + brake = mod_HandlingManager.fWheelFriction / m_fMass; +#ifdef FIX_BUGS + brake *= CTimer::GetTimeStepFix(); +#endif + } + } + + if(brake > adhesion){ + if(Abs(contactSpeedFwd) > 0.005f) + *wheelState = WHEEL_STATE_FIXED; + }else { + if(fwd > 0.0f){ + if(fwd > brake) + fwd = brake; + }else{ + if(fwd < -brake) + fwd = -brake; + } + } + } + + float speedSq = sq(right) + sq(fwd); + if(sq(adhesion) < speedSq){ + if(*wheelState != WHEEL_STATE_FIXED){ + if(bDriving && contactSpeedFwd < 0.2f) + *wheelState = WHEEL_STATE_SPINNING; + else + *wheelState = WHEEL_STATE_SKIDDING; + } + + float l = Sqrt(speedSq); + float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; + right *= adhesion * tractionLoss / l; + fwd *= adhesion * tractionLoss / l; + + if(destabTraction < 1.0f) + right *= destabTraction; + }else if(destabTraction < 1.0f){ + if(!bAlreadySkidding) + destabTraction *= pHandling->fTractionLoss; + if(sq(adhesion*destabTraction) < speedSq){ + float l = Sqrt(speedSq); + right *= adhesion * destabTraction / l; + } + } + + if(fwd != 0.0f || right != 0.0f){ CVector direction = fwd*wheelFwd + right*wheelRight; + float speed = direction.Magnitude(); direction.Normalise(); float impulse = speed*m_fMass; float turnImpulse = speed*GetMass(wheelContactPoint, direction); + CVector vTurnImpulse = turnImpulse * direction; ApplyMoveForce(impulse * direction); - ApplyTurnForce(turnImpulse * direction, wheelContactPoint); + + float turnRight = DotProduct(vTurnImpulse, GetRight()); + float contactRight = DotProduct(wheelContactPoint, GetRight()); + float contactFwd = DotProduct(wheelContactPoint, GetForward()); + + if(wheelId != BIKEWHEEL_REAR || !bBraking && !bReversing) + ApplyTurnForce((vTurnImpulse - turnRight*GetRight()) * fTweakBikeWheelTurnForce, + wheelContactPoint - contactRight*GetRight()); + + ApplyTurnForce(turnRight*GetRight(), contactFwd*GetForward()); } } @@ -597,35 +1065,81 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec return angularVelocity * CTimer::GetTimeStep(); } +int +CVehicle::FindTyreNearestPoint(float x, float y) +{ + CVector pos = CVector(x - GetPosition().x, y - GetPosition().y, 0.0f); + float fwd = DotProduct(GetForward(), pos); + float right = DotProduct(GetRight(), pos); + + int piece; + if(IsBike()){ + piece = fwd > 0.0f ? CAR_PIECE_WHEEL_LF : CAR_PIECE_WHEEL_LR; + }else{ + piece = fwd > 0.0f ? + right > 0.0f ? CAR_PIECE_WHEEL_RF : CAR_PIECE_WHEEL_LF : + right > 0.0f ? CAR_PIECE_WHEEL_RR : CAR_PIECE_WHEEL_LR; + } + return piece - CAR_PIECE_WHEEL_LF; +} + void -CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage) +CVehicle::InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos) { if (!bCanBeDamaged) return; - if (bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle())) + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + damage *= 0.5f; + if (GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle())) return; + + if(damage > 10.0f && (damagedBy == FindPlayerPed() || damagedBy == FindPlayerVehicle()) && GetStatus() != STATUS_WRECKED){ + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 1.0f; + CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(5, 25); + } + bool bFrightensDriver = false; switch (weaponType) { case WEAPONTYPE_UNARMED: + case WEAPONTYPE_BRASSKNUCKLE: + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_KNIFE: case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + case WEAPONTYPE_CHAINSAW: if (bMeleeProof) return; break; case WEAPONTYPE_COLT45: - case WEAPONTYPE_UZI: + case WEAPONTYPE_PYTHON: case WEAPONTYPE_SHOTGUN: - case WEAPONTYPE_AK47: - case WEAPONTYPE_M16: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: - case WEAPONTYPE_TOTAL_INVENTORY_WEAPONS: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: case WEAPONTYPE_UZI_DRIVEBY: if (bBulletProof) return; bFrightensDriver = true; break; - case WEAPONTYPE_ROCKETLAUNCHER: - case WEAPONTYPE_MOLOTOV: case WEAPONTYPE_GRENADE: + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_ROCKET: case WEAPONTYPE_EXPLOSION: if (bExplosionProof) return; @@ -642,6 +1156,52 @@ CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage default: break; } + + if(bFrightensDriver && GetStatus() == STATUS_PLAYER && m_fHealth < 250.0f) + return; + + // Pop tires + if(damagedBy && damagedBy->IsPed() && (IsCar() || IsBike())){ + int accuracy = 0; + switch(weaponType){ + case WEAPONTYPE_COLT45: + accuracy = 10; + break; + case WEAPONTYPE_PYTHON: + if(!((CPed*)damagedBy)->IsPlayer()) + accuracy = 64; + break; + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_M60: + case WEAPONTYPE_HELICANNON: + accuracy = 25; + break; + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_UZI_DRIVEBY: + accuracy = 15; + break; + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + if(!((CPed*)damagedBy)->IsPlayer()) + accuracy = 15; + break; + } + + if(((CPed*)damagedBy)->IsPlayer() && (CCamera::m_bUseMouse3rdPerson || TheCamera.Using1stPersonWeaponMode())) + accuracy = 0; + + if(accuracy != 0 && !bTyresDontBurst && (CGeneral::GetRandomNumber()&0x7F) < accuracy){ + if(IsBike()) + BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, false); + else if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) + BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, true); + } + } + if (m_fHealth > 0.0f) { if (VehicleCreatedBy == RANDOM_VEHICLE && pDriver && (GetStatus() == STATUS_SIMPLE || GetStatus() == STATUS_PHYSICS) && @@ -654,24 +1214,41 @@ CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage } } m_nLastWeaponDamage = weaponType; + m_pLastDamageEntity = damagedBy; float oldHealth = m_fHealth; if (m_fHealth > damage) { m_fHealth -= damage; - if (VehicleCreatedBy == RANDOM_VEHICLE && - (m_fHealth < DAMAGE_HEALTH_TO_FLEE_ALWAYS || - bFrightensDriver && m_randomSeed > DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE)) { + if (VehicleCreatedBy == RANDOM_VEHICLE && !IsBoat()){ switch (GetStatus()) { case STATUS_SIMPLE: case STATUS_PHYSICS: - if (pDriver) { - SetStatus(STATUS_ABANDONED); - pDriver->bFleeAfterExitingCar = true; - pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); - } - for (int i = 0; i < m_nNumMaxPassengers; i++) { - if (pPassengers[i]) { - pPassengers[i]->bFleeAfterExitingCar = true; - pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + if(AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_PLOUGH_THROUGH || + CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.5f && AutoPilot.m_nCarMission == MISSION_CRUISE){ + // Drive away like a maniac + if(pDriver && pDriver->m_objective != OBJECTIVE_LEAVE_VEHICLE){ + if(AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH) + AutoPilot.m_nCruiseSpeed *= 1.5f; + AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + } + }else{ + // Leave vehicle + if (pDriver && pDriver->CharCreatedBy != MISSION_CHAR) { + SetStatus(STATUS_ABANDONED); + pDriver->bFleeAfterExitingCar = true; + pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + pDriver->Say(SOUND_PED_FLEE_SPRINT); + } + int time = 200; + for (int i = 0; i < m_nNumMaxPassengers; i++) { + if (pPassengers[i] && + pPassengers[i]->m_objective != OBJECTIVE_LEAVE_VEHICLE && + pPassengers[i]->CharCreatedBy != MISSION_CHAR) { + pPassengers[i]->bFleeAfterExitingCar = true; + pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + pPassengers[i]->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; + pPassengers[i]->Say(SOUND_PED_FLEE_SPRINT); + time += 200; + } } } break; @@ -713,56 +1290,98 @@ CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage void CVehicle::DoFixedMachineGuns(void) { - if(CPad::GetPad(0)->GetCarGunFired() && !bGunSwitchedOff){ - if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 150){ - CVector source, target; - float dx, dy, len; - - dx = GetForward().x; - dy = GetForward().y; - len = Sqrt(SQR(dx) + SQR(dy)); - if(len < 0.1f) len = 0.1f; - dx /= len; - dy /= len; - - m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); - - source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f); - target = source + CVector(dx, dy, 0.0f)*60.0f; - target += CVector( - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f, - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f, - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.02f); - CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); - FireOneInstantHitRound(&source, &target, 15); - - source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f); - target = source + CVector(dx, dy, 0.0f)*60.0f; - target += CVector( - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f, - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f, - ((CGeneral::GetRandomNumber()&0xFF)-128) * 0.02f); - CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); - FireOneInstantHitRound(&source, &target, 15); - - DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); - - m_nAmmoInClip--; - if(m_nAmmoInClip == 0){ + if(TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_FORWARD){ + if(CPad::GetPad(0)->GetCarGunFired() && !bGunSwitchedOff){ + FireFixedMachineGuns(); + }else{ + if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400) m_nAmmoInClip = 20; - m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400; - } } - }else{ - if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400) - m_nAmmoInClip = 20; } } void +CVehicle::FireFixedMachineGuns(void) +{ + if (CTimer::GetTimeInMilliseconds() <= m_nGunFiringTime + 150) + return; + CVector source, target; + float dx, dy, len; + + dx = GetForward().x; + dy = GetForward().y; + len = Sqrt(SQR(dx) + SQR(dy)); + if (len < 0.1f) len = 0.1f; + dx /= len; + dy /= len; + + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + + source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f); + target = source + CVector(dx, dy, 0.0f) * 60.0f; + target += CVector( + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); + CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); + FireOneInstantHitRound(&source, &target, 15); + + source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f); + target = source + CVector(dx, dy, 0.0f) * 60.0f; + target += CVector( + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); + CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); + FireOneInstantHitRound(&source, &target, 15); + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + + m_nAmmoInClip--; + if (m_nAmmoInClip == 0) { + m_nAmmoInClip = 20; + m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400; + } +} + +void +CVehicle::ActivateBomb(void) +{ + 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); + } +} + +void +CVehicle::ActivateBombWhenEntered(void) +{ + 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; + }else + bDriverLastFrame = false; +} + +void CVehicle::ExtinguishCarFire(void) { - m_fHealth = Max(m_fHealth, 300.0f); + if(GetStatus() != STATUS_WRECKED) + m_fHealth = Max(m_fHealth, 300.0f); if(m_pCarFire) m_pCarFire->Extinguish(); if(IsCar()){ @@ -833,6 +1452,69 @@ CVehicle::ShufflePassengersToMakeSpace(void) } void +CVehicle::MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop) +{ + int i; + player = nil; + cop = nil; + + if(ped1->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && + ((CPlayerPed*)ped1)->m_pWanted->m_nWantedLevel > 0 && + ped2->m_pedInObjective == ped1){ + player = (CPlayerPed*)ped1; + cop = (CCopPed*)ped2; + return; + } + + bool ped1IsDriver = ped1 == pDriver; + + // Just what the hell is this weird code? + CPed *peds[9]; + CPed *peds2[9]; + int numPeds = 0; + int numPeds2 = 0; + for(i = 0; i < m_nNumMaxPassengers; i++){ + CPed *p = pPassengers[i]; + if(p && p != ped1 && !p->bStayInCarOnJack){ + peds[numPeds++] = p; + // uhh what? + if(i < 1 && !ped1IsDriver) + continue; + peds2[numPeds2++] = p; + } + } + + // So we're copying this array for no reason... + CPed *peds3[9]; + int numPeds3 = 0; + for(i = 0; i < numPeds; i++){ + if(peds[i]->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && + ((CPlayerPed*)peds[i])->m_pWanted->m_nWantedLevel > 0 && + ped2->m_pedInObjective == peds[i]){ + player = (CPlayerPed*)peds[i]; + cop = (CCopPed*)ped2; + return; + } + peds3[numPeds3++] = peds[i]; + } + + int time = 1800; + for(i = 0; i < numPeds3; i++){ + peds3[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + time; + peds3[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + time += CGeneral::GetRandomNumberInRange(300.0f, 600.0f); + } + + if(IsCar() && numPeds2 > 0 && CGeneral::GetRandomTrueFalse()) + for(i = 0; i < numPeds2; i++) + if(peds2[i]->IsFemale() || CGeneral::GetRandomTrueFalse()){ + peds2[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 10000; + peds2[i]->b156_8 = true; + peds2[i]->bFleeAfterExitingCar = true; + } +} + +void CVehicle::ProcessDelayedExplosion(void) { if(m_nBombTimer == 0) @@ -851,8 +1533,6 @@ CVehicle::ProcessDelayedExplosion(void) if (m_nBombTimer != 0) return; - if(FindPlayerVehicle() != this && m_pBlowUpEntity == FindPlayerPed()) - CWorld::Players[CWorld::PlayerInFocus].AwardMoneyForExplosion(this); BlowUpCar(m_pBlowUpEntity); } @@ -860,12 +1540,13 @@ bool CVehicle::IsLawEnforcementVehicle(void) { switch(GetModelIndex()){ - case MI_FBICAR: case MI_POLICE: case MI_ENFORCER: case MI_PREDATOR: case MI_RHINO: case MI_BARRACKS: + case MI_FBIRANCH: + case MI_VICECHEE: return true; default: return false; @@ -883,6 +1564,8 @@ CVehicle::UsesSiren(uint32 id) case MI_POLICE: case MI_ENFORCER: case MI_PREDATOR: + case MI_FBIRANCH: + case MI_VICECHEE: return true; default: return false; @@ -894,24 +1577,7 @@ CVehicle::IsVehicleNormal(void) { if (!pDriver || m_nNumPassengers != 0 || GetStatus() == STATUS_WRECKED) return false; - switch (GetModelIndex()){ - case MI_FIRETRUCK: - case MI_AMBULAN: - case MI_TAXI: - case MI_POLICE: - case MI_ENFORCER: - case MI_BUS: - case MI_RHINO: - case MI_BARRACKS: - case MI_DODO: - case MI_COACH: - case MI_CABBIE: - case MI_RCBANDIT: - case MI_BORGNINE: - return false; - default: - return true; - } + return GetModelInfo()->m_vehicleClass != -1; } bool @@ -919,9 +1585,8 @@ CVehicle::CarHasRoof(void) { if((pHandling->Flags & HANDLING_HAS_NO_ROOF) == 0) return true; - if(m_aExtras[0] && m_aExtras[1]) - return false; - return true; + // component 0 is assumed to be a roof + return m_aExtras[0] == 0 || m_aExtras[1] == 0; } bool @@ -988,7 +1653,8 @@ CVehicle::CanPedOpenLocks(CPed *ped) { if(m_nDoorLock == CARLOCK_LOCKED || m_nDoorLock == CARLOCK_LOCKED_INITIALLY || - m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE) + m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE || + m_nDoorLock == CARLOCK_SKIP_SHUT_DOORS) return false; if(ped->IsPlayer() && m_nDoorLock == CARLOCK_LOCKOUT_PLAYER_ONLY) return false; @@ -996,10 +1662,18 @@ CVehicle::CanPedOpenLocks(CPed *ped) } bool +CVehicle::CanDoorsBeDamaged(void) +{ + return m_nDoorLock == CARLOCK_NOT_USED || + m_nDoorLock == CARLOCK_UNLOCKED || + m_nDoorLock == CARLOCK_SKIP_SHUT_DOORS; +} + +bool CVehicle::CanPedEnterCar(void) { // can't enter when car is on side - if(GetUp().z > 0.1f || GetUp().z < -0.1f){ + if(IsBike() || GetUp().z > 0.1f || GetUp().z < -0.1f){ // also when car is moving too fast if(m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f)) return false; @@ -1011,16 +1685,14 @@ CVehicle::CanPedEnterCar(void) } bool -CVehicle::CanPedExitCar(void) +CVehicle::CanPedExitCar(bool jumpExit) { CVector up = GetUp(); if(up.z > 0.1f || up.z < -0.1f){ -#ifdef VC_PED_PORTS if (IsBoat()) return true; -#endif // can't exit when car is moving too fast - if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f) + if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f && !jumpExit) return false; // if car is slow enough, check turn speed if(Abs(m_vecTurnSpeed.x) > 0.01f || @@ -1043,6 +1715,23 @@ CVehicle::CanPedExitCar(void) } } +bool +CVehicle::CanPedJumpOutCar(void) +{ + if(GetUp().z < 0.3f) + return false; + float speed = m_vecMoveSpeed.MagnitudeSqr(); + return speed < 0.1f || speed > 0.5f ? false : true; +} + +bool +CVehicle::CanPedJumpOffBike(void) +{ + if(pPassengers[0]) + return false; + return m_vecMoveSpeed.MagnitudeSqr() < 0.07f ? false : true; +} + void CVehicle::ChangeLawEnforcerState(uint8 enable) { @@ -1067,7 +1756,7 @@ CVehicle::SetUpDriver(void) if(VehicleCreatedBy != RANDOM_VEHICLE) return nil; - pDriver = CPopulation::AddPedInCar(this); + pDriver = CPopulation::AddPedInCar(this, true); pDriver->m_pMyVehicle = this; pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle); pDriver->bInVehicle = true; @@ -1080,15 +1769,31 @@ CVehicle::SetUpDriver(void) CPed* CVehicle::SetupPassenger(int n) { + int i; + if(pPassengers[n]) return pPassengers[n]; - pPassengers[n] = CPopulation::AddPedInCar(this); - pPassengers[n]->m_pMyVehicle = this; - pPassengers[n]->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle); - pPassengers[n]->bInVehicle = true; - pPassengers[n]->SetPedState(PED_DRIVING); - if(bIsBus) + if((IsTaxi() || IsLimo()) && n == 0) + pPassengers[0] = nil; + else{ + CPed *passenger = CPopulation::AddPedInCar(this, false); + pPassengers[n] = passenger; + passenger->m_pMyVehicle = this; + passenger->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle); + passenger->bInVehicle = true; + passenger->SetPedState(PED_DRIVING); + + if(passenger->m_nPedType == PEDTYPE_CIVMALE || passenger->m_nPedType == PEDTYPE_CIVFEMALE) + for(i = 0; i < n; i++) + if(pPassengers[i] && pPassengers[n] && + (pPassengers[i]->m_nPedType == PEDTYPE_CIVMALE || pPassengers[i]->m_nPedType == PEDTYPE_CIVFEMALE) && + passenger->GetModelIndex() == pPassengers[i]->GetModelIndex()){ + pPassengers[n] = nil; + CPopulation::RemovePed(passenger); + } + } + if(bIsBus && pPassengers[n]) pPassengers[n]->bRenderPedInCar = false; ++m_nNumPassengers; return pPassengers[n]; @@ -1101,23 +1806,42 @@ CVehicle::SetDriver(CPed *driver) pDriver->RegisterReference((CEntity**)&pDriver); if(bFreebies && driver == FindPlayerPed()){ - if(GetModelIndex() == MI_AMBULAN) - FindPlayerPed()->m_fHealth = Min(FindPlayerPed()->m_fHealth + 20.0f, 100.0f); - else if(GetModelIndex() == MI_TAXI) - CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; - else if(GetModelIndex() == MI_POLICE) - driver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5); - else if(GetModelIndex() == MI_ENFORCER) - driver->m_fArmour = Max(driver->m_fArmour, 100.0f); - else if(GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE) - CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; bFreebies = false; + switch(GetModelIndex()){ + case MI_AMBULAN: + FindPlayerPed()->m_fHealth = Max(FindPlayerPed()->m_fHealth, Min(FindPlayerPed()->m_fHealth + 20.0f, CWorld::Players[0].m_nMaxHealth)); + break; + + case MI_TAXI: + case MI_CABBIE: + case MI_ZEBRA: + case MI_KAUFMAN: + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 12; + break; + + case MI_POLICE: + CStreaming::RequestModel(MI_SHOTGUN, STREAMFLAGS_DONT_REMOVE); + bFreebies = true; + break; + + case MI_ENFORCER: + driver->m_fArmour = Max(driver->m_fArmour, CWorld::Players[0].m_nMaxArmour); + break; + + case MI_CADDY: + if(!(driver->IsPlayer() && ((CPlayerPed*)driver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true))) + CStreaming::RequestModel(MI_GOLFCLUB, STREAMFLAGS_DONT_REMOVE); + break; + } } - ApplyTurnForce(0.0f, 0.0f, -0.2f*driver->m_fMass, - driver->GetPosition().x - GetPosition().x, - driver->GetPosition().y - GetPosition().y, - 0.0f); + if(IsBike()) + ApplyMoveForce(-0.2f*driver->m_fMass * GetUp()); + else + ApplyTurnForce(0.0f, 0.0f, -0.2f*driver->m_fMass, + driver->GetPosition().x - GetPosition().x, + driver->GetPosition().y - GetPosition().y, + 0.0f); } bool @@ -1125,10 +1849,13 @@ CVehicle::AddPassenger(CPed *passenger) { int i; - ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, - passenger->GetPosition().x - GetPosition().x, - passenger->GetPosition().y - GetPosition().y, - 0.0f); + if(IsBike()) + ApplyTurnForce(-0.2f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); + else + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); for(i = 0; i < m_nNumMaxPassengers; i++) if(pPassengers[i] == nil){ @@ -1145,10 +1872,13 @@ CVehicle::AddPassenger(CPed *passenger, uint8 n) if(bIsBus) return AddPassenger(passenger); - ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, - passenger->GetPosition().x - GetPosition().x, - passenger->GetPosition().y - GetPosition().y, - 0.0f); + if(IsBike()) + ApplyTurnForce(-0.2f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); + else + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); if(n < m_nNumMaxPassengers && pPassengers[n] == nil){ pPassengers[n] = passenger; @@ -1162,6 +1892,22 @@ void CVehicle::RemoveDriver(void) { SetStatus(STATUS_ABANDONED); + if(pDriver == FindPlayerPed()){ + if(GetModelIndex() == MI_POLICE && CStreaming::HasModelLoaded(MI_SHOTGUN)){ + if(bFreebies){ + if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_SHOTGUN, true)) + pDriver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5, true); + else + pDriver->GrantAmmo(WEAPONTYPE_SHOTGUN, 5); + bFreebies = false; + } + CStreaming::SetModelIsDeletable(MI_SHOTGUN); + }else if(GetModelIndex() == MI_CADDY && CStreaming::HasModelLoaded(MI_GOLFCLUB)){ + if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true)) + pDriver->GiveWeapon(WEAPONTYPE_GOLFCLUB, 1, true); + CStreaming::SetModelIsDeletable(MI_GOLFCLUB); + } + } pDriver = nil; } @@ -1187,6 +1933,57 @@ CVehicle::RemovePassenger(CPed *p) } } +bool +CVehicle::IsDriver(CPed *ped) +{ + if(ped == nil) + return false; + return ped == pDriver; +} + +bool +CVehicle::IsDriver(int32 model) +{ + return pDriver && pDriver->GetModelIndex() == model; +} + +bool +CVehicle::IsPassenger(CPed *ped) +{ + int i; + if(ped == nil) + return false; + for(i = 0; i < 8; i++) + if(pPassengers[i] == ped) + return true; + return false; +} + +bool +CVehicle::IsPassenger(int32 model) +{ + int i; + for(i = 0; i < 8; i++) + if(pPassengers[i] && pPassengers[i]->GetModelIndex() == model) + return true; + return false; +} + +void +CVehicle::UpdatePassengerList(void) +{ + int i; + bool hasPassenger = false; + if(m_nNumPassengers) + for(i = 0; i < 8; i++) + if(pPassengers[i]){ + hasPassenger = true; + break; + } + if(!hasPassenger) + m_nNumPassengers = 0; +} + void CVehicle::ProcessCarAlarm(void) { @@ -1196,9 +1993,10 @@ CVehicle::ProcessCarAlarm(void) return; step = CTimer::GetTimeStepInMilliseconds(); - if((uint16)m_nAlarmState < step) + if((uint16)m_nAlarmState < step){ m_nAlarmState = 0; - else + m_nCarHornTimer = 0; + }else m_nAlarmState -= step; } @@ -1226,6 +2024,287 @@ CVehicle::IsSphereTouchingVehicle(float sx, float sy, float sz, float radius) return true; } +RpMaterial* +SetCompAlphaCB(RpMaterial *material, void *data) +{ + uint32 alpha = (uint32)(uintptr)data; + RwRGBA *col = (RwRGBA*)RpMaterialGetColor(material); // get rid of const + col->alpha = alpha; + return material; +} + +void +CVehicle::SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha) +{ + RpGeometry *geo = RpAtomicGetGeometry(atomic); + RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geo, SetCompAlphaCB, (void*)alpha); +} + +void +CVehicle::UpdateClumpAlpha(void) +{ + 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); +} + +void +CVehicle::HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd) +{ + int i; + float angle; + CColPoint point; + CEntity *entity; + uint8 r, g, b; + + if(heli == nil) + return; + + uint8 surface = SURFACE_TARMAC; + int frm = CTimer::GetFrameCounter() & 7; + float testLowZ = ground - 10.0f; + float dustSize = 0.0f; + float baseSize = 1.0f; + float offset = 1.0f; // when heli is tilted + float particleZ = -101.0f; + int n = 0; + + if(heli->GetModelIndex() == MI_RCGOBLIN || heli->GetModelIndex() == MI_RCRAIDER){ + radius = 3.0f; + dustSize = 0.04f; + baseSize = 0.07f; + offset = 0.3f; + } + + CVector heliPos = heli->GetPosition(); + + if(heli->IsVehicle() && ((CVehicle*)heli)->IsCar()){ + heliPos.x -= (heliPos.z - ground)*heli->GetUp().x*offset*0.5f; + heliPos.y -= (heliPos.z - ground)*heli->GetUp().y*offset*0.5f; + } + + float steamSize = 0.25f * radius * baseSize; + float splashSize = 0.3f * radius * baseSize; + + i = 0; + for(i = 0; i < 32+rnd; i++){ + angle = i * TWOPI/32.0f; + CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f); + CVector dir = CVector(pos.x, pos.y, 1.0f)*0.01f; + pos += heliPos; + + if(i < 32 && i == 4*frm){ + if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil)){ + n = rnd; + particleZ = point.point.z; + surface = point.surfaceB; + }else + n = 0; + + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos, &waterLevel, false) && waterLevel > particleZ){ + surface = SURFACE_WATER; + n = rnd; + particleZ = waterLevel; + } + } + + if(n){ + pos.z = particleZ; + if(surface == SURFACE_WATER){ + float red = (0.3*CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*255.0f/4.0f; + float green = (0.3*CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*255.0f/4.0f; + float blue = (0.3*CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*255.0f/4.0f; + r = clamp(red, 0.0f, 255.0f); + g = clamp(green, 0.0f, 255.0f); + b = clamp(blue, 0.0f, 255.0f); + RwRGBA col1 = { r, g, b, CGeneral::GetRandomNumberInRange(8, 32) }; + RwRGBA col2 = { 255, 255, 255, 32 }; + + if(n&1) + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, pos, dir, nil, steamSize, col2); + else + CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, dir, nil, splashSize, col1, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1); + }else{ + switch(surface){ + default: + case SURFACE_TARMAC: + r = 10; + g = 10; + b = 10; + break; + case SURFACE_GRASS: + r = 10; + g = 10; + b = 3; + break; + case SURFACE_GRAVEL: + r = 10; + g = 8; + b = 7; + break; + case SURFACE_MUD_DRY: + r = 10; + g = 6; + b = 3; + break; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + r = 10; + g = 10; + b = 7; + break; + } + RwRGBA col = { r, g, b, 32 }; + if(heliPos.z - pos.z < 20.0f) + CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, dustSize, col); + } + + n--; + } + } +} + +#define GLARE_MIN_DIST (13.0f) +#define GLARE_FULL_DIST (30.0f) +#define GLARE_MIN_ANGLE (0.99f) +#define GLARE_FULL_ANGLE (0.995f) + +void +CVehicle::DoSunGlare(void) +{ + if(bRenderScorched || GetPosition().z < 0.0f || + GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR || CWeather::SunGlare <= 0.0f) + return; + + CVector camDir = TheCamera.GetPosition() - GetPosition(); + float dist = camDir.Magnitude(); + camDir *= 2.0f/dist; + CVector glareVec = camDir + CTimeCycle::GetSunDirection(); + CVector localGlareVec; + localGlareVec.x = DotProduct(glareVec, GetRight()); + localGlareVec.y = DotProduct(glareVec, GetForward()); + localGlareVec.z = 0.0; + localGlareVec.Normalise(); + + CVector2D fwd2D = GetForward(); + fwd2D.Normalise(); + CVector2D camDir2D = camDir; + camDir2D.Normalise(); + float fwdness = Abs(DotProduct2D(fwd2D, camDir2D)); + + // check angle + float strength; + if(fwdness > GLARE_FULL_ANGLE) + strength = 1.0f; + else if(fwdness > GLARE_MIN_ANGLE) + strength = (fwdness - GLARE_MIN_ANGLE)/(GLARE_FULL_ANGLE-GLARE_MIN_ANGLE); + else + return; + // check distance + if(dist > GLARE_FULL_DIST){ + // no max distance + }else if(dist > GLARE_MIN_DIST) + strength *= (dist - GLARE_MIN_DIST)/(GLARE_FULL_DIST - GLARE_MIN_DIST); + else + return; + + float intens = 0.8f * strength * CWeather::SunGlare; + int r = intens * (CTimeCycle::GetSunCoreRed() + 2*255)/3.0f; + int g = intens * (CTimeCycle::GetSunCoreGreen() + 2*255)/3.0f; + int b = intens * (CTimeCycle::GetSunCoreBlue() + 2*255)/3.0f; + + CColModel *colmodel = GetColModel(); + CCollision::CalculateTrianglePlanes(colmodel); + + int i; + for(i = 0; i < colmodel->numTriangles-2; i += 2){ + int a1 = colmodel->triangles[i].a; + int b1 = colmodel->triangles[i].b; + int c1 = colmodel->triangles[i].c; + int a2 = colmodel->triangles[i+1].a; + int b2 = colmodel->triangles[i+1].b; + int c2 = colmodel->triangles[i+1].c; + CVector vert1 = colmodel->vertices[a1]; + CVector vert4; + // Need an upward surface + if(vert1.z <= 0.0f) + continue; + + // trying to find a quad here + int numTri2Verts = 0; + if(a2 != a1 && a2 != b1 && a2 != c1){ + // a2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[a2]; + } + if(b2 != a1 && b2 != b1 && b2 != c1){ + // b2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[b2]; + } + if(c2 != a1 && c2 != b1 && c2 != c1){ + // c2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[c2]; + } + // Need exactly one vertex from tri2 for a quad with tri1 + if(numTri2Verts != 1) + continue; + + CVector mid = (vert1 + colmodel->vertices[b1] + colmodel->vertices[c1] + vert4)/4.0f; + float dy = mid.y - vert1.y; + float dx = mid.x - vert1.x; + float dist = 1.4f * Min(Abs(dx), Abs(dy)); + if(dist > 0.6f){ + CVector pos = GetMatrix() * (dist * localGlareVec + mid) + camDir; + CCoronas::RegisterCorona((uintptr)this + 27 + i, + r, g, b, 255, + pos, 0.9f*CWeather::SunGlare, 90.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, + CCoronas::STREAK_OFF, 0.0f); + } + } +} + +void +CVehicle::KillPedsInVehicle(void) +{ + int i; + if(pDriver){ + CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); + if(pDriver->GetPedState() == PED_DRIVING){ + pDriver->SetDead(); + if(!pDriver->IsPlayer()) + pDriver->FlagToDestroyWhenNextProcessed(); + }else + pDriver->SetDie(); + } + 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(); + } + } +} + void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle) { @@ -1367,3 +2446,35 @@ CVehicle::Load(uint8*& buf) SkipSaveBuf(buf, 99); } #endif + +eVehicleAppearance +CVehicle::GetVehicleAppearance(void) +{ + uint32 flags = pHandling->Flags & 0xF0000; + if (flags == 0) + return VEHICLE_APPEARANCE_CAR; + if (flags == HANDLING_IS_BIKE) + return VEHICLE_APPEARANCE_BIKE; + if (flags == HANDLING_IS_HELI) + return VEHICLE_APPEARANCE_HELI; + if (flags == HANDLING_IS_PLANE) + return VEHICLE_APPEARANCE_PLANE; + if (flags == HANDLING_IS_BOAT) + return VEHICLE_APPEARANCE_BOAT; + return VEHICLE_APPEARANCE_NONE; +} + +bool +IsVehiclePointerValid(CVehicle* pVehicle) +{ + if (!pVehicle) + return false; + int index = CPools::GetVehiclePool()->GetJustIndex(pVehicle); +#ifdef FIX_BUGS + if (index < 0 || index >= NUMVEHICLES) +#else + if (index < 0 || index > NUMVEHICLES) +#endif + return false; + return pVehicle->m_vehType == VEHICLE_TYPE_PLANE || pVehicle->m_entryInfoList.first; +} diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h index 48546e68..bc14bc77 100644 --- a/src/vehicles/Vehicle.h +++ b/src/vehicles/Vehicle.h @@ -5,8 +5,11 @@ #include "ModelIndices.h" #include "AnimManager.h" #include "Weapon.h" +#include "HandlingMgr.h" class CPed; +class CPlayerPed; +class CCopPed; class CFire; struct tHandlingData; @@ -28,6 +31,16 @@ enum eCarLock { CARLOCK_SKIP_SHUT_DOORS }; +enum eBombType +{ + CARBOMB_NONE, + CARBOMB_TIMED, + CARBOMB_ONIGNITION, + CARBOMB_REMOTE, + CARBOMB_TIMEDACTIVE, + CARBOMB_ONIGNITIONACTIVE, +}; + enum eDoors { DOOR_BONNET = 0, @@ -97,23 +110,54 @@ enum tWheelState enum eFlightModel { FLIGHT_MODEL_DODO, - // not used in III FLIGHT_MODEL_RCPLANE, - FLIGHT_MODEL_HELI, - FLIGHT_MODEL_SEAPLANE + FLIGHT_MODEL_RCHELI, + FLIGHT_MODEL_SEAPLANE, + FLIGHT_MODEL_PLANE_UNUSED, + FLIGHT_MODEL_PLANE, + FLIGHT_MODEL_HELI +}; + +enum eVehicleAppearance +{ + VEHICLE_APPEARANCE_NONE, + VEHICLE_APPEARANCE_CAR, + VEHICLE_APPEARANCE_BIKE, + VEHICLE_APPEARANCE_HELI, + VEHICLE_APPEARANCE_BOAT, + VEHICLE_APPEARANCE_PLANE, +}; + +// TODO: what is this even? +enum eBikeWheelSpecial +{ + BIKE_WHEELSPEC_0, // both wheels on ground + BIKE_WHEELSPEC_1, // rear wheel on ground + BIKE_WHEELSPEC_2, // only front wheel on ground + BIKE_WHEELSPEC_3, // can't happen +}; + +enum +{ + ROTOR_TOP = 3, + ROTOR_FRONT = 4, + ROTOR_RIGHT = 5, + ROTOR_LEFT = 7, + ROTOR_BACK = 8, + ROTOR_BOTTOM = 9, }; class CVehicle : public CPhysical { public: - // 0x128 tHandlingData *pHandling; + tFlyingHandlingData *pFlyingHandling; CAutoPilot AutoPilot; uint8 m_currentColour1; uint8 m_currentColour2; uint8 m_aExtras[2]; int16 m_nAlarmState; - int16 m_nMissionValue; + int16 m_nRouteSeed; CPed *pDriver; CPed *pPassengers[8]; uint8 m_nNumPassengers; @@ -162,15 +206,29 @@ public: uint8 bVehicleColProcessed : 1;// Has ProcessEntityCollision been processed for this car? uint8 bIsCarParkVehicle : 1; // Car has been created using the special CAR_PARK script command uint8 bHasAlreadyBeenRecorded : 1; // Used for replays + uint8 bPartOfConvoy : 1; + uint8 bHeliMinimumTilt : 1; // This heli should have almost no tilt really + uint8 bAudioChangingGear : 1; // sounds like vehicle is changing gear + + uint8 bIsDrowning : 1; // is vehicle occupants taking damage in water (i.e. vehicle is dead in water) + uint8 bTyresDontBurst : 1; // If this is set the tyres are invincible + uint8 bCreatedAsPoliceVehicle : 1;// True if this guy was created as a police vehicle (enforcer, policecar, miamivice car etc) + uint8 bRestingOnPhysical : 1; // Dont go static cause car is sitting on a physical object that might get removed + uint8 bParking : 1; + uint8 bCanPark : 1; + + uint8 m_bombType : 3; + uint8 bDriverLastFrame : 1; int8 m_numPedsUseItAsCover; uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default) int8 m_nPacManPickupsCarried; uint8 m_nRoadblockType; - int16 m_nRoadblockNode; float m_fHealth; // 1000.0f = full health. 250.0f = fire. 0 -> explode uint8 m_nCurrentGear; float m_fChangeGearTime; + CEntity* m_pBombRigger; + uint32 m_nSetPieceExtendedRangeTime; uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats) uint32 m_nTimeOfDeath; uint16 m_nTimeBlocked; @@ -180,12 +238,14 @@ public: float m_fMapObjectHeightBehind; // rear Z? eCarLock m_nDoorLock; int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage + CEntity *m_pLastDamageEntity; int8 m_nRadioStation; uint8 m_bRainAudioCounter; uint8 m_bRainSamplesCounter; - uint8 m_nCarHornTimer; - uint8 m_nCarHornPattern; // last horn? + uint32 m_nCarHornTimer; + uint8 m_nCarHornPattern; bool m_bSirenOrAlarm; + uint8 m_nCarHornDelay; int8 m_comedyControlState; CStoredCollPoly m_aCollPolys[2]; // poly which is under front/rear part of car float m_fSteerInput; @@ -215,11 +275,15 @@ public: virtual bool IsDoorFullyOpen(eDoors door) { return false; } virtual bool IsDoorClosed(eDoors door) { return false; } virtual bool IsDoorMissing(eDoors door) { return false; } + virtual bool IsDoorReady(uint32 door) { return false; } + virtual bool IsDoorMissing(uint32 door) { return false; } + virtual bool IsOpenTopCar(void) { return false; } virtual void RemoveRefsToVehicle(CEntity *ent) {} virtual void BlowUpCar(CEntity *ent) {} virtual bool SetUpWheelColModel(CColModel *colModel) { return false; } - virtual void BurstTyre(uint8 tyre) {} - virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false;} + virtual void BurstTyre(uint8 tyre, bool applyForces) {} + virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false; } + virtual bool IsClearToDriveAway(void); virtual float GetHeightAboveRoad(void); virtual void PlayCarHorn(void) {} #ifdef COMPATIBLE_SAVES @@ -227,6 +291,7 @@ public: virtual void Load(uint8*& buf); #endif + eVehicleAppearance GetVehicleAppearance(void); bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; } bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; } bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } @@ -235,11 +300,17 @@ public: bool IsBike(void) { return m_vehType == VEHICLE_TYPE_BIKE; } void FlyingControl(eFlightModel flightModel); + bool DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult); + bool BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult); + void ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus); + void ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus); void ExtinguishCarFire(void); void ProcessDelayedExplosion(void); float ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius); + int FindTyreNearestPoint(float x, float y); bool IsLawEnforcementVehicle(void); void ChangeLawEnforcerState(uint8 enable); bool UsesSiren(uint32 id); @@ -249,8 +320,11 @@ public: bool IsOnItsSide(void); bool CanBeDeleted(void); bool CanPedOpenLocks(CPed *ped); + bool CanDoorsBeDamaged(void); bool CanPedEnterCar(void); - bool CanPedExitCar(void); + bool CanPedExitCar(bool jumpExit); + bool CanPedJumpOutCar(void); + bool CanPedJumpOffBike(void); // do these two actually return something? CPed *SetUpDriver(void); CPed *SetupPassenger(int n); @@ -259,20 +333,34 @@ public: bool AddPassenger(CPed *passenger, uint8 n); void RemovePassenger(CPed *passenger); void RemoveDriver(void); + bool IsDriver(CPed *ped); + bool IsDriver(int32 model); + bool IsPassenger(CPed *ped); + bool IsPassenger(int32 model); + void UpdatePassengerList(void); void ProcessCarAlarm(void); bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius); bool ShufflePassengersToMakeSpace(void); - void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage); + void MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop); + void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos = CVector(0.0f, 0.0f, 0.0f)); void DoFixedMachineGuns(void); + void FireFixedMachineGuns(void); + void ActivateBomb(void); + void ActivateBombWhenEntered(void); + void KillPedsInVehicle(void); + + void SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha); + void UpdateClumpAlpha(void); + + static void HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd); + void DoSunGlare(void); -#ifdef FIX_BUGS bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1 && GetStatus() != STATUS_WRECKED; } -#else - bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; } -#endif CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); } - bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE; } - AnimationId GetDriverAnim(void) { return IsCar() && bLowVehicle ? ANIM_CAR_LSIT : (IsBoat() && GetModelIndex() != MI_SPEEDER ? ANIM_DRIVE_BOAT : ANIM_CAR_SIT); } + bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_ZEBRA || GetModelIndex() == MI_KAUFMAN; } + bool IsLimo(void) { return GetModelIndex() == MI_STRETCH || GetModelIndex() == MI_LOVEFIST; } + bool IsRealHeli(void) { return !!(pHandling->Flags & HANDLING_IS_HELI); } + bool IsRealPlane(void) { return !!(pHandling->Flags & HANDLING_IS_PLANE); } static bool bWheelsOnlyCheat; static bool bAllDodosCheat; @@ -282,9 +370,12 @@ public: #ifdef ALT_DODO_CHEAT static bool bAltDodoCheat; #endif + static bool bHoverCheat; + static bool bAllTaxisHaveNitro; static bool m_bDisableMouseSteering; + static bool bDisableRemoteDetonation; + static bool bDisableRemoteDetonationOnContact; }; -VALIDATE_SIZE(CVehicle, 0x288); - void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle); +bool IsVehiclePointerValid(CVehicle* pVehicle); |