diff options
Diffstat (limited to 'src/vehicles')
-rw-r--r-- | src/vehicles/Automobile.cpp | 2326 | ||||
-rw-r--r-- | src/vehicles/Automobile.h | 92 | ||||
-rw-r--r-- | src/vehicles/Bike.cpp | 2925 | ||||
-rw-r--r-- | src/vehicles/Bike.h | 138 | ||||
-rw-r--r-- | src/vehicles/Boat.cpp | 1001 | ||||
-rw-r--r-- | src/vehicles/Boat.h | 32 | ||||
-rw-r--r-- | src/vehicles/CarGen.cpp | 142 | ||||
-rw-r--r-- | src/vehicles/CarGen.h | 7 | ||||
-rw-r--r-- | src/vehicles/Cranes.cpp | 18 | ||||
-rw-r--r-- | src/vehicles/DamageManager.cpp | 20 | ||||
-rw-r--r-- | src/vehicles/Floater.cpp | 173 | ||||
-rw-r--r-- | src/vehicles/Floater.h | 2 | ||||
-rw-r--r-- | src/vehicles/HandlingMgr.cpp | 312 | ||||
-rw-r--r-- | src/vehicles/HandlingMgr.h | 160 | ||||
-rw-r--r-- | src/vehicles/Heli.cpp | 200 | ||||
-rw-r--r-- | src/vehicles/Heli.h | 12 | ||||
-rw-r--r-- | src/vehicles/Plane.cpp | 244 | ||||
-rw-r--r-- | src/vehicles/Plane.h | 16 | ||||
-rw-r--r-- | src/vehicles/Train.cpp | 26 | ||||
-rw-r--r-- | src/vehicles/Train.h | 2 | ||||
-rw-r--r-- | src/vehicles/Vehicle.cpp | 1625 | ||||
-rw-r--r-- | src/vehicles/Vehicle.h | 206 |
22 files changed, 8093 insertions, 1586 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index d2fedbd5..44e0b044 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_fPlaneSteer = 0.0f; + m_nBusDoorTimerEnd = 0; m_nBusDoorTimerStart = 0; @@ -139,6 +166,9 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fGasPedalAudio = 0.0f; bNotDamagedUpsideDown = false; bMoreResistantToDamage = false; + bTankDetonateCars = true; + bStuckInSand = false; + bHeliDestroyed = false; m_fVelocityChangeForAudio = 0.0f; m_hydraulicState = 0; @@ -150,7 +180,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_aWheelRotation[i] = 0.0f; m_aWheelSpeed[i] = 0.0f; m_aWheelState[i] = WHEEL_STATE_NORMAL; - m_aWheelSkidmarkMuddy[i] = false; + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; m_aWheelSkidmarkBloody[i] = false; } @@ -159,6 +189,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_nDriveWheelsOnGroundPrev = 0; m_fHeightAboveRoad = 0.0f; m_fTraction = 1.0f; + m_fTireTemperature = 1.0f; CColModel *colModel = mi->GetColModel(); if(colModel->lines == nil){ @@ -173,10 +204,6 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_nNumPassengers = 0; - m_bombType = CARBOMB_NONE; - bDriverLastFrame = false; - m_pBombRigger = nil; - if(m_nDoorLock == CARLOCK_UNLOCKED && (id == MI_POLICE || id == MI_ENFORCER || id == MI_RHINO)) m_nDoorLock = CARLOCK_LOCKED_INITIALLY; @@ -184,6 +211,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fCarGunLR = 0.0f; m_fCarGunUD = 0.05f; m_fPropellerRotation = 0.0f; + m_fHeliOrientation = -1.0f; m_weaponDoorTimerLeft = 0.0f; m_weaponDoorTimerRight = m_weaponDoorTimerLeft; @@ -194,7 +222,10 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) CMatrix mat2(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); mat1.GetPosition() += CVector(mat2.GetPosition().x + 0.1f, 0.0f, mat2.GetPosition().z); mat1.UpdateRW(); - }else if(GetModelIndex() == MI_MIAMI_SPARROW || GetModelIndex() == MI_MIAMI_RCRAIDER){ + }else if(GetModelIndex() == MI_HUNTER){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(IsRealHeli()){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); @@ -205,7 +236,6 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) } } - void CAutomobile::SetModelIndex(uint32 id) { @@ -213,6 +243,11 @@ CAutomobile::SetModelIndex(uint32 id) SetupModelNodes(); } +#define SAND_SLOWDOWN (0.01f) +float CAR_BALANCE_MULT = 0.3f; +CVector vecSeaSparrowGunPos(-0.5f, 2.4f, -0.785f); +CVector vecHunterGunPos(0.0f, 4.8f, -1.3f); +CVector vecHunterRocketPos(2.5f, 1.0f, -0.5f); CVector vecDAMAGE_ENGINE_POS_SMALL(-0.1f, -0.1f, 0.0f); CVector vecDAMAGE_ENGINE_POS_BIG(-0.5f, -0.3f, 0.0f); @@ -227,12 +262,25 @@ CAutomobile::ProcessControl(void) colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; else colModel = GetColModel(); + bool drivingInSand = false; bWarnedPeds = false; + m_doingBurnout = 0; + bStuckInSand = false; + bRestingOnPhysical = false; + + bool carHasNitro = bAllTaxisHaveNitro && GetStatus() == STATUS_PLAYER && IsTaxi(); - // skip if the collision isn't for the current level - if(colModel->level > LEVEL_GENERIC && colModel->level != CCollision::ms_collisionInMemory) + if(CReplay::IsPlayingBack()) return; + // Heli wind + if(IsRealHeli()) + if((GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS) && m_aWheelSpeed[1] > 0.075f || + GetStatus() == STATUS_SIMPLE) + CWindModifiers::RegisterOne(GetPosition(), 1); + + UpdatePassengerList(); + // Improve grip of vehicles in certain cases bool strongGrip1 = false; bool strongGrip2 = false; @@ -247,7 +295,8 @@ CAutomobile::ProcessControl(void) else if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) strongGrip2 = true; } - } + }else if(GetModelIndex() == MI_RCBANDIT && GetStatus() != STATUS_PLAYER_REMOTE) + strongGrip1 = true; if(bIsBus) ProcessAutoBusDoors(); @@ -258,7 +307,7 @@ CAutomobile::ProcessControl(void) if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED && GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PLAYER_DISABLED){ switch(GetModelIndex()) - case MI_FBICAR: + case MI_FBIRANCH: case MI_POLICE: case MI_ENFORCER: case MI_SECURICA: @@ -268,25 +317,15 @@ CAutomobile::ProcessControl(void) } // Process driver - if(pDriver){ - if(!bDriverLastFrame && m_bombType == CARBOMB_ONIGNITIONACTIVE){ - // If someone enters the car and there is a bomb, detonate - m_nBombTimer = 1000; - m_pBlowUpEntity = m_pBombRigger; - if(m_pBlowUpEntity) - m_pBlowUpEntity->RegisterReference((CEntity**)&m_pBlowUpEntity); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TICK, 1.0f); - } - bDriverLastFrame = true; - + if(pDriver) if(IsUpsideDown() && CanPedEnterCar()){ if(!pDriver->IsPlayer() && !(pDriver->m_leader && pDriver->m_leader->bInVehicle) && pDriver->CharCreatedBy != MISSION_CHAR) pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); } - }else - bDriverLastFrame = false; + + ActivateBombWhenEntered(); // Process passengers if(m_nNumPassengers != 0 && IsUpsideDown() && CanPedEnterCar()){ @@ -300,18 +339,7 @@ CAutomobile::ProcessControl(void) CRubbish::StirUp(this); - // blend in clump - int clumpAlpha = CVisibilityPlugins::GetClumpAlpha((RpClump*)m_rwObject); - if(bFadeOut){ - clumpAlpha -= 8; - if(clumpAlpha < 0) - clumpAlpha = 0; - }else if(clumpAlpha < 255){ - clumpAlpha += 16; - if(clumpAlpha > 255) - clumpAlpha = 255; - } - CVisibilityPlugins::SetClumpAlpha((RpClump*)m_rwObject, clumpAlpha); + UpdateClumpAlpha(); AutoPilot.m_bSlowedDownBecauseOfCars = false; AutoPilot.m_bSlowedDownBecauseOfPeds = false; @@ -322,24 +350,33 @@ CAutomobile::ProcessControl(void) else if(pHandling->Flags & HANDLING_NONPLAYER_STABILISER && GetStatus() == STATUS_PHYSICS) m_vecCentreOfMass.z = pHandling->CentreOfMass.z - 0.2f*pHandling->Dimension.z; else - m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + m_vecCentreOfMass = pHandling->CentreOfMass; + + // Park car + if(bCanPark && !bParking && VehicleCreatedBy != MISSION_VEHICLE && AutoPilot.m_nCarMission == MISSION_CRUISE && + ((CTimer::GetFrameCounter() + m_randomSeed)&0xF) == 0 && !IsTaxi()){ + CVector parkPosition = GetPosition() + 3.0f*GetRight() + 10.0f*GetForward(); + CEntity *ent = nil; + CColPoint colpoint; + if(!CWorld::ProcessLineOfSight(GetPosition(), parkPosition, colpoint, ent, true, true, true, false, false, false) || + ent == this) + CCarAI::GetCarToParkAtCoors(this, &parkPosition); + } // Process depending on status bool playerRemote = false; switch(GetStatus()){ case STATUS_PLAYER_REMOTE: - if(CPad::GetPad(0)->WeaponJustDown()){ + if(CPad::GetPad(0)->WeaponJustDown() && !bDisableRemoteDetonation){ BlowUpCar(FindPlayerPed()); CRemote::TakeRemoteControlledCarFromPlayer(); } - if(GetModelIndex() == MI_RCBANDIT){ - CVector pos = GetPosition(); + if(GetModelIndex() == MI_RCBANDIT && !bDisableRemoteDetonationOnContact){ + //CVector pos = GetPosition(); // FindPlayerCoors unused - if(RcbanditCheckHitWheels() || bIsInWater || CPopulation::IsPointInSafeZone(&pos)){ - if(CPopulation::IsPointInSafeZone(&pos)) - CGarages::TriggerMessage("HM2_5", -1, 5000, -1); + if(RcbanditCheckHitWheels() || bIsInWater){ CRemote::TakeRemoteControlledCarFromPlayer(); BlowUpCar(FindPlayerPed()); } @@ -350,7 +387,7 @@ CAutomobile::ProcessControl(void) // fall through case STATUS_PLAYER: if(playerRemote || - pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR){ + pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR && pDriver->GetPedState() != PED_ARRESTED){ // process control input if controlled by player if(playerRemote || pDriver->m_nPedType == PEDTYPE_PLAYER1) ProcessControlInputs(0); @@ -359,7 +396,59 @@ CAutomobile::ProcessControl(void) if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) DoDriveByShootings(); + + // Tweak center on mass when driving on two wheels + int twoWheelTime = CWorld::Players[CWorld::PlayerInFocus].m_nTimeNotFullyOnGround; + if(twoWheelTime > 500 && !IsRealHeli() && !IsRealPlane()){ + float tweak = Min(twoWheelTime-500, 1000)/500.0f; + if(GetUp().z > 0.0f){ + // positive when on left wheels, negative on right wheels + if(GetRight().z <= 0.0f) + tweak *= -1.0f; + m_vecCentreOfMass.z = pHandling->CentreOfMass.z + + CPad::GetPad(0)->GetSteeringLeftRight()/128.0f * + CAR_BALANCE_MULT * tweak * colModel->boundingBox.max.z; + } + }else + m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + + if(bHoverCheat) + DoHoverSuspensionRatios(); + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_RHINO){ + float slowdown; + CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); + float fSpeed = parallelSpeed.MagnitudeSqr(); + if(fSpeed > SQR(0.3f)){ + fSpeed = Sqrt(fSpeed); + parallelSpeed *= 0.3f / fSpeed; + slowdown = SAND_SLOWDOWN * Max(1.0f - 2.0f*fSpeed, 0.2f); + }else{ + bStuckInSand = true; + slowdown = SAND_SLOWDOWN; + } + if(pHandling->Flags & HANDLING_GOOD_INSAND) + slowdown *= 0.5f; + if(CWeather::WetRoads > 0.2f) + slowdown *= (1.2f - CWeather::WetRoads); + ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*slowdown*m_fMass); + drivingInSand = true; + } + } + }else if(pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar())){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; } + if(CPad::GetPad(0)->WeaponJustDown()) + ActivateBomb(); break; case STATUS_SIMPLE: @@ -380,6 +469,7 @@ CAutomobile::ProcessControl(void) PlayHornIfNecessary(); ReduceHornCounter(); bVehicleColProcessed = false; + bAudioChangingGear = false; // that's all we do for simple vehicles return; @@ -387,15 +477,44 @@ CAutomobile::ProcessControl(void) CCarAI::UpdateCarAI(this); CCarCtrl::SteerAICarWithPhysics(this); PlayHornIfNecessary(); + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_SANDKING && GetModelIndex() != MI_BFINJECT){ + bStuckInSand = true; + if(CWeather::WetRoads > 0.0f) + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass * (1.0f-CWeather::WetRoads)); + else + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); + } + } break; case STATUS_ABANDONED: - m_fBrakePedal = 0.2f; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)) + m_fBrakePedal = 0.2f; + else + m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } break; case STATUS_WRECKED: @@ -404,26 +523,32 @@ CAutomobile::ProcessControl(void) m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; break; case STATUS_PLAYER_DISABLED: - m_fBrakePedal = 1.0f; - bIsHandbrakeOn = true; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || + (pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar()))){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; + }else{ + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + } m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; - m_nCarHornTimer = 0; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; break; default: break; } - // what's going on here? - if(GetPosition().z < -0.6f && - Abs(m_vecMoveSpeed.x) < 0.05f && - Abs(m_vecMoveSpeed.y) < 0.05f) - m_vecTurnSpeed *= Pow(0.95f, CTimer::GetTimeStep()); - // Skip physics if object is found to have been static recently bool skipPhysics = false; if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){ @@ -451,7 +576,8 @@ CAutomobile::ProcessControl(void) if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && - m_fDistanceTravelled < distanceLimit || + m_fDistanceTravelled < distanceLimit && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity && m_pDamageEntity->IsPed()) || makeStatic){ m_nStaticFrames++; @@ -467,15 +593,27 @@ CAutomobile::ProcessControl(void) } }else m_nStaticFrames = 0; + if(IsRealHeli() && m_aWheelSpeed[1] > 0.0f){ + skipPhysics = false; + m_nStaticFrames = 0; + } } // Postpone for(i = 0; i < 4; i++) - if(m_aGroundPhysical[i] && !CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ - bWasPostponed = true; - return; + if(m_aGroundPhysical[i]){ + bRestingOnPhysical = true; + if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } } + if(bRestingOnPhysical){ + skipPhysics = false; + m_nStaticFrames = 0; + } + VehicleDamage(0.0f, 0); // special control @@ -487,12 +625,11 @@ CAutomobile::ProcessControl(void) TankControl(); BlowUpCarsInPath(); break; - case MI_YARDIE: - // beta also had esperanto here it seems + case MI_VOODOO: HydraulicControl(); break; default: - if(CVehicle::bCheat3){ + if(CVehicle::bCheat3 || carHasNitro){ // Make vehicle jump when horn is sounded if(GetStatus() == STATUS_PLAYER && m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f) && // BUG: game checks [0] four times, instead of all wheels @@ -531,12 +668,17 @@ CAutomobile::ProcessControl(void) 1.2f*m_vecMoveSpeed, nil, 2.0f); ApplyMoveForce(CVector(0.0f, 0.0f, 1.0f)*m_fMass*0.4f); - ApplyTurnForce(GetUp()*m_fMass*0.035f, GetForward()*1.0f); + ApplyTurnForce(GetUp()*m_fTurnMass*0.01f, GetForward()*1.0f); } } break; } + if(GetStatus() == STATUS_PHYSICS || GetStatus() == STATUS_SIMPLE) + if(AutoPilot.m_nCarMission == MISSION_HELI_FLYTOCOORS || + AutoPilot.m_nCarMission == MISSION_PLANE_FLYTOCOORS) + skipPhysics = true; + float brake; if(skipPhysics){ bHasContacted = false; @@ -546,11 +688,13 @@ CAutomobile::ProcessControl(void) m_nCollisionRecords = 0; bHasCollided = false; bVehicleColProcessed = false; + bAudioChangingGear = false; m_nDamagePieceType = 0; m_fDamageImpulse = 0.0f; m_pDamageEntity = nil; m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_fTireTemperature = 1.0f; }else{ // This has to be done if ProcessEntityCollision wasn't called @@ -603,6 +747,14 @@ CAutomobile::ProcessControl(void) if(m_aSuspensionSpringRatio[i] > 1.0f) m_aSuspensionSpringRatio[i] = 1.0f; } + }else if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[i].surfaceB) == ADHESIVE_SAND && + GetModelIndex() != MI_RHINO){ + fwdSpeed *= 0.7f; + float f = 1.0f - fwdSpeed/0.3f - 0.7f*CWeather::WetRoads; + f = Max(f, 0.4f); + m_aSuspensionSpringRatio[i] += 0.35f*f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; } // get points and directions if spring is compressed @@ -620,13 +772,20 @@ CAutomobile::ProcessControl(void) if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) bias = 1.0f - bias; - ApplySpringCollision(pHandling->fSuspensionForceLevel, + ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, springDirections[i], contactPoints[i], - m_aSuspensionSpringRatio[i], bias); - m_aWheelSkidmarkMuddy[i] = - m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || - m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY || - m_aWheelColPoints[i].surfaceB == SURFACE_SAND; + m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); + + m_aWheelSkidmarkUnk[i] = false; + if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY) + m_aWheelSkidmarkType[i] = SKIDMARK_MUDDY; + else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ + m_aWheelSkidmarkType[i] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[i] = true; + }else + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; }else{ contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); } @@ -643,6 +802,8 @@ CAutomobile::ProcessControl(void) m_aGroundPhysical[i] = nil; #endif } + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].normal.z > 0.35f) + springDirections[i] = -m_aWheelColPoints[i].normal; } // dampen springs @@ -661,7 +822,6 @@ CAutomobile::ProcessControl(void) } } - bool gripCheat = true; fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); if(!strongGrip1 && !CVehicle::bCheat3) @@ -669,14 +829,25 @@ CAutomobile::ProcessControl(void) float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); acceleration /= m_fForceMultiplier; - // unused - if(GetModelIndex() == MI_MIAMI_RCBARON || - GetModelIndex() == MI_MIAMI_RCRAIDER || - GetModelIndex() == MI_MIAMI_SPARROW) + if(IsRealHeli() || IsRealPlane()) acceleration = 0.0f; + if(bAudioChangingGear && m_fGasPedal > 0.4f && m_fBrakePedal < 0.1f && fwdSpeed > 0.15f && + this == FindPlayerVehicle() && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ + if(GetStatus() == STATUS_PLAYER && pHandling->Flags & HANDLING_IS_BUS){ + if(m_nBusDoorTimerEnd == 0) + m_nBusDoorTimerEnd = 1000; + else if(m_nBusDoorTimerEnd > CTimer::GetTimeStepInMilliseconds()) + m_nBusDoorTimerEnd -= CTimer::GetTimeStepInMilliseconds(); + } + + if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f) + ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); + } + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); - bool neutralHandling = !!(pHandling->Flags & HANDLING_NEUTRALHANDLING); + bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias); float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; @@ -722,21 +893,7 @@ CAutomobile::ProcessControl(void) if(CVehicle::bCheat3) traction *= 4.0f; - if(FindPlayerVehicle() && FindPlayerVehicle() == this){ - if(CPad::GetPad(0)->WeaponJustDown()){ - if(m_bombType == CARBOMB_TIMED){ - m_bombType = CARBOMB_TIMEDACTIVE; - m_nBombTimer = 7000; - m_pBlowUpEntity = FindPlayerPed(); - CGarages::TriggerMessage("GA_12", -1, 3000, -1); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TIMED_ACTIVATED, 1.0f); - }else if(m_bombType == CARBOMB_ONIGNITION){ - m_bombType = CARBOMB_ONIGNITIONACTIVE; - CGarages::TriggerMessage("GA_12", -1, 3000, -1); - DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_ONIGNITION_ACTIVATED, 1.0f); - } - } - }else if(strongGrip1 || CVehicle::bCheat3){ + if(FindPlayerVehicle() != this && (strongGrip1 || CVehicle::bCheat3)){ traction *= 1.2f; acceleration *= 1.4f; if(strongGrip2 || CVehicle::bCheat3){ @@ -748,13 +905,16 @@ CAutomobile::ProcessControl(void) static float fThrust; static tWheelState WheelState[4]; - // Process front wheels on ground + bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); + // Process front wheels on ground - first try + + if(!rearWheelsFirst){ if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ float s = Sin(m_fSteerAngle); float c = Cos(m_fSteerAngle); - CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-s, c, 0.0f)); - CVector wheelRight = Multiply3x3(GetMatrix(), CVector(c, s, 0.0f)); + + CVector wheelFwd, wheelRight, tmp; if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) @@ -762,6 +922,15 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; if(GetStatus() == STATUS_PLAYER) @@ -796,6 +965,15 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; if(GetStatus() == STATUS_PLAYER) @@ -827,33 +1005,36 @@ CAutomobile::ProcessControl(void) // Process front wheels off ground - if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ - if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } - m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; - } - if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ - if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) - m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; } - m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } } // Process rear wheels @@ -862,14 +1043,28 @@ CAutomobile::ProcessControl(void) CVector wheelFwd = GetForward(); CVector wheelRight = GetRight(); + float rearBrake = brake; + float rearTraction = traction; + if(bIsHandbrakeOn){ #ifdef FIX_BUGS - // Not sure if this is needed, but brake usually has timestep as a factor - if(bIsHandbrakeOn) - brake = 20000.0f * CTimer::GetTimeStepFix(); + // Not sure if this is needed, but brake usually has timestep as a factor + rearBrake = 20000.0f * CTimer::GetTimeStepFix(); #else - if(bIsHandbrakeOn) - brake = 20000.0f; + rearBrake = 20000.0f; #endif + if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ + m_fTireTemperature += 0.005*CTimer::GetTimeStep(); + if(m_fTireTemperature > 2.0f) + m_fTireTemperature = 2.0f; + } + }else if(m_doingBurnout && !mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)){ + rearBrake = 0.0f; + rearTraction = 0.0f; + // BUG: missing timestep + ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); + }else if(m_fTireTemperature > 1.0f){ + rearTraction *= m_fTireTemperature; + } if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) @@ -877,8 +1072,14 @@ CAutomobile::ProcessControl(void) else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal)*m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal); + wheelRight.Normalise(); + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceA = SURFACE_WHEELBASE; - float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*traction; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB); WheelState[CARWHEEL_REAR_LEFT] = m_aWheelState[CARWHEEL_REAR_LEFT]; @@ -887,7 +1088,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, CARWHEEL_REAR_LEFT, &m_aWheelSpeed[CARWHEEL_REAR_LEFT], @@ -897,7 +1098,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_LEFT, &m_aWheelSpeed[CARWHEEL_REAR_LEFT], @@ -905,14 +1106,26 @@ CAutomobile::ProcessControl(void) WHEEL_STATUS_OK); } +#ifdef FIX_BUGS + // Shouldn't we reset these after the left wheel? + wheelFwd = GetForward(); + wheelRight = GetRight(); +#endif + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = 0.0f; else fThrust = acceleration; + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal); + wheelRight.Normalise(); + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceA = SURFACE_WHEELBASE; - float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*traction; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB); WheelState[CARWHEEL_REAR_RIGHT] = m_aWheelState[CARWHEEL_REAR_RIGHT]; @@ -921,7 +1134,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, CARWHEEL_REAR_RIGHT, &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], @@ -931,7 +1144,7 @@ CAutomobile::ProcessControl(void) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], m_nWheelsOnGround, fThrust, - brake*brakeBiasRear, + rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_RIGHT, &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], @@ -940,58 +1153,203 @@ CAutomobile::ProcessControl(void) } } + if(m_doingBurnout && !mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && + (m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING || m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING)){ + m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); + if(m_fTireTemperature > 3.0f) + m_fTireTemperature = 3.0f; + }else if(m_fTireTemperature > 1.0f){ + m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; + } + // Process rear wheels off ground - if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ - if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) - m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; } - m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; - } - if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ - if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; - else{ - if(acceleration > 0.0f){ - if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; - }else{ - if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) - m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + } + } + + // Process front wheels on ground - second try + + if(rearWheelsFirst){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + float s = Sin(m_fSteerAngle); + float c = Cos(m_fSteerAngle); + + CVector wheelFwd, wheelRight, tmp; + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); + WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); + WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process front wheels off ground + + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } - m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } + } } for(i = 0; i < 4; i++){ float wheelPos = colModel->lines[i].p0.z; if(m_aSuspensionSpringRatio[i] > 0.0f) wheelPos -= m_aSuspensionSpringRatio[i]*m_aSuspensionSpringLength[i]; - m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; + if(GetModelIndex() == MI_VOODOO && bUsingSpecialColModel) + m_aWheelPosition[i] = wheelPos; + else + m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; } for(i = 0; i < 4; i++) m_aWheelState[i] = WheelState[i]; + if(m_fGasPedal < 0.0f){ + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_LEFT] = WHEEL_STATE_NORMAL; + if(m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_RIGHT] = WHEEL_STATE_NORMAL; + } // Process horn if(GetStatus() != STATUS_PLAYER){ - ReduceHornCounter(); + if(!IsAlarmOn()) + ReduceHornCounter(); }else{ - if(GetModelIndex() == MI_MRWHOOP){ - if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory] && - !Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5]){ - m_bSirenOrAlarm = !m_bSirenOrAlarm; - printf("m_bSirenOrAlarm toggled to %d\n", m_bSirenOrAlarm); - } - }else if(UsesSiren(GetModelIndex())){ + if(UsesSiren()){ if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory]){ if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5] && Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+3) % 5]) @@ -1004,43 +1362,137 @@ CAutomobile::ProcessControl(void) m_bSirenOrAlarm = !m_bSirenOrAlarm; }else m_nCarHornTimer = 0; - }else if(GetModelIndex() != MI_YARDIE && !CVehicle::bCheat3){ - if(Pads[0].GetHorn()) - m_nCarHornTimer = 1; - else - m_nCarHornTimer = 0; + }else if(GetModelIndex() != MI_VOODOO && !CVehicle::bCheat3 && !carHasNitro){ + if(!IsAlarmOn()){ + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } } } // Flying + bool playRotorSound = false; if(GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PHYSICS){ - if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW) - m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.0005f, 0.0f); + if(IsRealHeli()){ + bEngineOn = false; + m_aWheelSpeed[1] = Max(m_aWheelSpeed[1]-0.0005f, 0.0f); + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } }else if((GetModelIndex() == MI_DODO || CVehicle::bAllDodosCheat) && m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ #ifdef ALT_DODO_CHEAT if (bAltDodoCheat) - FlyingControl(FLIGHT_MODEL_SEAPLANE); + FlyingControl(FLIGHT_MODEL_PLANE); else #endif + if(GetModelIndex() == MI_DODO) FlyingControl(FLIGHT_MODEL_DODO); - }else if(GetModelIndex() == MI_MIAMI_RCBARON){ - FlyingControl(FLIGHT_MODEL_RCPLANE); - }else if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW || bAllCarCheat){ -#ifdef ALLCARSHELI_CHEAT - if (bAllCarCheat) - FlyingControl(FLIGHT_MODEL_HELI); else -#endif - { - if (CPad::GetPad(0)->GetCircleJustDown()) - m_aWheelSpeed[0] = Max(m_aWheelSpeed[0] - 0.03f, 0.0f); - if (m_aWheelSpeed[0] < 0.22f) - m_aWheelSpeed[0] += 0.0001f; - if (m_aWheelSpeed[0] > 0.15f) + FlyingControl(FLIGHT_MODEL_PLANE); + }else if(GetModelIndex() == MI_RCBARON){ + FlyingControl(FLIGHT_MODEL_RCPLANE); + }else if(IsRealHeli() || bAllCarCheat){ + // Speed up rotor + if(m_aWheelSpeed[1] < 0.22f && !bIsInWater){ + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + m_aWheelSpeed[1] += 0.003f; + else + m_aWheelSpeed[1] += 0.001f; + } + + // Fly + if(m_aWheelSpeed[1] > 0.15f){ + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + FlyingControl(FLIGHT_MODEL_RCHELI); + else if(m_nWheelsOnGround < 4 && !(GetModelIndex() == MI_SEASPAR && bTouchingWater) || + CPad::GetPad(0)->GetAccelerate() != 0 || CPad::GetPad(0)->GetCarGunUpDown() > 1.0f || + Abs(m_vecMoveSpeed.x) > 0.02f || + Abs(m_vecMoveSpeed.y) > 0.02f || + Abs(m_vecMoveSpeed.z) > 0.02f) FlyingControl(FLIGHT_MODEL_HELI); } + + // Blade collision + if(m_aWheelSpeed[1] > 0.015f && m_aCarNodes[CAR_BONNET]){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 0.72f, 0.9f); + else if(GetModelIndex() == MI_SPARROW || GetModelIndex() == MI_SEASPAR) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 5.15f, 0.8f); + else if(GetModelIndex() == MI_HUNTER) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 0.5f); + else + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 1.0f); + } + + // Heli weapons + if(GetModelIndex() == MI_HUNTER && GetStatus() == STATUS_PLAYER){ + // Hunter rockets + if(CPad::GetPad(0)->CarGunJustDown() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+350){ + CWeapon gun(WEAPONTYPE_ROCKETLAUNCHER, 100); + CVector source = vecHunterRocketPos; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + source = vecHunterRocketPos; + source.x = -source.x; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + // Hunter gun + }else if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+60){ + CWeapon gun(WEAPONTYPE_HELICANNON, 5000); + CVector source = vecHunterGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + }else if(GetModelIndex() == MI_SEASPAR && GetStatus() == STATUS_PLAYER){ + // Sea sparrow gun + if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+40){ + CWeapon gun(WEAPONTYPE_M4, 5000); + CVector source = vecSeaSparrowGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + } + + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } + + // Play rotor sound + if(playRotorSound && m_aCarNodes[CAR_BONNET]){ + CVector camDist = TheCamera.GetPosition() - GetPosition(); + float distSq = camDist.MagnitudeSqr(); + if(distSq < SQR(20.0f) && Abs(m_fPropellerRotation - m_aWheelRotation[1]) > DEGTORAD(30.0f)){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + CVector blade = mat.GetRight(); + blade = GetMatrix() * blade; + camDist /= Max(Sqrt(distSq), 0.01f); + if(Abs(DotProduct(camDist, blade)) > 0.95f){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_31, 0.0f); + m_fPropellerRotation = m_aWheelRotation[1]; + } + } } } @@ -1082,7 +1534,7 @@ CAutomobile::ProcessControl(void) CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)), - nil, 0.9f); + nil, 0.63f); CVector coors = damagePos; coors.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), @@ -1094,10 +1546,8 @@ CAutomobile::ProcessControl(void) // Blow up car after 5 seconds m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); - if(m_fFireBlowUpTimer > 5000.0f){ - CWorld::Players[CWorld::PlayerInFocus].AwardMoneyForExplosion(this); + if(m_fFireBlowUpTimer > 5000.0f) BlowUpCar(m_pSetOnFireEntity); - } }else m_fFireBlowUpTimer = 0.0f; @@ -1109,7 +1559,7 @@ CAutomobile::ProcessControl(void) if(m_bSirenOrAlarm && (CTimer::GetFrameCounter()&7) == 5 && - UsesSiren(GetModelIndex()) && GetModelIndex() != MI_MRWHOOP) + UsesSiren() && GetModelIndex() != MI_MRWHOOP) CCarAI::MakeWayForCarWithSiren(this); @@ -1117,10 +1567,14 @@ CAutomobile::ProcessControl(void) float suspShake = 0.0f; float surfShake = 0.0f; + float speedsq = m_vecMoveSpeed.MagnitudeSqr(); for(i = 0; i < 4; i++){ float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; - if(suspChange > 0.3f){ - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); + if(suspChange > 0.3f && !drivingInSand && speedsq > 0.04f){ + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); + else + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); if(suspChange > suspShake) suspShake = suspChange; } @@ -1129,7 +1583,7 @@ CAutomobile::ProcessControl(void) if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ if(surfShake < 0.2f) surfShake = 0.3f; - }else if(surf == SURFACE_MUD_DRY || surf == SURFACE_SAND){ + }else if(surf == SURFACE_MUD_DRY){ if(surfShake < 0.1f) surfShake = 0.2f; }else if(surf == SURFACE_GRASS){ @@ -1137,13 +1591,17 @@ CAutomobile::ProcessControl(void) surfShake = 0.1f; } + if(this == FindPlayerVehicle()) +// BUG: this only observes one of the wheels + TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; m_aSuspensionSpringRatio[i] = 1.0f; } // Shake pad - if((suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ + if(!drivingInSand && (suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ float speed = m_vecMoveSpeed.MagnitudeSqr(); if(speed > sq(0.1f)){ speed = Sqrt(speed); @@ -1158,8 +1616,9 @@ CAutomobile::ProcessControl(void) } bVehicleColProcessed = false; + bAudioChangingGear = false; - if(!bWarnedPeds) + if(!bWarnedPeds && GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) CCarCtrl::ScanForPedDanger(this); @@ -1167,20 +1626,20 @@ CAutomobile::ProcessControl(void) // TODO: make the numbers defines float heading; - if(GetPosition().x > 1900.0f){ + if(GetPosition().x > 1950.0f-400.0f){ if(m_vecMoveSpeed.x > 0.0f) m_vecMoveSpeed.x *= -1.0f; heading = GetForward().Heading(); if(heading > 0.0f) // going west SetHeading(-heading); - }else if(GetPosition().x < -1900.0f){ + }else if(GetPosition().x < -1950.0f-400.0f){ if(m_vecMoveSpeed.x < 0.0f) m_vecMoveSpeed.x *= -1.0f; heading = GetForward().Heading(); if(heading < 0.0f) // going east SetHeading(-heading); } - if(GetPosition().y > 1900.0f){ + if(GetPosition().y > 1950.0f){ if(m_vecMoveSpeed.y > 0.0f) m_vecMoveSpeed.y *= -1.0f; heading = GetForward().Heading(); @@ -1188,7 +1647,7 @@ CAutomobile::ProcessControl(void) SetHeading(PI-heading); else if(heading > -HALFPI && heading < 0.0f) SetHeading(-PI-heading); - }else if(GetPosition().y < -1900.0f){ + }else if(GetPosition().y < -1950.0f){ if(m_vecMoveSpeed.y < 0.0f) m_vecMoveSpeed.y *= -1.0f; heading = GetForward().Heading(); @@ -1207,11 +1666,31 @@ CAutomobile::ProcessControl(void) (m_fGasPedal == 0.0f && brake == 0.0f || GetStatus() == STATUS_WRECKED)){ if(Abs(m_vecMoveSpeed.x) < 0.005f && Abs(m_vecMoveSpeed.y) < 0.005f && - Abs(m_vecMoveSpeed.z) < 0.005f){ + Abs(m_vecMoveSpeed.z) < 0.005f && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity == FindPlayerPed()) && + (m_aSuspensionSpringRatioPrev[0] < 1.0f && m_aSuspensionSpringRatioPrev[1] < 1.0f && + m_aSuspensionSpringRatioPrev[2] < 1.0f && m_aSuspensionSpringRatioPrev[3] < 1.0f)){ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed.z = 0.0f; } } + + if(IsRealHeli() && bHeliDestroyed && !bRenderScorched){ + ApplyMoveForce(0.0f, 0.0f, -2.0f*CTimer::GetTimeStep()); + m_vecTurnSpeed.z += -0.002f*CTimer::GetTimeStep(); + m_vecTurnSpeed.x += -0.0002f*CTimer::GetTimeStep(); + + RwRGBA col = { 84, 84, 84, 255 }; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, GetMatrix()*CVector(0.0f, 0.0f, -10.0f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.7f, col, 0, 0, 0, 3000); + + if(CWorld::TestSphereAgainstWorld(GetPosition(), 10.0f, this, true, false, false, false, false, false) || + GetPosition().z < 6.0f) + if(!bRenderScorched){ // we already know this is true... + CExplosion::AddExplosion(this, nil, EXPLOSION_CAR, GetPosition(), 0); + bRenderScorched = true; + } + } } void @@ -1235,6 +1714,17 @@ CAutomobile::PreRender(void) int i, j, n; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){ + // Rotate Rhino turret + CMatrix m; + CVector p; + m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + p = m.GetPosition(); + m.SetRotateZ(m_fCarGunLR); + m.Translate(p); + m.UpdateRW(); + } + if(GetModelIndex() == MI_RCBANDIT){ CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f); CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f); @@ -1245,7 +1735,7 @@ CAutomobile::PreRender(void) // Wheel particles - if(GetModelIndex() == MI_DODO){ + if(GetModelIndex() == MI_DODO || GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR){ ; // nothing }else if(GetModelIndex() == MI_RCBANDIT){ for(i = 0; i < 4; i++){ @@ -1300,6 +1790,7 @@ CAutomobile::PreRender(void) rearSkidding = true; for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatioPrev[i] < 1.0f && m_aWheelColPoints[i].surfaceB != SURFACE_WATER) switch(m_aWheelState[i]){ case WHEEL_STATE_SPINNING: if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){ @@ -1319,46 +1810,79 @@ CAutomobile::PreRender(void) if(m_aWheelTimer[i] > 0.0f) CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); break; case WHEEL_STATE_SKIDDING: if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){ // same as below - AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if(Abs(fwdSpeed) > 5.0f){ + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); - CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, - m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), - CVector(0.0f, 0.0f, 0.0f)); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + } if(m_aWheelTimer[i] > 0.0f) CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); } break; case WHEEL_STATE_FIXED: - AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if(Abs(fwdSpeed) > 5.0f){ + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); - CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, - m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), - CVector(0.0f, 0.0f, 0.0f)); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + } if(m_aWheelTimer[i] > 0.0f) CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); break; default: if(Abs(fwdSpeed) > 0.5f) AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); - if(m_aWheelSkidmarkBloody[i] && m_aWheelTimer[i] > 0.0f) + if((m_aWheelSkidmarkBloody[i] || m_aWheelSkidmarkUnk[i]) && m_aWheelTimer[i] > 0.0f) CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + } + + // Sparks for friction of burst wheels + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[i] < 1.0f){ + static float speedSq; + speedSq = m_vecMoveSpeed.MagnitudeSqr(); + if(speedSq > SQR(0.1f) && + m_aWheelColPoints[i].surfaceB != SURFACE_GRASS && + m_aWheelColPoints[i].surfaceB != SURFACE_MUD_DRY && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND_BEACH && + m_aWheelColPoints[i].surfaceB != SURFACE_WATER){ + CVector normalSpeed = m_aWheelColPoints[i].normal * DotProduct(m_aWheelColPoints[i].normal, m_vecMoveSpeed); + CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_REAR_LEFT) + frictionSpeed -= 0.05f*GetRight(); + else + frictionSpeed += 0.05f*GetRight(); + CVector unusedRight = 0.15f*GetRight(); + CVector sparkDir = 0.25f*frictionSpeed; + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + + if(speedSq > 0.04f) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + if(speedSq > 0.16f){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + } + } } } } @@ -1381,7 +1905,7 @@ CAutomobile::PreRender(void) CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_LEFT, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + offset, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); + m_aWheelSkidmarkType[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); break; default: break; } @@ -1399,7 +1923,7 @@ CAutomobile::PreRender(void) CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_RIGHT, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + offset, GetForward().x, GetForward().y, - &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); + m_aWheelSkidmarkType[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); break; default: break; } @@ -1434,21 +1958,22 @@ CAutomobile::PreRender(void) AddDamagedVehicleParticles(); // Exhaust smoke - if(bEngineOn && fwdSpeed < 90.0f){ + if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; - CVector pos1, pos2, dir; + CVector pos1, pos2, dir1, dir2; if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ - dir.z = 0.0f; + dir1.z = 0.0f; + dir2.z = 0.0f; if(fwdSpeed < 10.0f){ CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); steerFwd = Multiply3x3(GetMatrix(), steerFwd); float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); - dir.x = steerFwd.x * r; - dir.y = steerFwd.y * r; + dir1.x = steerFwd.x * r; + dir1.y = steerFwd.y * r; }else{ - dir.x = m_vecMoveSpeed.x; - dir.y = m_vecMoveSpeed.y; + dir1.x = m_vecMoveSpeed.x; + dir1.y = m_vecMoveSpeed.y; } bool dblExhaust = false; @@ -1458,17 +1983,62 @@ CAutomobile::PreRender(void) pos2 = exhaustPos; pos2.x = -pos2.x; pos2 = GetMatrix() * pos2; + dir2 = dir1; } - n = 4.0f*m_fGasPedal; - if(dblExhaust) - for(i = 0; i <= n; i++){ - CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); - CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); + static float fumesLimit = 2.0f; + if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit) + for(i = 0; i < 4;){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + + static float extraFumesLimit = 0.5f; + if(m_fGasPedal > extraFumesLimit && m_nCurrentGear < 3){ + if(CGeneral::GetRandomNumber() & 1) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + else if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + } + + // Fire on Cuban hermes + if(GetModelIndex() == MI_CUBAN && i == 1 && m_fGasPedal > 0.9f){ + if(m_nCurrentGear == 1 || m_nCurrentGear == 3 && (CTimer::GetTimeInMilliseconds()%1500) > 750){ + if(CGeneral::GetRandomNumber() & 1){ + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + }else{ + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + } + } + } + + if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && + CWeather::Rain == 0.0f && i == 0){ + CVector camDist = GetPosition() - TheCamera.GetPosition(); + if(DotProduct(GetForward(), camDist) > 0.0f || + TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT){ + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + } + } + + if(GetModelIndex() == MI_CUBAN && i < 1){ + i = 1; + pos1 = GetMatrix() * CVector(1.134f, -1.276f, -0.56f); + pos2 = GetMatrix() * CVector(-1.134f, -1.276f, -0.56f); + dir1 += 0.05f*GetRight(); + dir2 -= 0.05f*GetRight(); + }else + i = 99; } - else - for(i = 0; i <= n; i++) - CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); } } @@ -1540,8 +2110,8 @@ CAutomobile::PreRender(void) float angle = (CTimer::GetTimeInMilliseconds() & 0x3FF)*TWOPI/0x3FF; float s = 8.0f*Sin(angle); float c = 8.0f*Cos(angle); - CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex, - &pos, c, s, s, -c, r, g, b, 8.0f); + //CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex, + // &pos, c, s, s, -c, r, g, b, 8.0f); CPointLights::AddLight(CPointLights::LIGHT_POINT, pos + GetUp()*2.0f, CVector(0.0f, 0.0f, 0.0f), 12.0f, @@ -1579,17 +2149,26 @@ CAutomobile::PreRender(void) } break; - case MI_FBICAR: + case MI_FBIRANCH: + case MI_VICECHEE: if(m_bSirenOrAlarm){ CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f); if(CTimer::GetTimeInMilliseconds() & 0x100 && DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f) - CCoronas::RegisterCorona((uintptr)this + 21, - 0, 0, 255, 255, - pos, 0.4f, 50.0f, - CCoronas::TYPE_STAR, - CCoronas::FLARE_NONE, - CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + if(GetModelIndex() == MI_VICECHEE) + CCoronas::RegisterCorona((uintptr)this + 21, + 255, 70, 70, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this + 21, + 0, 0, 255, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::UpdateCoronaCoors((uintptr)this + 21, pos, 50.0f, 0.0f); } @@ -1597,7 +2176,8 @@ CAutomobile::PreRender(void) case MI_TAXI: case MI_CABBIE: - case MI_BORGNINE: + case MI_ZEBRA: + case MI_KAUFMAN: if(bTaxiLight){ CVector pos = GetPosition() + GetUp()*0.95f; CCoronas::RegisterCorona((uintptr)this + 21, @@ -1614,7 +2194,8 @@ CAutomobile::PreRender(void) } if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_DODO && - GetModelIndex() != MI_RHINO) { + GetModelIndex() != MI_RHINO && GetModelIndex() != MI_RCBARON && + GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI) { // Process lights // Turn lights on/off @@ -1653,7 +2234,6 @@ CAutomobile::PreRender(void) lookVector = CVector(1.0f, 0.0f, 0.0f); // 1.0 if directly behind car, -1.0 if in front - // BUG on PC: Abs of DotProduct is taken float behindness = DotProduct(lookVector, GetForward()); behindness = clamp(behindness, -1.0f, 1.0f); // shouldn't be necessary // 0.0 if behind car, PI if in front @@ -1668,7 +2248,8 @@ CAutomobile::PreRender(void) lightL -= GetRight()*2.0f*headLightPos.x; // Headlight coronas - if(behindness < 0.0f){ + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) < 0.0f && + (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON || this != FindPlayerVehicle())){ // In front of car float intensity = -0.5f*behindness + 0.3f; float size = 1.0f - behindness; @@ -1758,7 +2339,7 @@ CAutomobile::PreRender(void) lightL -= GetRight()*2.0f*tailLightPos.x; // Taillight coronas - if(behindness > 0.0f){ + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) > 0.0f){ // Behind car float intensity = 0.4f*behindness + 0.4f; float size = (behindness + 1.0f)/2.0f; @@ -1832,7 +2413,7 @@ CAutomobile::PreRender(void) if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowHeadLightsTex, &pos, - 7.0f*fwd.x, 7.0f*fwd.y, 7.0f*fwd.y, -7.0f*fwd.x, 45, 45, 45, 7.0f); + 7.0f*fwd.x, 7.0f*fwd.y, 5.5f*fwd.y, -5.5f*fwd.x, 45, 45, 45, 7.0f); f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f); pos += CVector(f*fwd.x, f*fwd.y, 0.0f); @@ -1924,26 +2505,43 @@ CAutomobile::PreRender(void) // end of lights } - CShadows::StoreShadowForCar(this); -} + if (IsRealHeli()) + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); + else if ( GetModelIndex() == MI_RCBARON) + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_RCPLANE); + else + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_CAR); + + DoSunGlare(); + + // Heli dust + if(IsRealHeli() && m_aWheelSpeed[1] > 0.1125f && GetPosition().z < 30.0f){ + bool foundGround = false; + float waterZ = -1000.0f; + float groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, GetPosition().z, &foundGround); + if(!CWaterLevel::GetWaterLevel(GetPosition(), &waterZ, false)) + waterZ = 0.0f; + groundZ = Max(groundZ, waterZ); + float rnd = (m_aWheelSpeed[1]-0.1125f)*((int)Max(16.0f-4.0f*CTimer::GetTimeStep(),2.0f))*400.0f/43.0f; + float radius = 10.0f; + if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) + radius = 3.0f; + if(GetPosition().z - groundZ < radius) + HeliDustGenerate(this, radius-(GetPosition().z - groundZ), groundZ, Ceil(rnd)); + } -void -CAutomobile::Render(void) -{ - int i; 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 +2549,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 +2560,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 +2708,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 +2737,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 +2868,104 @@ CAutomobile::Render(void) ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT); ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET); ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT); + } + + if((GetModelIndex() == MI_PHEONIX || GetModelIndex() == MI_BFINJECT) && + GetStatus() == STATUS_PLAYER && m_aCarNodes[CAR_WING_LR]){ + float rotation = 0.0f; + + if(GetModelIndex() == MI_BFINJECT) + if(m_fPropellerRotation > TWOPI) m_fPropellerRotation -= TWOPI; + + if(Abs(m_fGasPedal) > 0.0f){ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.2f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation < 1.3f){ + m_fPropellerRotation = Min(m_fPropellerRotation+0.1f*CTimer::GetTimeStep(), 1.3f); + rotation = m_fPropellerRotation; + }else{ + float wave = Sin((CTimer::GetTimeInMilliseconds()%10000)/70.0f); + rotation = m_fPropellerRotation + 0.13*wave; + } + } + }else{ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.1f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation > 0.0f){ + m_fPropellerRotation = Max(m_fPropellerRotation-0.05f*CTimer::GetTimeStep(), 0.0f); + rotation = m_fPropellerRotation; + } + } + } + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WING_LR])); + pos = mat.GetPosition(); + if(GetModelIndex() == MI_BFINJECT) + mat.SetRotateY(rotation); + else + mat.SetRotateX(rotation); + mat.Translate(pos); + mat.UpdateRW(); + } +} - mi->SetVehicleColour(m_currentColour1, m_currentColour2); +void +CAutomobile::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + + if(IsRealHeli()){ + RpAtomic *atomic = nil; + int rotorAlpha = (1.5f - Min(1.7f*Max(m_aWheelSpeed[1],0.0f)/0.22f, 1.5f))*255.0f; + rotorAlpha = Min(rotorAlpha, 255); + int blurAlpha = Max(1.5f*m_aWheelSpeed[1]/0.22f - 0.4f, 0.0f)*150.0f; + blurAlpha = Min(blurAlpha, 150); + + // Top rotor + if(m_aCarNodes[CAR_BONNET]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BONNET], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Rear rotor + if(m_aCarNodes[CAR_BOOT]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BOOT], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Blurred top rotor + if(m_aCarNodes[CAR_WINDSCREEN]){ + RwFrameForAllObjects(m_aCarNodes[CAR_WINDSCREEN], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } + atomic = nil; + // Blurred rear rotor + if(m_aCarNodes[CAR_BUMP_REAR]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BUMP_REAR], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } } - if(!CVehicle::bWheelsOnlyCheat) + if(CVehicle::bWheelsOnlyCheat){ + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + if(m_aCarNodes[CAR_WHEEL_RM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); + if(m_aCarNodes[CAR_WHEEL_LM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); + }else CEntity::Render(); } @@ -2157,6 +2988,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 +3000,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 +3012,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 +3045,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 +3078,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 +3134,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 +3153,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 +3333,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 +3444,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 +3517,6 @@ CAutomobile::HydraulicControl(void) } setPrevRatio = true; - m_aWheelPosition[i] -= 0.05f; } DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); } @@ -2753,7 +3547,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 +3566,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 +3636,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 +3691,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 +3760,7 @@ CAutomobile::ProcessBuoyancy(void) } }else{ bIsInWater = false; + bIsDrowning = false; bTouchingWater = false; static RwRGBA splashCol = {155, 155, 185, 196}; @@ -2967,13 +3771,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 +3791,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 +3799,21 @@ CAutomobile::ProcessBuoyancy(void) void CAutomobile::DoDriveByShootings(void) { - CAnimBlendAssociation *anim; + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + CWeapon *weapon = pDriver->GetWeapon(); - if(weapon->m_eWeaponType != WEAPONTYPE_UZI) + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) return; - weapon->Update(pDriver->m_audioEntityId); + weapon->Update(pDriver->m_audioEntityId, nil); bool lookingLeft = false; bool lookingRight = false; - if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN){ + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.m_bObbeCinematicCarCamOn){ if(CPad::GetPad(0)->GetLookLeft()) lookingLeft = true; if(CPad::GetPad(0)->GetLookRight()) @@ -3051,37 +3825,42 @@ CAutomobile::DoDriveByShootings(void) lookingRight = true; } + AnimationId rightAnim = ANIM_DRIVEBY_R; + AnimationId leftAnim = ANIM_DRIVEBY_L; + if (pDriver->m_pMyVehicle->bLowVehicle) { + rightAnim = ANIM_DRIVEBY_LOW_R; + leftAnim = ANIM_DRIVEBY_LOW_L; + } + if(lookingLeft || lookingRight){ if(lookingLeft){ - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim == nil || anim->blendDelta < 0.0f) - CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_L); - else - anim->SetRun(); + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, leftAnim); }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim == nil || anim->blendDelta < 0.0f) - CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_R); - else - anim->SetRun(); + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, rightAnim); } - if(CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer){ - weapon->FireFromCar(this, lookingLeft); - weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, true); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } } }else{ weapon->Reload(); - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; - anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim) anim->blendDelta = -1000.0f; } @@ -3089,11 +3868,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 +3953,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 +4028,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 +4036,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 +4072,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 +4102,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 +4190,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 +4301,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 +4328,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 +4405,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 +4428,7 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) for(i = 0; i < 4; i++){ dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, - CGeneral::GetRandomNumberInRange(0.02f, 0.06f), gravelCol); + CGeneral::GetRandomNumberInRange(0.05f, 0.09f), gravelCol); } return 1; case SURFACE_MUD_DRY: @@ -3580,30 +4440,30 @@ CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) CGeneral::GetRandomNumberInRange(0.02f, 0.06f), mudCol); } return 0; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + if(CTimer::GetFrameCounter() & 2 || + CGeneral::GetRandomNumberInRange(CWeather::WetRoads, 1.01f) > 0.5f) + return 0; + dir.x = 0.5f*m_vecMoveSpeed.x; + dir.y = 0.5f*m_vecMoveSpeed.y; + for(i = 0; i < 1; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); + CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, + 2.0f*m_vecMoveSpeed.Magnitude(), sandCol); + } + return 0; default: - if ( CWeather::WetRoads > 0.01f -#ifdef PC_PARTICLE - && CTimer::GetFrameCounter() & 1 -#endif - ) - { - CParticle::AddParticle( -#if defined(FIX_BUGS) && !defined(PC_PARTICLE) // looks wrong on PC particles - PARTICLE_WHEEL_WATER, -#else - PARTICLE_WATERSPRAY, -#endif - colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), -#ifdef PC_PARTICLE - CVector(0.0f, 0.0f, 1.0f), -#else - CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), -#endif - nil, - CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + if(CWeather::WetRoads > 0.01f){ + if(CTimer::GetFrameCounter() & 1) + CParticle::AddParticle( + PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); return 0; } - return 1; } } @@ -3714,6 +4574,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 +4595,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 +4640,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 +4680,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 +4727,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 +4767,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 +4797,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 +4826,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 +4842,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 +4899,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 +4930,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 +4961,6 @@ CAutomobile::PlayHornIfNecessary(void) PlayCarHorn(); } - void CAutomobile::ResetSuspension(void) { @@ -4107,8 +5003,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 +5038,16 @@ CAutomobile::BlowUpCarsInPath(void) { int i; - if(m_vecMoveSpeed.Magnitude() > 0.1f) + if(m_vecMoveSpeed.Magnitude() > 0.1f && bTankDetonateCars) for(i = 0; i < m_nCollisionRecords; i++) if(m_aCollisionRecords[i] && m_aCollisionRecords[i]->IsVehicle() && m_aCollisionRecords[i]->GetModelIndex() != MI_RHINO && - !m_aCollisionRecords[i]->bRenderScorched) + !m_aCollisionRecords[i]->bRenderScorched){ + if(this == FindPlayerVehicle()) + CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, this, FindPlayerPed(), 2000); ((CVehicle*)m_aCollisionRecords[i])->BlowUpCar(this); + } } bool @@ -4241,6 +5140,9 @@ CAutomobile::ProcessSwingingDoor(int32 component, eDoors door) if(Damage.GetDoorStatus(door) != DOOR_STATUS_SWINGING) return; + if (m_aCarNodes[component] == nil) + return; + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); CVector pos = mat.GetPosition(); float axes[3] = { 0.0f, 0.0f, 0.0f }; @@ -4250,6 +5152,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 +5199,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 +5253,6 @@ GetCurrentAtomicObjectCB(RwObject *object, void *data) return object; } -static CColPoint aTempPedColPts[MAX_COLLISION_POINTS]; - CObject* CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) { @@ -4351,6 +5285,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 +5312,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 +5371,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 +5418,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 +5461,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 +5562,123 @@ CAutomobile::SetAllTaxiLights(bool set) m_sAllTaxiLights = set; } +void +CAutomobile::TellHeliToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_HELI_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f){ + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y) + PI; + while(m_fOrientation > TWOPI) m_fOrientation -= TWOPI; + } +} + +void +CAutomobile::TellPlaneToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_PLANE_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f) + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); +} + +void +CAutomobile::PopBoot(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Doors[DOOR_BOOT].m_fAngle = Doors[DOOR_BOOT].m_fMinAngle; + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[DOOR_BOOT])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + axes[Doors[DOOR_BOOT].m_nAxis] = Doors[DOOR_BOOT].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); + } +} + +void +CAutomobile::PopBootUsingPhysics(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)) + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_SWINGING); + Doors[DOOR_BOOT].m_fAngle = -2.0f; +} + +void +CAutomobile::CloseAllDoors(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(!IsDoorMissing(DOOR_FRONT_LEFT)) + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + if(mi->m_numDoors > 1){ + if(!IsDoorMissing(DOOR_FRONT_RIGHT)) + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + if(mi->m_numDoors > 2){ + if(!IsDoorMissing(DOOR_REAR_LEFT)) + OpenDoor(CAR_DOOR_LR, DOOR_REAR_LEFT, 0.0f); + if(!IsDoorMissing(DOOR_REAR_RIGHT)) + OpenDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT, 0.0f); + } + } +} + +void +CAutomobile::KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped) +{ + AnimationId anim = ANIM_KO_SHOT_FRONT1; + if(ped == nil) + return; + + ped->m_vehEnterType = door; + ped->SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_IDLE_STANCE, 100.0f); + CPed::PedSetOutCarCB(nil, ped); + ped->SetMoveState(PEDMOVE_STILL); + if(GetUp().z < 0.0f) + ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); + else + ped->SetHeading(GetForward().Heading()); + + switch(weapon){ + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_UNIDENTIFIED: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->m_pCollidingEntity = this; + anim = NUM_STD_ANIMS; + break; + + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_RAMMEDBYCAR: + case WEAPONTYPE_FALL: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + anim = ANIM_KD_LEFT; + ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); + break; + } + + if(weapon != WEAPONTYPE_UNARMED){ + ped->SetFall(1000, anim, 0); + ped->bIsStanding = false; + ped->m_headingRate = 0.0f; + } + ped->m_pMyVehicle = nil; +} + #ifdef COMPATIBLE_SAVES void CAutomobile::Save(uint8*& buf) diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h index 604bed17..3dee998d 100644 --- a/src/vehicles/Automobile.h +++ b/src/vehicles/Automobile.h @@ -3,33 +3,10 @@ #include "Vehicle.h" #include "DamageManager.h" #include "Door.h" +#include "Skidmarks.h" class CObject; -enum eCarNodes -{ - CAR_WHEEL_RF = 1, - CAR_WHEEL_RM, - CAR_WHEEL_RB, - CAR_WHEEL_LF, - CAR_WHEEL_LM, - CAR_WHEEL_LB, - CAR_BUMP_FRONT, - CAR_BUMP_REAR, - CAR_WING_RF, - CAR_WING_RR, - CAR_DOOR_RF, - CAR_DOOR_RR, - CAR_WING_LF, - CAR_WING_LR, - CAR_DOOR_LF, - CAR_DOOR_LR, - CAR_BONNET, - CAR_BOOT, - CAR_WINDSCREEN, - NUM_CAR_NODES, -}; - // These are used for all the wheel arrays // DON'T confuse with VEHWHEEL, which are vehicle components enum { @@ -39,28 +16,10 @@ 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, - CAR_DOOR_FLAG_LR = 0x2, - CAR_DOOR_FLAG_RF = 0x4, - CAR_DOOR_FLAG_RR = 0x8 -}; class CAutomobile : public CVehicle { public: - // 0x288 CDamageManager Damage; CDoor Doors[6]; RwFrame *m_aCarNodes[NUM_CAR_NODES]; @@ -69,22 +28,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 +52,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_fPlaneSteer; // related to the above float m_fVelocityChangeForAudio; float m_randomValues[6]; // used for what? float m_fFireBlowUpTimer; @@ -102,6 +65,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 +99,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 +116,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 +139,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 +152,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,19 +167,4 @@ public: static void SetAllTaxiLights(bool set); }; -VALIDATE_SIZE(CAutomobile, 0x5A8); - -inline uint8 GetCarDoorFlag(int32 carnode) { - switch (carnode) { - case CAR_DOOR_LF: - return CAR_DOOR_FLAG_LF; - case CAR_DOOR_LR: - return CAR_DOOR_FLAG_LR; - case CAR_DOOR_RF: - return CAR_DOOR_FLAG_RF; - case CAR_DOOR_RR: - return CAR_DOOR_FLAG_RR; - default: - return CAR_DOOR_FLAG_UNKNOWN; - } -} +extern CVector vecHunterGunPos;
\ No newline at end of file diff --git a/src/vehicles/Bike.cpp b/src/vehicles/Bike.cpp new file mode 100644 index 00000000..760a8ec6 --- /dev/null +++ b/src/vehicles/Bike.cpp @@ -0,0 +1,2925 @@ +#include "common.h" +#include "General.h" +#include "Pad.h" +#include "DMAudio.h" +#include "Clock.h" +#include "Timecycle.h" +#include "ZoneCull.h" +#include "Camera.h" +#include "Darkel.h" +#include "Rubbish.h" +#include "Explosion.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Coronas.h" +#include "SpecialFX.h" +#include "WaterLevel.h" +#include "Floater.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "Record.h" +#include "CarCtrl.h" +#include "CarAI.h" +#include "Script.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" + +//--MIAMI: done except for TODOs + +// 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; + } +} + +#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; + bIsBeingPickedUp = false; + bIsStanding = false; + bExtraSpeed = false; + bIsOnFire = false; + bWheelieCam = false; + + m_fTireTemperature = 1.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 bBalancedByRider = 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: + bBalancedByRider = true; + bIsBeingPickedUp = 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: + bBalancedByRider = 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; + bWheelieCam = false; + // that's all we do for simple vehicles + return; + + case STATUS_PHYSICS: + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + PlayHornIfNecessary(); + + bBalancedByRider = true; + bWheelieCam = false; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + }else + bIsBeingPickedUp = 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; + + bBalancedByRider = (pDriver || pPassengers[0] || bIsBeingCarJacked) && !bIsStanding; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + bWheelieCam = 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; + + bBalancedByRider = false; + bWheelieCam = 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; + + bBalancedByRider = true; + bWheelieCam = false; + break; + } + + if(bIsStanding) + if(Abs(GetRight().z) > 0.35f || Abs(GetForward().z) > 0.5f) + bIsStanding = false; + + if(bBalancedByRider || bIsBeingPickedUp || 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 -= Min(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 *= Min(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) && !bIsBeingPickedUp){ + 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(!(bBalancedByRider || bIsBeingPickedUp || 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 || i == 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; + 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 || bIsBeingPickedUp){ + 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(bBalancedByRider || bIsBeingPickedUp){ + 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(bBalancedByRider || bIsBeingPickedUp || bIsStanding){ + float onSideness = clamp(DotProduct(GetRight(), m_vecAvgSurfaceNormal), -1.0f, 1.0f); + CVector worldCOM = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + // Keep bike upright + if(bBalancedByRider){ + 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) +{ + int i; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + // Wheel particles + + if(m_aWheelState[BIKEWHEEL_REAR] != WHEEL_STATE_NORMAL && + m_aWheelColPoints[BIKESUSP_R2].surfaceB != SURFACE_WATER && m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + static float smokeSize = 0.2f; + CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; + if(m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f) + groundPos = (groundPos + m_aWheelColPoints[BIKESUSP_R1].point)/2.0f; + groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), CVector(0.0f, 0.0f, 0.0f), + nil, smokeSize); + + CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, + m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); + + if(m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING && + (CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_HARD || + CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_ROAD)){ + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.05f)); + } + }else if(m_aWheelSkidmarkBloody[BIKEWHEEL_REAR] || m_aWheelSkidmarkUnk[BIKEWHEEL_REAR]){ + CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; + groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); + + CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, + m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); + } + + // Process lights + + // Turn lights on/off + bool shouldLightsBeOn = + CClock::GetHours() > 20 || + CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) || + CClock::GetHours() < 7 || + CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) || + m_randomSeed/50000.0f < CWeather::Foggyness || + m_randomSeed/50000.0f < CWeather::WetRoads; + if(shouldLightsBeOn != bLightsOn && GetStatus() != STATUS_WRECKED){ + if(GetStatus() == STATUS_ABANDONED){ + // Turn off lights on abandoned vehicles only when we they're far away + if(bLightsOn && + Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f) + bLightsOn = false; + }else + bLightsOn = shouldLightsBeOn; + } + + // Actually render the lights + bool alarmOn = false; + bool alarmOff = false; + if(IsAlarmOn()){ + if(CTimer::GetTimeInMilliseconds() & 0x100) + alarmOn = true; + else + alarmOff = true; + } + if(bEngineOn && bLightsOn || alarmOn || alarmOff){ + CalculateLeanMatrix(); + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + + // 1.0 if directly behind car, -1.0 if in front + float behindness = DotProduct(lookVector, GetForward()); + // 0.0 if behind car, PI if in front + float angle = Abs(Acos(Abs(behindness))); + + // Headlight + + CMatrix mat; + CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; + if(GetModelIndex() == 152){ // this is the bobcat in VC, but we don't want that effect anyway + mat.SetUnity(); + mat.RotateZ(m_fWheelAngle); + mat = m_leanMatrix * mat; + }else + mat = m_leanMatrix; + CVector light = mat * headLightPos; + if(behindness < 0.0f){ + // In front of bike + float intensity = -0.5f*behindness + 0.3f; + float size = 1.0f - behindness; + + if(behindness < -0.97f && camDist < 30.0f){ + // Directly in front and not too far away + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, + light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, + light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + } + } + + if(alarmOff){ + CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, + light, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this, light, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright light + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); + + // Taillight + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + light = m_leanMatrix * tailLightPos; + + // Taillight corona + if(behindness > 0.0f){ + // Behind car + float intensity = 0.4f*behindness + 0.4f; + float size = (behindness + 1.0f)/2.0f; + + if(m_fGasPedal < 0.0f){ + // reversing + // no lights in this case + }else{ + if(m_fBrakePedal > 0.0f){ + intensity += 0.4f; + size += 0.3f; + } + + if(alarmOff){ + CCoronas::RegisterCorona((uintptr)this + 14, 0, 0, 0, 0, + light, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 14, 128*intensity, 0, 0, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright light + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + + // Light shadows + if(!alarmOff){ + CVector pos = GetPosition(); + CVector2D fwd(GetForward()); + fwd.Normalise(); + float f = headLightPos.y + 6.0f; + pos += CVector(f*fwd.x, f*fwd.y, 2.0f); + CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowExplosionTex, &pos, + 7.0f*fwd.x, 7.0f*fwd.y, 3.5f*fwd.y, -3.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); + CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos, + 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f); + } + + if(this == FindPlayerVehicle() && !alarmOff){ + CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(), + 20.0f, 1.0f, 1.0f, 1.0f, + FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE, + false); + CVector pos = GetPosition() - 4.0f*GetForward(); + if(m_fBrakePedal > 0.0f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 10.0f, 1.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + else + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 7.0f, 0.6f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + } + }else if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){ + // Lights off + CalculateLeanMatrix(); + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + CVector light = m_leanMatrix * tailLightPos; + + if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + lookVector.Normalise(); + float behindness = DotProduct(lookVector, GetForward()); + if(behindness > 0.0f){ + if(m_fGasPedal < 0.0f){ + // reversing + // no lights in this case + }else{ + // braking + CCoronas::RegisterCorona((uintptr)this + 14, 120, 0, 0, 255, + light, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + } + + + // Wheel particles + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; + int drawParticles = Abs(fwdSpeed) < 90.0f; + int susp = BIKESUSP_F1; + for(i = 0; i < 2; i++){ + if(i == BIKEWHEEL_REAR) + susp = BIKESUSP_R1; + + static float speedSq; + // Sparks for friction of burst wheels + if(m_wheelStatus[i] == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[susp] < 1.0f && + (speedSq = m_vecMoveSpeed.MagnitudeSqr(), speedSq > SQR(0.1f)) && + m_aWheelColPoints[susp].surfaceB != SURFACE_GRASS && + m_aWheelColPoints[susp].surfaceB != SURFACE_MUD_DRY && + m_aWheelColPoints[susp].surfaceB != SURFACE_SAND && + m_aWheelColPoints[susp].surfaceB != SURFACE_SAND_BEACH){ + CVector normalSpeed = m_aWheelColPoints[susp].normal * DotProduct(m_aWheelColPoints[susp].normal, m_vecMoveSpeed); + CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; + CVector sparkDir = 0.25f*frictionSpeed; + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + + if(speedSq > 0.04f) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + if(speedSq > 0.16f){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + } + }else if(m_aSuspensionSpringRatioPrev[i] < 1.0f && + (fwdSpeed > 0.2f || m_aWheelState[i] == WHEEL_STATE_SPINNING)){ + if(m_aWheelColPoints[susp].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[susp].surfaceB == SURFACE_MUD_DRY || + m_aWheelColPoints[susp].surfaceB == SURFACE_SAND || + m_aWheelColPoints[susp].surfaceB == SURFACE_SAND_BEACH) + AddWheelDirtAndWater(&m_aWheelColPoints[susp], drawParticles); + } + } + + AddDamagedVehicleParticles(); + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_BIKE); + + CMatrix mat; + CVector pos; + 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(); + } + + // Exhaust smoke + if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ + CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; + CVector pos1, pos2, dir; + + if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ + dir.z = 0.0f; + if(fwdSpeed < 10.0f){ + CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); + steerFwd = Multiply3x3(GetMatrix(), steerFwd); + float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); + dir.x = steerFwd.x * r; + dir.y = steerFwd.y * r; + }else{ + dir.x = m_vecMoveSpeed.x; + dir.y = m_vecMoveSpeed.y; + } + + bool dblExhaust = false; + pos1 = GetMatrix() * exhaustPos; + if(pHandling->Flags & HANDLING_DBL_EXHAUST){ + dblExhaust = true; + pos2 = exhaustPos; + pos2.x = -pos2.x; + pos2 = GetMatrix() * pos2; + } + + static float fumesLimit = 2.0f; + if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + if(dblExhaust) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); + + if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && + CWeather::Rain == 0.0f){ + CVector camDist = GetPosition() - TheCamera.GetPosition(); + if(DotProduct(GetForward(), camDist) > 0.0f || + TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT){ + if(dblExhaust) + pos1 = 0.5f*pos1 + 0.5f*pos2; + + if(TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT) + pos1 -= 0.2f*GetForward(); + + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + } + } + } + } + } +} + +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) +{ + int i; + CVector impulse, point; + + if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ + bTouchingWater = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + + 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.8f || + impulseRatio > 0.4f && (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; + + if(pDriver){ + pDriver->bIsInWater = true; + if(pDriver->IsPlayer() || !bWaterTight){ + if(m_aSuspensionSpringRatio[0] < 1.0f || + m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || + m_aSuspensionSpringRatio[3] < 1.0f) + pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + else + KnockOffRider(WEAPONTYPE_DROWNING, 0, pDriver, false); + } + } + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]){ + pPassengers[i]->bIsInWater = true; + if(pPassengers[i]->IsPlayer() || !bWaterTight){ + if(m_aSuspensionSpringRatio[0] < 1.0f || + m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || + m_aSuspensionSpringRatio[3] < 1.0f) + pPassengers[i]->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + else + KnockOffRider(WEAPONTYPE_DROWNING, 0, pPassengers[i], false); + } + } + }else{ + bIsInWater = false; + bIsDrowning = false; + } + }else{ + bIsInWater = false; + bIsDrowning = false; + bTouchingWater = false; + } +} + +void +CBike::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->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 || + TheCamera.m_bObbeCinematicCarCamOn){ + 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 || CPad::GetPad(0)->GetCarGunFired()){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FT); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_RHS); + }else if(lookingRight){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FT); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_LHS); + }else{ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FT); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_FT); + } + + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, lookingRight); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FT); + if(anim) + anim->blendDelta = -1000.0f; + } +} + +void +CBike::VehicleDamage(void) +{ + float impulse = m_fDamageImpulse; + float colSpeed = 800.0f*impulse/m_fMass; + if(GetStatus() == STATUS_PLAYER) + colSpeed *= 0.65f; + else if(VehicleCreatedBy == MISSION_VEHICLE) + colSpeed *= 0.4f; + + if(!bCanBeDamaged) + return; + + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + impulse *= 0.5f; + + if(bIsStanding && impulse > 20.0f) + bIsStanding = false; + + // Inflict damage on the driver and passenger + if(pDriver && pDriver->GetPedState() == PED_DRIVING && colSpeed > 10.0f){ + float fwd = 0.6f; + if(Abs(DotProduct(m_vecDamageNormal, GetForward())) > 0.85f){ + float u = Max(DotProduct(m_vecDamageNormal, CVector(0.0f, 0.0f, 1.0f)), 0.0f); + if(u < 0.85f) + u = 0.0f; + fwd += 7.0f * SQR(u); + } + float up = 0.05f; + + if(GetModelIndex() == MI_SANCHEZ){ + fwd *= 0.65f; + up *= 0.75f; + } + + float total = fwd*Abs(DotProduct(m_vecDamageNormal, GetForward())) + + 0.45f*Abs(DotProduct(m_vecDamageNormal, GetRight())) + + up*Max(DotProduct(m_vecDamageNormal, GetUp()), 0.0f); + float damage = (total - 1.5f*Max(DotProduct(m_vecDamageNormal, GetUp()), 0.0f))*colSpeed; + + if(pDriver->IsPlayer() && CCullZones::CamStairsForPlayer() && CCullZones::FindZoneWithStairsAttributeForPlayer()) + damage = 0.0f; + + if(damage > 75.0f){ + int dir = -10; + if(pDriver){ + dir = pDriver->GetLocalDirection(-m_vecDamageNormal); + if(pDriver->m_fHealth > 0.0f) + pDriver->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); + if(pDriver && pDriver->GetPedState() == PED_DRIVING) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pDriver, false); + } + if(pPassengers[0]){ + dir = pPassengers[0]->GetLocalDirection(-m_vecDamageNormal); + if(pPassengers[0]->m_fHealth > 0.0f) + pPassengers[0]->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); + if(pPassengers[0] && pPassengers[0]->GetPedState() == PED_DRIVING) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pPassengers[0], false); + } + } + } + + if(impulse > 25.0f && GetStatus() != STATUS_WRECKED){ + float damage = (impulse-25.0f)*pHandling->fCollisionDamageMultiplier; + if(damage > 0.0f){ + if(damage > 5.0f && + pDriver && + m_pDamageEntity && m_pDamageEntity->IsVehicle() && + (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && + ((CVehicle*)m_pDamageEntity)->pDriver) +// TODO(MIAMI): enum + pDriver->Say(144); + + int oldHealth = m_fHealth; + if(this == FindPlayerVehicle()) + m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.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; + } + } + + if(m_fHealth < 250.0f){ + // Car is on fire + if(!bIsOnFire){ + // Set engine on fire and remember who did this + bIsOnFire = true; + m_fFireBlowUpTimer = 0.0f; + m_pSetOnFireEntity = m_pDamageEntity; + if(m_pSetOnFireEntity) + m_pSetOnFireEntity->RegisterReference(&m_pSetOnFireEntity); + } + } +} + +void +CBike::AddDamagedVehicleParticles(void) +{ + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + return; + if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + return; + if(m_fHealth >= 650.0f) + return; + + CVector direction = 0.5f*m_vecMoveSpeed; + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->GetFrontSeatPosn(); + + damagePos.z -= 0.4f; + damagePos = GetMatrix()*damagePos; + + CalculateLeanMatrix(); + + if(m_fHealth < 250.0f){ + // fire, done in processControl + }else if(m_fHealth < 320.0f){ + direction *= 0.2f; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, direction + 0.02f*m_leanMatrix.GetRight()); + }else if(m_fHealth < 390.0f){ + if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.05f*m_leanMatrix.GetRight()); + direction *= 0.3f; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, direction + 0.04f*m_leanMatrix.GetRight()); + }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; + if(TheCamera.GetLookDirection() != LOOKING_FORWARD){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.08f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); + }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0){ + direction = 0.8f*m_vecMoveSpeed; + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.07f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); + } + }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos + 0.06f*m_leanMatrix.GetRight(), direction); + } +} + +int32 +CBike::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) +{ + int i; + CVector dir; + static float minSize = 0.02f; + static float maxSize = 0.04f; + 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 && + colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) + return 0; + + switch(colpoint->surfaceB){ + case SURFACE_GRASS: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), grassCol); + } + return 0; + case SURFACE_GRAVEL: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), gravelCol); + } + return 1; + case SURFACE_MUD_DRY: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), mudCol); + } + return 0; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + if(CTimer::GetFrameCounter() & 2) + return 0; + dir.x = 0.75f*m_vecMoveSpeed.x; + dir.y = 0.75f*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, + 0.8f*m_vecMoveSpeed.Magnitude(), sandCol); + } + return 0; + default: + if(CWeather::WetRoads > 0.01f){ + CParticle::AddParticle( + PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + return 0; + } + return 1; + } + + return 0; +} + +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; + +#ifdef FIX_BUGS + // taken from CAutomobile. maybe tweak values? + 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; + } +#endif + + // 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) +{ + RwMatrix *mat = RwMatrixCreate(); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *vehColModel = mi->GetColModel(); + + colModel->boundingSphere = vehColModel->boundingSphere; + colModel->boundingBox = vehColModel->boundingBox; + + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_FRONT], m_aBikeNodes[BIKE_CHASSIS]); + colModel->spheres[0].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LF); + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_REAR], m_aBikeNodes[BIKE_CHASSIS]); + colModel->spheres[1].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); + colModel->numSpheres = 2; +#ifdef FIX_BUGS + RwMatrixDestroy(mat); +#endif + return true; +} + +float fBikeBurstForceMult = 0.02f; +float fBikeBurstFallSpeed = 0.3f; +float fBikeBurstFallSpeedPlayer = 0.55f; + +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 + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 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()); + } + + // This code checks piece types originally so it is never triggered + // as we have converted them to wheel indices above already. + if(pDriver){ +#ifdef FIX_SIGNIFICANT_BUGS + if(wheel == BIKEWHEEL_FRONT && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || + wheel == BIKEWHEEL_REAR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ +#else + if(wheel == CAR_PIECE_WHEEL_LF && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || + wheel == CAR_PIECE_WHEEL_LR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ +#endif + float speedSq = m_vecMoveSpeed.MagnitudeSqr(); + if(speedSq > fBikeBurstFallSpeed && + (GetStatus() != STATUS_PLAYER || speedSq > fBikeBurstFallSpeedPlayer)){ +#ifdef FIX_SIGNIFICANT_BUGS + if(wheel == BIKEWHEEL_FRONT){ +#else + if(wheel == CAR_PIECE_WHEEL_LF){ +#endif + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pDriver, false); + if(pPassengers[0]) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pPassengers[0], false); + }else + ApplyTurnForce(2.0f*fBikeBurstForceMult*m_fTurnMass*GetRight(), GetForward()); + } + } + } + } +} + +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::KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn) +{ + AnimationId anim = ANIM_KO_SHOT_FRONT1; + if(ped == nil) + return; + + if(!ped->IsPlayer()){ + if(bGetBackOn){ + if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + FindPlayerPed()->m_carInObjective == ped->m_pMyVehicle && + !CTheScripts::IsPlayerOnAMission()) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + else if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + !CTheScripts::IsPlayerOnAMission()) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + else if(ped->m_pedStats->m_temper <= ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + !CTheScripts::IsPlayerOnAMission()){ + ped->SetObjective(OBJECTIVE_WANDER, ped->m_pMyVehicle); + ped->m_nPathDir = CGeneral::GetRandomNumberInRange(0, 8); + } + }else if(ped->m_leader == nil){ + if(pDriver == ped) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, this); + else + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, this); + } + } + + if(ped->IsPed()){ + CAnimBlendAssociation *assoc; + for(assoc = RpAnimBlendClumpGetFirstAssociation(ped->GetClump(), ASSOC_DRIVING); + assoc; + assoc = RpAnimBlendGetNextAssociation(assoc)) + assoc->flags |= ASSOC_DELETEFADEDOUT; + } + + ped->SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_IDLE_STANCE, 100.0f); + ped->m_vehEnterType = CAR_DOOR_LF; + 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: + default: + switch(direction){ + case 0: + anim = ANIM_BIKE_FALL_R; + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.1f); + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.3f)) + ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetForward()); + ped->m_pCollidingEntity = this; + break; + case 1: + case 2: + if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ + anim = ANIM_KO_SPIN_R; + ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; + ped->ApplyMoveForce(5.0f*GetUp() + 6.0f*GetRight()); + }else{ + anim = ANIM_KD_LEFT; + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); + } + // BUG or is it intentionally missing? + //ped->m_pCollidingEntity = this; + break; + case 3: + if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ + anim = ANIM_KO_SPIN_L; + ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; + ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetRight()); + }else{ + anim = ANIM_KD_RIGHT; + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->ApplyMoveForce(4.0f*GetUp() - 8.0f*GetRight()); + } + // BUG or is it intentionally missing? + //ped->m_pCollidingEntity = this; + break; + } + break; + + case WEAPONTYPE_DROWNING:{ + RwRGBA color; + anim = ANIM_FALL_FALL; + ped->m_vecMoveSpeed *= 0.2f; + ped->m_vecMoveSpeed.z = 0.0f; + ped->m_pCollidingEntity = this; + 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, 48) + 48; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLASH, 0.0f); + CVector splashPos = ped->GetPosition() + 2.2f*ped->m_vecMoveSpeed; + float waterZ = 0.0f; + if(CWaterLevel::GetWaterLevel(splashPos, &waterZ, false)) + splashPos.z = waterZ; + CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, splashPos, CVector(0.0f, 0.0f, 0.1f), + 0.0f, 200, color, true); + break; + } + + case WEAPONTYPE_FALL: { + ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; + float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; + ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, + CGeneral::GetRandomNumberInRange(3.0f, 7.0f)); + ped->m_pCollidingEntity = this; + switch(direction){ + case 0: anim = ANIM_KO_SKID_BACK; break; + case 1: anim = ANIM_KD_RIGHT; break; + case 2: anim = ANIM_KO_SKID_FRONT; break; + case 3: anim = ANIM_KD_LEFT; break; + } + if(m_nWheelsOnGround == 0) + ped->bKnockedOffBike = true; + break; + } + + case WEAPONTYPE_RAMMEDBYCAR: { + ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; + static float minForceZ = 8.0f; + static float maxForceZ = 15.0f; + float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; + ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, + CGeneral::GetRandomNumberInRange(minForceZ, maxForceZ)); + ped->m_pCollidingEntity = this; + switch(direction){ + case 0: anim = ANIM_KO_SKID_BACK; break; + case 1: anim = ANIM_KD_RIGHT; break; + case 2: anim = ANIM_KO_SKID_FRONT; break; + case 3: anim = ANIM_KD_LEFT; break; + } + ped->bKnockedOffBike = true; + if(ped->IsPlayer()) + ped->Say(SOUND_PED_DAMAGE); + break; + } + } + + if(weapon == WEAPONTYPE_DROWNING){ + ped->bIsStanding = false; + ped->bWasStanding = false; + ped->bIsInTheAir = true; + ped->bIsInWater = true; + ped->bTouchingWater = true; + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_FALL_FALL, 4.0f); + }else if(weapon != WEAPONTYPE_UNARMED){ + if(ped->m_fHealth > 0.0f) + ped->SetFall(1000, anim, 0); + else + ped->SetDie(anim); + ped->bIsStanding = false; + } + + CEntity *ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); + if(ent){ + CColPoint point; + ent = nil; + if(CWorld::ProcessVerticalLine(ped->GetPosition(), ped->GetPosition().z-2.0f, point, ent, true, false, false, false, false, false, nil)){ + if(ped->m_pMyVehicle == nil){ + ped->m_pMyVehicle = this; + ped->PositionPedOutOfCollision(); + ped->m_pMyVehicle = nil; + }else + ped->PositionPedOutOfCollision(); + }else + ped->GetMatrix().Translate(CVector(0.0f, 0.0f, -2.0f)); + ped->m_pCollidingEntity = ped->m_pMyVehicle; + ped->bKnockedOffBike = true; + ped->bHeadStuckInCollision = true; + }else if(weapon == WEAPONTYPE_RAMMEDBYCAR){ + if(CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 1.3f), 0.6f, nil, true, false, false, false, false, false) == nil) + ped->GetMatrix().Translate(CVector(0.0f, 0.0f, 0.5f)); + } + ped->m_pMyVehicle = nil; +} + +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; + } +} + +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..669e236e 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,137 @@ 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 bIsBeingPickedUp : 1; + uint8 bIsStanding : 1; + uint8 bExtraSpeed : 1; // leaning forward + uint8 bIsOnFire : 1; + uint8 bWheelieCam : 1; + 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 KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn); + void VehicleDamage(void); + void ProcessBuoyancy(void); + void DoDriveByShootings(void); + void AddDamagedVehicleParticles(void); + int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); + 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); +}; + +inline int8 GetBikeDoorFlag(int32 carnode) { + switch (carnode) { + case CAR_DOOR_RR: + case CAR_DOOR_LR: + return CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + case CAR_DOOR_RF: + case CAR_DOOR_LF: + case CAR_WINDSCREEN: + return CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +}
\ No newline at end of file diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index 50117690..ae437f56 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -2,7 +2,9 @@ #include "General.h" #include "Timecycle.h" +#include "Weather.h" #include "HandlingMgr.h" +#include "CarAI.h" #include "CarCtrl.h" #include "RwHelper.h" #include "ModelIndices.h" @@ -12,23 +14,30 @@ #include "Darkel.h" #include "Explosion.h" #include "Particle.h" +#include "ParticleObject.h" #include "WaterLevel.h" #include "Floater.h" #include "World.h" +#include "Stats.h" #include "Pools.h" #include "Pad.h" #include "Boat.h" +#include "AnimBlendAssociation.h" +#include "RpAnimBlend.h" +#include "Record.h" + +//--MIAMI: file done #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]; @@ -48,9 +57,14 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_fSteeringLeftRight = 0.0f; m_nPadID = 0; m_fMovingRotation = 0.0f; + m_fMovingSpeed = 0.0f; + m_skimmerThingTimer = 0.0f; + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds(); SetModelIndex(mi); pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)minfo->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((eHandlingId)minfo->m_handlingId); + pBoatHandling = mod_HandlingManager.GetBoatPointer((eHandlingId)minfo->m_handlingId); minfo->ChooseVehicleColour(m_currentColour1, m_currentColour2); m_fMass = pHandling->fMass; @@ -63,10 +77,6 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; - m_fThrustZ = 0.25f; - m_fThrustY = 0.35f; - m_vecMoveRes = CVector(0.7f, 0.998f, 0.999f); - m_vecTurnRes = CVector(0.85f, 0.96f, 0.96f); m_boat_unused3 = false; m_fVolumeUnderWater = 7.0f; @@ -79,6 +89,7 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) bIsInWater = true; + m_phys_unused1 = 0.0f; m_boat_unused2 = 0; m_bIsAnchored = true; m_fOrientation = INVALID_ORIENTATION; @@ -91,12 +102,17 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_afWakePointLifeTime[i] = 0.0f; m_nAmmoInClip = 20; + + if(GetModelIndex() == MI_MARQUIS) + m_boom.Init(-PI/10.0f, PI/10.0f, 0, 2); + else + m_boom.Init(-PI/5.0f, PI/5.0f, 0, 2); } void CBoat::SetModelIndex(uint32 id) { - CEntity::SetModelIndex(id); + CVehicle::SetModelIndex(id); SetupModelNodes(); } @@ -109,39 +125,55 @@ CBoat::GetComponentWorldPosition(int32 component, CVector &pos) void CBoat::ProcessControl(void) { - if(m_nZoneLevel > LEVEL_GENERIC && m_nZoneLevel != CCollision::ms_collisionInMemory) - return; - bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f; PruneWakeTrail(); + if(bRenderScorched) + m_fBuoyancy *= 0.99f; + + if(FindPlayerPed()->m_pWanted->m_nWantedLevel > 0 && GetModelIndex() == MI_PREDATOR){ + CVehicle *playerVeh = FindPlayerVehicle(); + if(playerVeh && playerVeh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT && + (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || + AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE || + AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || + AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE || + AutoPilot.m_nCarMission == MISSION_ATTACKPLAYER) && + CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_115, 0.0f); + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF); + } + } + int r, g, b; + RwRGBA dropColor = { 0, 0, 0, 0 }; RwRGBA splashColor, jetColor; - r = 114.75f*(CTimeCycle::GetAmbientRed() + 0.5f*CTimeCycle::GetDirectionalRed()); - g = 114.75f*(CTimeCycle::GetAmbientGreen() + 0.5f*CTimeCycle::GetDirectionalGreen()); - b = 114.75f*(CTimeCycle::GetAmbientBlue() + 0.5f*CTimeCycle::GetDirectionalBlue()); + r = 127.5f*(CTimeCycle::GetAmbientRed_Obj() + 0.5f*CTimeCycle::GetDirectionalRed()); + g = 127.5f*(CTimeCycle::GetAmbientGreen_Obj() + 0.5f*CTimeCycle::GetDirectionalGreen()); + b = 127.5f*(CTimeCycle::GetAmbientBlue_Obj() + 0.5f*CTimeCycle::GetDirectionalBlue()); r = clamp(r, 0, 255); g = clamp(g, 0, 255); b = clamp(b, 0, 255); splashColor.red = r; splashColor.green = g; splashColor.blue = b; - splashColor.alpha = CGeneral::GetRandomNumberInRange(128, 150); + splashColor.alpha = CGeneral::GetRandomNumberInRange(160, 196); - r = 242.25f*(CTimeCycle::GetAmbientRed() + 0.5f*CTimeCycle::GetDirectionalRed()); - g = 242.25f*(CTimeCycle::GetAmbientGreen() + 0.5f*CTimeCycle::GetDirectionalGreen()); - b = 242.25f*(CTimeCycle::GetAmbientBlue() + 0.5f*CTimeCycle::GetDirectionalBlue()); + r = 229.5f*(CTimeCycle::GetAmbientRed() + 0.85f*CTimeCycle::GetDirectionalRed()); + g = 229.5f*(CTimeCycle::GetAmbientGreen() + 0.85f*CTimeCycle::GetDirectionalGreen()); + b = 229.5f*(CTimeCycle::GetAmbientBlue() + 0.85f*CTimeCycle::GetDirectionalBlue()); r = clamp(r, 0, 255); g = clamp(g, 0, 255); b = clamp(b, 0, 255); jetColor.red = r; jetColor.green = g; jetColor.blue = b; - jetColor.alpha = CGeneral::GetRandomNumberInRange(96, 128); + jetColor.alpha = CGeneral::GetRandomNumberInRange(196, 228); CGeneral::GetRandomNumber(); // unused + UpdateClumpAlpha(); ProcessCarAlarm(); switch(GetStatus()){ @@ -151,10 +183,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 +199,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: @@ -174,7 +211,7 @@ CBoat::ProcessControl(void) bIsHandbrakeOn = false; m_fBrakePedal = 0.5f; m_fGasPedal = 0.0f; - if((GetPosition() - CWorld::Players[CWorld::PlayerInFocus].GetPos()).Magnitude() > 150.0f){ + if((GetPosition() - FindPlayerCentreOfWorld_NoSniperShift()).Magnitude() > 150.0f){ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); return; @@ -185,33 +222,42 @@ CBoat::ProcessControl(void) float collisionDamage = pHandling->fCollisionDamageMultiplier * m_fDamageImpulse; #ifdef FIX_BUGS - if (collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && m_fHealth >= 150.0f && !bCollisionProof) { + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + collisionDamage *= 0.5f; + if (collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && !bCollisionProof) { #else - if(collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && m_fHealth >= 150.0f){ + if(collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED){ #endif float prevHealth = m_fHealth; - if(this == FindPlayerVehicle()){ - if(bTakeLessDamage) - m_fHealth -= (collisionDamage-25.0f)/6.0f; - else - m_fHealth -= (collisionDamage-25.0f)/2.0f; - }else{ - if(collisionDamage > 60.0f && pDriver) - pDriver->Say(SOUND_PED_CAR_COLLISION); - if(bTakeLessDamage) - m_fHealth -= (collisionDamage-25.0f)/12.0f; - else - m_fHealth -= (collisionDamage-25.0f)/4.0f; - } + if(prevHealth >= 250.0f){ +#ifndef FIX_BUGS + // if collisionDamage < 50 we actually increase health here... + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + collisionDamage *= 0.5f; +#endif + if(this == FindPlayerVehicle()){ + if(bTakeLessDamage) + m_fHealth -= (collisionDamage-25.0f)/6.0f; + else + m_fHealth -= (collisionDamage-25.0f)/2.0f; + }else{ + if(collisionDamage > 60.0f && pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + if(bTakeLessDamage) + m_fHealth -= (collisionDamage-25.0f)/12.0f; + else + m_fHealth -= (collisionDamage-25.0f)/4.0f; + } - if(m_fHealth <= 0.0f && prevHealth > 0.0f){ - m_fHealth = 1.0f; - m_pSetOnFireEntity = m_pDamageEntity; + if(m_fHealth <= 0.0f && prevHealth > 0.0f){ + m_fHealth = 1.0f; + m_pSetOnFireEntity = m_pDamageEntity; + } } } // Damage particles - if(m_fHealth <= 600.0f && GetStatus() != STATUS_WRECKED && + if(m_fHealth <= 460.0f && GetStatus() != STATUS_WRECKED && Abs(GetPosition().x - TheCamera.GetPosition().x) < 200.0f && Abs(GetPosition().y - TheCamera.GetPosition().y) < 200.0f){ float speedSq = m_vecMoveSpeed.MagnitudeSqr(); @@ -237,7 +283,7 @@ CBoat::ProcessControl(void) smokePos = GetMatrix() * smokePos; // On fire - if(m_fHealth < 150.0f){ + if(m_fHealth < 250.0f){ CParticle::AddParticle(PARTICLE_CARFLAME, smokePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(2.25f/200.0f, 0.09f)), nil, 0.9f); @@ -254,52 +300,92 @@ CBoat::ProcessControl(void) if(speedSq < 0.25f && (CTimer::GetFrameCounter() + m_randomSeed) & 1) CParticle::AddParticle(PARTICLE_ENGINE_STEAM, smokePos, smokeDir); - if(speedSq < 0.25f && m_fHealth <= 350.0f) + if(speedSq < 0.25f && m_fHealth <= 390.0f) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, smokePos, 1.25f*smokeDir); } + bool bSeparateTurnForce = bHasHitWall; CPhysical::ProcessControl(); CVector buoyanceImpulse(0.0f, 0.0f, 0.0f); CVector buoyancePoint(0.0f, 0.0f, 0.0f); - if(mod_Buoyancy.ProcessBuoyancy(this, pHandling->fBuoyancy, &buoyancePoint, &buoyanceImpulse)){ + if(mod_Buoyancy.ProcessBuoyancyBoat(this, pHandling->fBuoyancy, &buoyancePoint, &buoyanceImpulse, bSeparateTurnForce)){ // Process boat in water 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->bTouchingWater = true; + 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; m_vecBuoyancePoint = buoyancePoint; - ApplyMoveForce(buoyanceImpulse); - if(!onLand) - ApplyTurnForce(buoyanceImpulse, buoyancePoint); + if(GetModelIndex() == MI_SKIMMER && GetUp().z < -0.5f && Abs(m_vecMoveSpeed.x) < 0.2f && Abs(m_vecMoveSpeed.y) < 0.2f) + ApplyMoveForce(0.03f*buoyanceImpulse); + else + ApplyMoveForce(buoyanceImpulse); + if(bSeparateTurnForce) + ApplyTurnForce(0.4f*buoyanceImpulse, buoyancePoint); + + // TODO: what is this? + if(GetModelIndex() == MI_SKIMMER) + if(m_skimmerThingTimer != 0.0f || + GetForward().z < -0.5f && GetUp().z > -0.5f && m_vecMoveSpeed.z < -0.15f && + buoyanceImpulse.z > 0.01f*m_fMass * GRAVITY*CTimer::GetTimeStep() && + buoyanceImpulse.z < 0.4f*m_fMass * GRAVITY*CTimer::GetTimeStep()){ + float turnImpulse = -0.00017f*GetForward().z*buoyanceImpulse.z * m_fMass*CTimer::GetTimeStep(); + ApplyTurnForce(turnImpulse*GetForward(), GetUp()); + bBoatInWater = false; + //BUG? aren't we forgetting the timestep here? + float moveImpulse = -0.5f*DotProduct(m_vecMoveSpeed, GetForward()) * m_fMass; + ApplyMoveForce(moveImpulse*GetForward()); + if(m_skimmerThingTimer == 0.0f) + m_skimmerThingTimer = CTimer::GetTimeInMilliseconds() + 300.0f; + else if(m_skimmerThingTimer < CTimer::GetTimeInMilliseconds()) + m_skimmerThingTimer = 0.0f; + } if(!onLand && bBoatInWater && GetUp().z > 0.0f){ - float impulse; - if(m_fGasPedal > 0.05f) - impulse = m_vecMoveSpeed.MagnitudeSqr()*pHandling->fSuspensionForceLevel*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f*m_fGasPedal; + float impulse = m_vecMoveSpeed.MagnitudeSqr()*pBoatHandling->fAqPlaneForce*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f; + if(GetModelIndex() == MI_SKIMMER) + impulse *= 1.0f + m_fGasPedal; + else if(m_fGasPedal > 0.05f) + impulse *= m_fGasPedal; else impulse = 0.0f; - impulse = Min(impulse, GRAVITY*pHandling->fSuspensionDampingLevel*m_fMass*CTimer::GetTimeStep()); + impulse = Min(impulse, GRAVITY*pBoatHandling->fAqPlaneLimit*m_fMass*CTimer::GetTimeStep()); ApplyMoveForce(impulse*GetUp()); - ApplyTurnForce(impulse*GetUp(), buoyancePoint - pHandling->fSuspensionBias*GetForward()); + ApplyTurnForce(impulse*GetUp(), buoyancePoint - pBoatHandling->fAqPlaneOffset*GetForward()); } // Handle boat moving forward - if(Abs(m_fGasPedal) > 0.05f || m_vecMoveSpeed.Magnitude2D() > 0.01f){ - if(bBoatInWater) + float fwdSpeed = 1.0f; + if(Abs(m_fGasPedal) > 0.05f || (fwdSpeed = m_vecMoveSpeed.Magnitude2D()) > 0.01f){ + if(bBoatInWater && fwdSpeed > 0.05f) AddWakePoint(GetPosition()); - float steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward()); - if (GetModelIndex() == MI_GHOST) - steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward())*0.3f; - if(steerFactor < 0.0f) steerFactor = 0.0f; + float steerFactor = 1.0f; + if(GetStatus() == STATUS_PLAYER){ + float steerLoss = DotProduct(m_vecMoveSpeed, GetForward())*pHandling->fTractionBias; + if(CPad::GetPad(0)->GetHandBrake()) + steerLoss *= 0.5f; + steerFactor -= steerLoss; + steerFactor = clamp(steerFactor, 0.0f, 1.0f); + } - CVector propeller(0.0f, -pHandling->Dimension.y*m_fThrustY, -pHandling->Dimension.z*m_fThrustZ); + CVector boundMin = GetColModel()->boundingBox.min; + CVector propeller(0.0f, boundMin.y*pBoatHandling->fThrustY, boundMin.z*pBoatHandling->fThrustZ); propeller = Multiply3x3(GetMatrix(), propeller); CVector propellerWorld = GetPosition() + propeller; @@ -315,7 +401,11 @@ CBoat::ProcessControl(void) propellerDepth = SQR(propellerDepth); bPropellerInWater = true; - if(Abs(m_fGasPedal) > 0.05f){ + bool bSlowAhead = false; + if(Abs(m_fGasPedal) > 0.01f && GetModelIndex() != MI_SKIMMER){ + if(Abs(m_fGasPedal) < 0.05f) + bSlowAhead = true; + CVector forceDir = Multiply3x3(GetMatrix(), CVector(-steerSin, steerCos, -Abs(m_fSteerAngle))); CVector force = propellerDepth * m_fGasPedal * 40.0f * pHandling->Transmission.fEngineAcceleration * pHandling->fMass * forceDir; if(force.z > 0.2f) @@ -330,191 +420,326 @@ CBoat::ProcessControl(void) ApplyMoveForce(force * CTimer::GetTimeStep()); }else{ ApplyMoveForce(force * CTimer::GetTimeStep()); - ApplyTurnForce(force * CTimer::GetTimeStep(), propeller - pHandling->fTractionBias*GetUp()); - float rightForce = DotProduct(GetRight(), force); - ApplyTurnForce(-rightForce*GetRight() * CTimer::GetTimeStep(), GetUp()); + ApplyTurnForce(force * CTimer::GetTimeStep(), propeller - pBoatHandling->fThrustAppZ*GetUp()); + float rightForce = -DotProduct(GetRight(), force)*pHandling->fTractionMultiplier; + ApplyTurnForce(rightForce*GetRight() * CTimer::GetTimeStep(), GetUp()); } // Spray some particles CVector jetDir = -0.04f * force; if(m_fGasPedal > 0.0f){ if(GetStatus() == STATUS_PLAYER){ - bool cameraHack = TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || - TheCamera.WhoIsInControlOfTheCamera == CAMCONTROL_OBBE; CVector sternPos = GetColModel()->boundingBox.min; sternPos.x = 0.0f; sternPos.z = 0.0f; sternPos = Multiply3x3(GetMatrix(), sternPos); - CVector jetPos = GetPosition() + sternPos; - if(cameraHack) - jetPos.z = 1.0f; - else - jetPos.z = 0.0f; - -#ifdef PC_PARTICLE - CVector wakePos = GetPosition() + sternPos; - wakePos.z -= 0.65f; -#else CVector wakePos = GetPosition() + sternPos; - wakePos.z = -0.3f; -#endif - - CVector wakeDir = 0.75f * jetDir; - - CParticle::AddParticle(PARTICLE_BOAT_THRUSTJET, jetPos, jetDir, nil, 0.0f, jetColor); -#ifdef PC_PARTICLE - CParticle::AddParticle(PARTICLE_CAR_SPLASH, jetPos, 0.25f * jetDir, nil, 1.0f, splashColor, - CGeneral::GetRandomNumberInRange(0, 30), - CGeneral::GetRandomNumberInRange(0, 90), 3); -#endif - 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; - jetDir.x *= 0.01f; - jetDir.y *= 0.01f; - propellerWorld.z += 1.5f; + // no actual particles for player... + }else if(IsVisible() && ((CTimer::GetFrameCounter() + m_randomSeed) & 1) && + CVisibilityPlugins::GetDistanceSquaredFromCamera((RwV3d*)&propellerWorld) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + jetDir.z = 0.015f; + jetDir.x *= 3.5f; + jetDir.y *= 3.5f; + propellerWorld.z += 0.5f; - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 1.5f, jetColor); -#else - jetDir.z = 0.018f; - jetDir.x *= 0.03f; - jetDir.y *= 0.03f; - propellerWorld.z += 1.0f; + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 1.25f, jetColor, + CGeneral::GetRandomNumberInRange(0, 5), + CGeneral::GetRandomNumberInRange(0, 90), 1, 500); - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 0.0f, jetColor); -#endif - -#ifdef PC_PARTICLE - CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.1f * jetDir, nil, 0.5f, splashColor, + CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.75f * jetDir, nil, 0.5f, splashColor, CGeneral::GetRandomNumberInRange(0, 30), - CGeneral::GetRandomNumberInRange(0, 90), 3); -#endif + CGeneral::GetRandomNumberInRange(0, 45), 3, 500); } } - }else if(!onLand){ - float force = 50.0f*DotProduct(m_vecMoveSpeed, GetForward()); - force = Min(force, 10.0f); + }else + bSlowAhead = true; + + if(!onLand && bSlowAhead){ + float force = pHandling->fTractionLoss*DotProduct(m_vecMoveSpeed, GetForward()); + force = Min(force, 0.01f*m_fTurnMass); + if(m_fGasPedal > 0.01f){ + if(GetStatus() == STATUS_PLAYER) + force *= (0.55f - Abs(m_fGasPedal)) * 1.3f; + else + force *= (0.55f - Abs(m_fGasPedal)) * 2.5f; + } + if(m_fGasPedal < 0.0f && force > 0.0f || m_fGasPedal > 0.0f && force < 0.0f) + force *= -1.0f; CVector propellerForce = propellerDepth * Multiply3x3(GetMatrix(), force*CVector(-steerSin, 0.0f, 0.0f)); - ApplyMoveForce(propellerForce * CTimer::GetTimeStep()*0.5f); - ApplyTurnForce(propellerForce * CTimer::GetTimeStep()*0.5f, propeller); + ApplyMoveForce(propellerForce * CTimer::GetTimeStep()); + ApplyTurnForce(propellerForce * CTimer::GetTimeStep(), propeller); + float rightForce = -steerSin * force * 0.75f/steerFactor * Max(CTimer::GetTimeStep(), 0.01f); + ApplyTurnForce(GetRight() * rightForce, GetUp()); } }else bPropellerInWater = false; + + if(pHandling->fSuspensionBias != 0.0f){ + CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); + float rightSpeed = DotProduct(m_vecMoveSpeed, right); + float impulse = 0.1f*pHandling->fSuspensionBias * m_fMass * m_fVolumeUnderWater * rightSpeed * CTimer::GetTimeStep(); + ApplyMoveForce(right - impulse * 0.3f * CVector(-right.y, right.x, 0.0f)); + } + + if(GetStatus() == STATUS_PLAYER && CPad::GetPad(0)->GetHandBrake()){ + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(fwdSpeed > 0.0f){ + float impulse = -0.1f*pHandling->fSuspensionLowerLimit * m_fMass * m_fVolumeUnderWater * fwdSpeed * CTimer::GetTimeStep(); + ApplyMoveForce(impulse * GetForward()); + } + } } // 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) + if(!onLand && bBoatInWater && !bSeparateTurnForce) ApplyWaterResistance(); - // No idea what exactly is going on here besides drag in YZ - float fx = Pow(m_vecTurnRes.x, CTimer::GetTimeStep()); - float fy = Pow(m_vecTurnRes.y, CTimer::GetTimeStep()); - float fz = Pow(m_vecTurnRes.z, CTimer::GetTimeStep()); - m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space - // TODO: figure this out - float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx; - m_vecTurnSpeed.y *= fy; - m_vecTurnSpeed.z *= fz; - float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass; - m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world - CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); - ApplyTurnForce(CVector(0.0f, 0.0f, forceUp), com + GetForward()); + if((GetModelIndex() != MI_SKIMMER || m_skimmerThingTimer == 0.0f) && !bSeparateTurnForce){ + // No idea what exactly is going on here besides drag in YZ + float fx = Pow(pBoatHandling->vecTurnRes.x, CTimer::GetTimeStep()); + float fy = Pow(pBoatHandling->vecTurnRes.y, CTimer::GetTimeStep()); + float fz = Pow(pBoatHandling->vecTurnRes.z, CTimer::GetTimeStep()); + m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space + // TODO: figure this out + float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx; + m_vecTurnSpeed.y *= fy; + m_vecTurnSpeed.z *= fz; + float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass; + m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + ApplyTurnForce(forceUp*GetUp(), com + GetForward()); + } m_nDeltaVolumeUnderWater = (m_fVolumeUnderWater-m_fPrevVolumeUnderWater)*10000; // Falling into water - if(!onLand && bBoatInWater && GetUp().z > 0.0f && m_nDeltaVolumeUnderWater > 200){ - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, m_nDeltaVolumeUnderWater); - - float speedUp = m_vecMoveSpeed.MagnitudeSqr() * m_nDeltaVolumeUnderWater * 0.0004f; - if(speedUp + m_vecMoveSpeed.z > pHandling->fBrakeDeceleration) - 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; - CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); - CVector splashImpulse = speed * m_fMass; - ApplyMoveForce(splashImpulse); - ApplyTurnForce(splashImpulse, buoyancePoint); + if(!onLand && bBoatInWater && GetUp().z > 0.0f){ + float splashVol = m_nDeltaVolumeUnderWater*pBoatHandling->fWaveAudioMult; + if(splashVol > 200.0f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, splashVol); + + if(m_nDeltaVolumeUnderWater > 200){ + float speedUp = m_vecMoveSpeed.MagnitudeSqr() * m_nDeltaVolumeUnderWater * 0.001f; + if(speedUp + m_vecMoveSpeed.z > pHandling->fBrakeDeceleration) + 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->fBrakeBias; + CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); + CVector splashImpulse = speed * m_fMass; + ApplyMoveForce(splashImpulse); + ApplyTurnForce(splashImpulse, buoyancePoint); + } } - // Spray particles on sides of boat -#ifdef PC_PARTICLE - if(m_nDeltaVolumeUnderWater > 75) -#else - if(m_nDeltaVolumeUnderWater > 120) -#endif - { - float speed = m_vecMoveSpeed.Magnitude(); - float splash1Size = speed; - float splash2Size = float(m_nDeltaVolumeUnderWater) * 0.005f * 0.2f; - float front = 0.9f * GetColModel()->boundingBox.max.y; - if(splash1Size > 0.75f) splash1Size = 0.75f; - - CVector dir, pos; - - // right -#ifdef PC_PARTICLE - dir = -0.5f*m_vecMoveSpeed; - dir.z += 0.1f*speed; - dir += 0.5f*GetRight()*speed; - pos = front*GetForward() + 0.5f*GetRight() + GetPosition() + m_vecBuoyancePoint; - CWaterLevel::GetWaterLevel(pos, &pos.z, true); -#else - dir = 0.3f*m_vecMoveSpeed; - dir.z += 0.05f*speed; - dir += 0.5f*GetRight()*speed; - pos = (GetPosition() + m_vecBuoyancePoint) + (1.5f*GetRight()); -#endif - -#ifdef PC_PARTICLE - CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, 0.75f * dir, nil, splash1Size, splashColor, - CGeneral::GetRandomNumberInRange(0, 30), - CGeneral::GetRandomNumberInRange(0, 90), 1); - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size, jetColor); -#else - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size); -#endif - - - // left -#ifdef PC_PARTICLE - dir = -0.5f*m_vecMoveSpeed; - dir.z += 0.1f*speed; - dir -= 0.5f*GetRight()*speed; - pos = front*GetForward() - 0.5f*GetRight() + GetPosition() + m_vecBuoyancePoint; - CWaterLevel::GetWaterLevel(pos, &pos.z, true); -#else - dir = 0.3f*m_vecMoveSpeed; - dir.z += 0.05f*speed; - dir -= 0.5f*GetRight()*speed; - pos = (GetPosition() + m_vecBuoyancePoint) - (1.5f*GetRight()); -#endif - -#ifdef PC_PARTICLE - CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, 0.75f * dir, nil, splash1Size, splashColor, - CGeneral::GetRandomNumberInRange(0, 30), - CGeneral::GetRandomNumberInRange(0, 90), 1); - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size, jetColor); -#else - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size); -#endif + // Splashes + float speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.05f && GetUp().x > 0.0f && !TheCamera.GetLookingForwardFirstPerson() && IsVisible() && + (AutoPilot.m_nCarMission != MISSION_CRUISE || (CTimer::GetFrameCounter()&2) == 0)){ + CVector splashPos, splashDir; + float splashSize, front, waterLevel; + + switch(GetModelIndex()){ + case MI_RIO: + splashSize = speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.35f*speed*GetRight(); + splashPos = GetPosition() + 1.85f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_SQUALO: + splashSize = speed; + front = 0.75f * GetColModel()->boundingBox.max.y; + splashDir = -0.125f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir += 0.25f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_REEFER: + splashSize = speed; + front = 0.75f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir += 0.5f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 1.3f*GetRight() + front*GetForward(); + break; + case MI_COASTG: + splashSize = 0.25f*speed; + front = 0.8f * GetColModel()->boundingBox.max.y; + splashDir = 0.165f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.15f*speed*GetRight(); + splashPos = GetPosition() + 0.65f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_DINGHY: + splashSize = 0.25f*speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = 0.35f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.25f*speed*GetRight(); + splashPos = GetPosition() + 0.6f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + default: + splashSize = speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.35f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); + break; + } + if(splashSize > 0.75f) splashSize = 0.75f; + if(AutoPilot.m_nCarMission == MISSION_CRUISE) + splashDir *= 1.5f; + static float lifeMult = 1000.0f; + static float lifeBase = 300.0f; + splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; + CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); + if(splashPos.z-waterLevel < 3.0f && + CVisibilityPlugins::GetDistanceSquaredFromCamera((RwV3d*)&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + splashPos.z = waterLevel + 0.1f; + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + 1, lifeBase + splashDir.z*lifeMult); + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), + 0, lifeBase + splashDir.z*lifeMult); + } + + switch(GetModelIndex()){ + case MI_RIO: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.35f*speed*GetRight(); + splashPos = GetPosition() - 1.85f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_SQUALO: + splashDir = -0.125f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir -= 0.25f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_REEFER: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir -= 0.5f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 1.3f*GetRight() + front*GetForward(); + break; + case MI_COASTG: + splashDir = 0.165f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.15f*speed*GetRight(); + splashPos = GetPosition() - 0.65f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_DINGHY: + splashDir = 0.35f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.25f*speed*GetRight(); + splashPos = GetPosition() - 0.6f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + default: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.35f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); + break; + } + if(AutoPilot.m_nCarMission == MISSION_CRUISE) + splashDir *= 1.5f; + splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; + CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); + if(splashPos.z-waterLevel < 3.0f && + CVisibilityPlugins::GetDistanceSquaredFromCamera((RwV3d*)&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + splashPos.z = waterLevel + 0.1f; + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + 1, lifeBase + splashDir.z*lifeMult); + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), + 0, lifeBase + splashDir.z*lifeMult); + } + } + + // Spray waterdrops on screen + if(TheCamera.GetLookingForwardFirstPerson() && FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && + m_nDeltaVolumeUnderWater > 0 && numWaterDropOnScreen < 20){ + CVector dropPos; + CVector dropDir(CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(1.0f, 0.75f), 0.0f); + + int frm = CGeneral::GetRandomNumber() & 1; + if(TheCamera.m_CameraAverageSpeed < 0.35f){ + dropPos.x = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_WIDTH-50); + dropPos.y = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_HEIGHT-50); + }else{ + dropPos.x = CGeneral::GetRandomNumberInRange(200, (int)SCREEN_WIDTH-200); + dropPos.y = CGeneral::GetRandomNumberInRange(150, (int)SCREEN_HEIGHT-150); + } + dropPos.z = 1.0f; + + if(TheCamera.m_CameraAverageSpeed > 0.35f){ + if((int)SCREEN_WIDTH / 2 < dropPos.x) + dropPos.x += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + else + dropPos.x -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + + if((int)SCREEN_HEIGHT / 2 < dropPos.y) + dropPos.y += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + else + dropPos.y -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + } + + if(CParticle::AddParticle(PARTICLE_WATERDROP, dropPos, dropDir, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.15f), dropColor, 0, 0, frm)) + numWaterDropOnScreen++; + } + + if(m_fPrevVolumeUnderWater == 0.0f && m_fVolumeUnderWater > 0.0f && GetModelIndex() == MI_SKIMMER){ + CVector splashDir(0.0f, 0.0f, 0.25f*speed); + CVector splashPos = GetPosition(); + float level; + CWaterLevel::GetWaterLevel(splashPos, &level, true); + splashPos.z = level; + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, splashPos, splashDir, 0.0f, 65, splashColor, true); } m_fPrevVolumeUnderWater = m_fVolumeUnderWater; }else{ bBoatInWater = false; bIsInWater = false; +#ifdef FIX_BUGS + bIsDrowning = false; +#endif } + if(m_modelIndex == MI_SKIMMER && CTimer::GetTimeStep() > 0.0f){ + if(GetStatus() == STATUS_PLAYER){ + if(m_fMovingSpeed < 0.22f) + m_fMovingSpeed += 0.001f*CTimer::GetTimeStep(); + FlyingControl(FLIGHT_MODEL_SEAPLANE); + }else{ + if(m_fMovingSpeed > 0.0005f*CTimer::GetTimeStep()) + m_fMovingSpeed -= 0.0005f*CTimer::GetTimeStep(); + else + m_fMovingSpeed = 0.0f; + } + }else if(bCheat8) + FlyingControl(FLIGHT_MODEL_PLANE); + if(m_bIsAnchored){ m_vecMoveSpeed.x = 0.0f; m_vecMoveSpeed.y = 0.0f; @@ -547,7 +772,7 @@ CBoat::ProcessControlInputs(uint8 pad) m_fAccelerate += (CPad::GetPad(pad)->GetAccelerate()/255.0f - m_fAccelerate)*0.1f; m_fAccelerate = clamp(m_fAccelerate, 0.0f, 1.0f); }else - m_fAccelerate = -m_fBrake*0.2f; + m_fAccelerate = -m_fBrake*0.3f; m_fSteeringLeftRight += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteeringLeftRight)*0.2f; m_fSteeringLeftRight = clamp(m_fSteeringLeftRight, -1.0f, 1.0f); @@ -557,17 +782,21 @@ CBoat::ProcessControlInputs(uint8 pad) m_fGasPedal = m_fAccelerate; } +float fSeaPlaneWaterResistance = 30.0f; + void CBoat::ApplyWaterResistance(void) { - float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward()); // TODO: figure out how this works - float resistance = 0.001f * SQR(m_fVolumeUnderWater) * m_fMass; + float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass; + if(GetModelIndex() == MI_SKIMMER) + resistance *= fSeaPlaneWaterResistance; + float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward()); float magic = (SQR(fwdSpeed) + 0.05f) * resistance + 1.0f; magic = Abs(magic); - float fx = Pow(m_vecMoveRes.x/magic, 0.5f*CTimer::GetTimeStep()); - float fy = Pow(m_vecMoveRes.y/magic, 0.5f*CTimer::GetTimeStep()); - float fz = Pow(m_vecMoveRes.z/magic, 0.5f*CTimer::GetTimeStep()); + float fx = Pow(pBoatHandling->vecMoveRes.x/magic, 0.5f*CTimer::GetTimeStep()); + float fy = Pow(pBoatHandling->vecMoveRes.y/magic, 0.5f*CTimer::GetTimeStep()); + float fz = Pow(pBoatHandling->vecMoveRes.z/magic, 0.5f*CTimer::GetTimeStep()); m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // invert - to local space m_vecMoveSpeed.x *= fx; @@ -616,19 +845,14 @@ CBoat::BlowUpCar(CEntity *culprit) m_nBombTimer = 0; TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); - if(this == FindPlayerVehicle()) - FindPlayerPed()->m_fHealth = 0.0f; // kill player - if(pDriver){ - CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); - pDriver->SetDead(); - pDriver->FlagToDestroyWhenNextProcessed(); - } + KillPedsInVehicle(); bEngineOn = false; bLightsOn = false; ChangeLawEnforcerState(false); - CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); + CExplosion::AddExplosion(this, culprit, EXPLOSION_BOAT, GetPosition(), 0); + CDarkel::RegisterCarBlownUpByPlayer(this); if(m_aBoatNodes[BOAT_MOVING] == nil) return; @@ -656,6 +880,7 @@ CBoat::BlowUpCar(CEntity *culprit) RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; // init object obj->m_fMass = 10.0f; @@ -695,30 +920,177 @@ CBoat::BlowUpCar(CEntity *culprit) RpAtomicSetFlags(atomic, 0); } -RwIm3DVertex KeepWaterOutVertices[4]; -RwImVertexIndex KeepWaterOutIndices[6]; - void -CBoat::Render() +CBoat::PreRender(void) { CMatrix matrix; + CVector pos; + RpAtomic *atomic; - if (m_aBoatNodes[BOAT_MOVING] != nil) { - matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + if(GetModelIndex() == MI_SKIMMER){ + m_fMovingRotation += m_fMovingSpeed*CTimer::GetTimeStep(); + if(m_fMovingRotation > TWOPI) m_fMovingRotation -= TWOPI; + int alpha = (1.0f - Min(2.0f*m_fMovingSpeed*8.0f/PI, 1.0f))*255.0f; + if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PLAYER_REMOTE || GetStatus() == STATUS_PLAYER_PLAYBACKFROMBUFFER){ + if(m_aBoatNodes[BOAT_RUDDER]){ + float sine = Sin(m_fSteerAngle); + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); + pos = matrix.GetPosition(); + matrix.SetRotate(0.0f, 0.0f, -m_fSteerAngle); + matrix.Rotate(0.0f, DEGTORAD(22.0f)*sine, 0.0f); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_FLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(-CPad::GetPad(0)->GetSteeringUpDown()/128.0f); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(-CPad::GetPad(0)->GetSteeringUpDown()/128.0f); + matrix.Translate(pos); + matrix.UpdateRW(); + } + } + if(m_aBoatNodes[BOAT_MOVING]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + pos = matrix.GetPosition(); + matrix.SetRotateY(m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, alpha); + } + if(m_aBoatNodes[BOAT_WINDSCREEN]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_WINDSCREEN])); + pos = matrix.GetPosition(); + matrix.SetRotateY(-m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_WINDSCREEN], GetBoatAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, Max(150-alpha, 0)); + } + //CShadows::StoreShadowForVehicle(this); + }else if(GetModelIndex() == MI_COASTG || GetModelIndex() == MI_DINGHY || GetModelIndex() == MI_RIO || + GetModelIndex() == MI_SQUALO || GetModelIndex() == MI_MARQUIS){ + if(m_aBoatNodes[BOAT_RUDDER]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + } - CVector pos = matrix.GetPosition(); - matrix.SetRotateZ(m_fMovingRotation); + if(GetModelIndex() == MI_RIO || GetModelIndex() == MI_MARQUIS){ + float axes[3] = { 0.0f, 0.0f, 0.0f }; + m_boom.Process(this); + axes[m_boom.m_nAxis] = m_boom.m_fAngle; + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotate(axes[0], axes[1], axes[2]); matrix.Translate(pos); - matrix.UpdateRW(); - if (CVehicle::bWheelsOnlyCheat) { - RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); + } + + if(GetModelIndex() == MI_RIO){ + // That little wind propeller + if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); + pos = matrix.GetPosition(); + + float flapHeading = matrix.GetForward().Heading(); + float boatHeading = GetForward().Heading(); + float rot = -DEGTORAD(45.0f) - (flapHeading + boatHeading); + // eh what? + rot = CGeneral::LimitRadianAngle(rot); + if(rot > HALFPI) rot = PI; + else if(rot < -HALFPI) rot = -PI; + rot = clamp(rot, -DEGTORAD(63.0f), DEGTORAD(63.0f)); + m_fMovingSpeed += (0.008f * CWeather::Wind + 0.002f) * rot; + m_fMovingSpeed *= Pow(0.9985f, CTimer::GetTimeStep())/(500.0f*SQR(m_fMovingSpeed) + 1.0f); + + matrix.SetRotateZ(flapHeading + m_fMovingSpeed*CTimer::GetTimeStep()); + matrix.Translate(pos); + matrix.UpdateRW(); } + if(m_aBoatNodes[BOAT_MOVING]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + pos = matrix.GetPosition(); + matrix.SetRotateY(m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + CVector wind = CVector(0.707f, 0.707f, 0.0f) * (CWeather::Wind + 0.15f)*0.4f; + m_fMovingRotation += (m_vecMoveSpeed + wind).Magnitude()*CTimer::GetTimeStep(); + } + }else if(GetModelIndex() == MI_PREDATOR || GetModelIndex() == MI_REEFER){ + if (m_aBoatNodes[BOAT_MOVING] != nil) { + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + + CVector pos = matrix.GetPosition(); + matrix.SetRotateZ(m_fMovingRotation); + matrix.Translate(pos); + + matrix.UpdateRW(); + if (CVehicle::bWheelsOnlyCheat) { + RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); + } + } + m_fMovingRotation += 0.02f * CTimer::GetTimeStep(); } - m_fMovingRotation += 0.05f; +} + +RwIm3DVertex KeepWaterOutVertices[4]; +RwImVertexIndex KeepWaterOutIndices[6]; + +void +CBoat::Render() +{ ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->SetVehicleColour(m_currentColour1, m_currentColour2); + m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; if (!CVehicle::bWheelsOnlyCheat) CEntity::Render(); + if(GetModelIndex() == MI_SKIMMER) + return; KeepWaterOutIndices[0] = 0; KeepWaterOutIndices[1] = 2; KeepWaterOutIndices[2] = 1; @@ -730,6 +1102,18 @@ CBoat::Render() RwIm3DVertexSetRGBA(&KeepWaterOutVertices[2], 255, 255, 255, 255); RwIm3DVertexSetRGBA(&KeepWaterOutVertices[3], 255, 255, 255, 255); switch (GetModelIndex()) { + case MI_RIO: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.3f, -1.016f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.3f, -1.016f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.3f, -2.832f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.3f, -2.832f, 0.51f); + break; + case MI_SQUALO: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.222f, 2.004f, 0.846f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.222f, 2.004f, 0.846f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.24f, -1.367f, 0.846f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.24f, -1.367f, 0.846f); + break; case MI_SPEEDER: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.15f, 3.61f, 1.03f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.15f, 3.61f, 1.03f); @@ -743,12 +1127,37 @@ CBoat::Render() RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.66f, -4.48f, 0.83f); break; case MI_PREDATOR: - default: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.45f, 1.9f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.45f, 1.9f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.45f, -3.75f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.45f, -3.75f, 0.96f); break; + case MI_TROPIC: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.886f, -2.347f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.886f, -2.347f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.886f, -4.67f, 0.842f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.886f, -4.67f, 0.842f); + break; + case MI_COASTG: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.663f, 3.565f, 0.382f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.663f, 3.565f, 0.382f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.087f, 0.83f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.087f, 0.83f, 0.381f); + break; + case MI_DINGHY: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.797f, 1.641f, 0.573f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.797f, 1.641f, 0.573f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -0.865f, -1.444f, 0.509f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 0.865f, -1.444f, 0.509f); + break; + case MI_MARQUIS: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.246f, -1.373f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.246f, -1.373f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.023f, -5.322f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.023f, -5.322f, 0.787f); + break; + default: + return; } KeepWaterOutVertices[0].u = 0.0f; KeepWaterOutVertices[0].v = 0.0f; @@ -767,6 +1176,28 @@ CBoat::Render() RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); RwIm3DEnd(); } + bool drawAnotherRect = false; + if(GetModelIndex() == MI_COASTG){ + drawAnotherRect = true; + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.087f, 0.831f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.087f, 0.831f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.097f, -2.977f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.097f, -2.977f, 0.381f); + } + if(drawAnotherRect){ + KeepWaterOutVertices[0].u = 0.0f; + KeepWaterOutVertices[0].v = 0.0f; + KeepWaterOutVertices[1].u = 1.0f; + KeepWaterOutVertices[1].v = 0.0f; + KeepWaterOutVertices[2].u = 0.0f; + KeepWaterOutVertices[2].v = 1.0f; + KeepWaterOutVertices[3].u = 1.0f; + KeepWaterOutVertices[3].v = 1.0f; + if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); + RwIm3DEnd(); + } + } RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); @@ -783,6 +1214,7 @@ CBoat::Teleport(CVector v) CWorld::Add(this); } +//--MIAMI: unused bool CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) { @@ -814,6 +1246,7 @@ CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) return numVerts != 0; } +//--MIAMI: unused float CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) { @@ -850,22 +1283,35 @@ CBoat::FillBoatList() apFrameWakeGeneratingBoats[1] = nil; apFrameWakeGeneratingBoats[2] = nil; apFrameWakeGeneratingBoats[3] = nil; - + CVector2D camPos = TheCamera.GetPosition(); + CVector2D camFwd = TheCamera.GetForward(); + float camDist = camFwd.Magnitude(); + if(camDist > 0.0f) + camFwd /= camDist; for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) { CBoat *boat = (CBoat *)(CPools::GetVehiclePool()->GetSlot(i)); if (boat && boat->m_vehType == VEHICLE_TYPE_BOAT) { - int16 nNumWakePoints = boat->m_nNumWakePoints; - if (nNumWakePoints != 0) { + if (boat->m_nNumWakePoints != 0) { + CVector2D camToBoat = CVector2D(boat->GetPosition()) - camPos; + float distToCam = DotProduct2D(camFwd, camToBoat); + if(distToCam > 100.0f || distToCam < -15.0f) + continue; + float distSq = camToBoat.MagnitudeSqr(); + if(distSq > SQR(70.0f)) + continue; if (frameId >= ARRAY_SIZE(apFrameWakeGeneratingBoats)) { + float nearest = 999999.88f; int16 frameId2 = -1; for (int16 j = 0; j < ARRAY_SIZE(apFrameWakeGeneratingBoats); j++) { - if (apFrameWakeGeneratingBoats[j]->m_nNumWakePoints < nNumWakePoints) { + float tmpDistSq = (CVector2D(apFrameWakeGeneratingBoats[j]->GetPosition()) - camPos).MagnitudeSqr(); + if (tmpDistSq < nearest) { + nearest = tmpDistSq; frameId2 = j; - nNumWakePoints = apFrameWakeGeneratingBoats[j]->m_nNumWakePoints; } } - if (frameId2 != -1) + if (frameId2 != -1 && + (distSq < nearest || boat->GetStatus() == STATUS_PLAYER)) apFrameWakeGeneratingBoats[frameId2] = boat; } else { apFrameWakeGeneratingBoats[frameId++] = boat; @@ -897,23 +1343,96 @@ CBoat::AddWakePoint(CVector point) { int i; if(m_afWakePointLifeTime[0] > 0.0f){ - if((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(1.0f)){ - for(i = Min(m_nNumWakePoints, ARRAY_SIZE(m_afWakePointLifeTime)-1); i != 0; i--){ + if((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(2.0f)) { + if(GetStatus() == STATUS_PLAYER){ + if(m_nNumWakePoints >= 31) + m_nNumWakePoints = 31; + }else if(VehicleCreatedBy == MISSION_VEHICLE){ + if(m_nNumWakePoints >= 20) + m_nNumWakePoints = 20; + }else{ + if(m_nNumWakePoints >= 15) + m_nNumWakePoints = 15; + } + for(i = m_nNumWakePoints; i != 0; i--){ m_avec2dWakePoints[i] = m_avec2dWakePoints[i-1]; m_afWakePointLifeTime[i] = m_afWakePointLifeTime[i-1]; } m_avec2dWakePoints[0] = point; - m_afWakePointLifeTime[0] = 400.0f; + m_afWakePointLifeTime[0] = 150.0f; if(m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime)) m_nNumWakePoints++; } }else{ m_avec2dWakePoints[0] = point; - m_afWakePointLifeTime[0] = 400.0f; + m_afWakePointLifeTime[0] = 150.0f; m_nNumWakePoints = 1; } } +void +CBoat::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->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 || + TheCamera.m_bObbeCinematicCarCamOn){ + 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, true); + 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 56aff264..5f8cc8a8 100644 --- a/src/vehicles/Boat.h +++ b/src/vehicles/Boat.h @@ -1,33 +1,38 @@ #pragma once #include "Vehicle.h" +#include "Door.h" enum eBoatNodes { BOAT_MOVING = 1, - BOAT_RUDDER, BOAT_WINDSCREEN, + BOAT_RUDDER, + BOAT_FLAP_LEFT, + BOAT_FLAP_RIGHT, + BOAT_REARFLAP_LEFT, + BOAT_REARFLAP_RIGHT, NUM_BOAT_NODES }; class CBoat : public CVehicle { public: - // 0x288 - float m_fThrustZ; - float m_fThrustY; - CVector m_vecMoveRes; - CVector m_vecTurnRes; float m_fMovingRotation; + float m_fMovingSpeed; int32 m_boat_unused1; RwFrame *m_aBoatNodes[NUM_BOAT_NODES]; + CDoor m_boom; + tBoatHandlingData *pBoatHandling; uint8 bBoatInWater : 1; uint8 bPropellerInWater : 1; bool m_bIsAnchored; float m_fOrientation; + uint32 m_nPoliceShoutTimer; int32 m_boat_unused2; float m_fDamage; CEntity *m_pSetOnFireEntity; + float m_skimmerThingTimer; bool m_boat_unused3; float m_fAccelerate; float m_fBrake; @@ -42,12 +47,16 @@ 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); virtual void ProcessControl(); virtual void Teleport(CVector v); - virtual void PreRender(void) {}; + virtual void PreRender(void); virtual void Render(void); virtual void ProcessControlInputs(uint8); virtual void GetComponentWorldPosition(int32 component, CVector &pos); @@ -58,6 +67,7 @@ public: void SetupModelNodes(); void PruneWakeTrail(void); void AddWakePoint(CVector point); + void DoDriveByShootings(void); static CBoat *apFrameWakeGeneratingBoats[4]; @@ -71,10 +81,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 dd727e2b..84a19b82 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,10 @@ #include "Streaming.h" #include "Timer.h" #include "Vehicle.h" +#include "VisibilityPlugins.h" #include "World.h" +#include "Zones.h" +#include "Occlusion.h" uint8 CTheCarGenerators::ProcessCounter; uint32 CTheCarGenerators::NumOfCarGenerators; @@ -38,21 +42,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 +90,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,30 +105,45 @@ 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); + /* I don't think this is a correct comparasion */ #ifdef FIX_BUGS - if (m_nUsesRemaining != 0) + if (m_nUsesRemaining != 0) --m_nUsesRemaining; #else if (m_nUsesRemaining < -1) @@ -126,7 +158,7 @@ void CCarGenerator::Process() { if (m_nVehicleHandle == -1 && (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) && - m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayer()) + m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayers()) DoInternalProcessing(); if (m_nVehicleHandle == -1) return; @@ -160,25 +192,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() +bool CCarGenerator::CheckIfWithinRangeOfAnyPlayers() { 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.IsSphereVisible(m_vecPos, 0.0f) && COcclusion::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..fccbee96 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,8 +29,8 @@ 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 CheckIfWithinRangeOfAnyPlayer(); + bool CheckForBlockage(int32 mi); + bool CheckIfWithinRangeOfAnyPlayers(); void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } }; diff --git a/src/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp index 757974a6..83c727f5 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() || @@ -83,10 +84,8 @@ void CCranes::AddThisOneCrane(CEntity* pEntity) pCrane->m_nTimeForNextCheck = 0; pCrane->m_nCraneState = CCrane::IDLE; pCrane->m_bWasMilitaryCrane = false; - pCrane->m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[NumCranes]); - 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 +98,7 @@ void CCranes::AddThisOneCrane(CEntity* pEntity) pCrane->SetHookMatrix(); } else +#endif pCrane->m_pHook = nil; NumCranes++; } @@ -460,7 +460,7 @@ bool CCrane::DoesCranePickUpThisCarType(uint32 mi) return mi == MI_FIRETRUCK || mi == MI_AMBULAN || mi == MI_ENFORCER || - mi == MI_FBICAR || + mi == MI_FBIRANCH || mi == MI_RHINO || mi == MI_BARRACKS || mi == MI_POLICE; @@ -474,7 +474,7 @@ bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) case MI_FIRETRUCK: return (CarsCollectedMilitaryCrane & 1); case MI_AMBULAN: return (CarsCollectedMilitaryCrane & 2); case MI_ENFORCER: return (CarsCollectedMilitaryCrane & 4); - case MI_FBICAR: return (CarsCollectedMilitaryCrane & 8); + case MI_FBIRANCH: return (CarsCollectedMilitaryCrane & 8); case MI_RHINO: return (CarsCollectedMilitaryCrane & 0x10); case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20); case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40); @@ -489,7 +489,7 @@ void CCranes::RegisterCarForMilitaryCrane(uint32 mi) case MI_FIRETRUCK: CarsCollectedMilitaryCrane |= 1; break; case MI_AMBULAN: CarsCollectedMilitaryCrane |= 2; break; case MI_ENFORCER: CarsCollectedMilitaryCrane |= 4; break; - case MI_FBICAR: CarsCollectedMilitaryCrane |= 8; break; + case MI_FBIRANCH: CarsCollectedMilitaryCrane |= 8; break; case MI_RHINO: CarsCollectedMilitaryCrane |= 0x10; break; case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break; case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break; @@ -666,11 +666,11 @@ void CCranes::Load(uint8* buf, uint32 size) if (pCrane->m_pVehiclePickedUp != nil) pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1); } - for (int i = 0; i < NUM_CRANES; i++) { + /*for (int i = 0; i < NUM_CRANES; i++) { aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]); if (aCranes[i].m_nAudioEntity != 0) DMAudio.SetEntityStatus(aCranes[i].m_nAudioEntity, 1); - } + }*/ VALIDATESAVEBUF(size); } 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/Floater.cpp b/src/vehicles/Floater.cpp index 4331090d..92e3d80e 100644 --- a/src/vehicles/Floater.cpp +++ b/src/vehicles/Floater.cpp @@ -7,6 +7,8 @@ #include "Vehicle.h" #include "Floater.h" +//--MIAMI: done + cBuoyancy mod_Buoyancy; float fVolMultiplier = 1.0f; @@ -16,9 +18,29 @@ float fBoatVolumeDistribution[9] = { // rear 0.75f, 0.9f, 0.75f, 0.95f, 1.0f, 0.95f, - 0.3f, 0.7f, 0.3f + 0.4f, 0.7f, 0.4f // bow }; +float fBoatVolumeDistributionCat[9] = { + 0.9f, 0.3f, 0.9f, + 1.0f, 0.5f, 1.0f, + 0.95f, 0.4f, 0.95f +}; +float fBoatVolumeDistributionSail[9] = { + 0.55f, 0.95f, 0.55f, + 0.75f, 1.1f, 0.75f, + 0.3f, 0.8f, 0.3f +}; +float fBoatVolumeDistributionDinghy[9] = { + 0.65f, 0.85f, 0.65f, + 0.85f, 1.1f, 0.85f, + 0.65f, 0.95f, 0.65f +}; +float fBoatVolumeDistributionSpeed[9] = { + 0.7f, 0.9f, 0.7f, + 0.95f, 1.0f, 0.95f, + 0.6f, 0.7f, 0.6f +}; bool cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse) @@ -37,6 +59,76 @@ cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVec return f != 0.0f; } +bool +cBuoyancy::ProcessBuoyancyBoat(CVehicle *veh, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce) +{ + m_numSteps = 2.0f; + + if(!CWaterLevel::GetWaterLevel(veh->GetPosition(), &m_waterlevel, veh->bTouchingWater)) + return false; + m_matrix = veh->GetMatrix(); + PreCalcSetup(veh, buoyancy); + + + float x, y; + int ix, i; + tWaterLevel waterPosition; + CVector waterNormal; + + // Floater is divided into 3x3 parts. Process and sum each of them + float volDiv = 1.0f/((m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f)); + ix = 0; + for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ + i = ix; + for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ + CVector waterLevel(x, y, 0.0f); + FindWaterLevelNorm(m_positionZ, &waterLevel, &waterPosition, &waterNormal); + switch(veh->GetModelIndex()){ + case MI_RIO: + fVolMultiplier = fBoatVolumeDistributionCat[i]; + break; + case MI_SQUALO: + case MI_SPEEDER: + case MI_JETMAX: + fVolMultiplier = fBoatVolumeDistributionSpeed[i]; + break; + case MI_COASTG: + case MI_DINGHY: + fVolMultiplier = fBoatVolumeDistributionDinghy[i]; + break; + case MI_MARQUIS: + fVolMultiplier = fBoatVolumeDistributionSail[i]; + break; + case MI_PREDATOR: + case MI_SKIMMER: + case MI_REEFER: + case MI_TROPIC: + default: + fVolMultiplier = fBoatVolumeDistribution[i]; + break; + } + if(waterPosition != FLOATER_ABOVE_WATER){ + float volume = SimpleSumBuoyancyData(waterLevel, waterPosition); + float upImpulse = volume * volDiv * buoyancy * CTimer::GetTimeStep(); + CVector speed = veh->GetSpeed(Multiply3x3(veh->GetMatrix(), CVector(x, y, 0.0f))); + float damp = 1.0f - DotProduct(speed, waterNormal)*veh->pHandling->fSuspensionDampingLevel; + float finalImpulse = upImpulse*Max(damp, 0.0f); + impulse->z += finalImpulse; + if(!bNoTurnForce) + veh->ApplyTurnForce(finalImpulse*waterNormal, Multiply3x3(m_matrix, waterLevel)); + } + i += 3; + } + ix++; + } + + m_volumeUnderWater *= volDiv; + + *point = Multiply3x3(m_matrix, m_impulsePoint); + return m_isBoat || m_haveVolume; + +} + void cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) { @@ -48,17 +140,55 @@ cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) m_dimMax = colModel->boundingBox.max; if(m_isBoat){ - if(phys->GetModelIndex() == MI_PREDATOR){ + switch(phys->GetModelIndex()){ + case MI_PREDATOR: + default: + m_dimMax.y *= 1.05f; + m_dimMin.y *= 0.9f; + break; + case MI_SPEEDER: + m_dimMax.y *= 1.25f; + m_dimMin.y *= 0.83f; + break; + case MI_REEFER: + m_dimMin.y *= 0.9f; + break; + case MI_RIO: + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + m_dimMax.z += 0.25f; + m_dimMin.z -= 0.2f; + break; + case MI_SQUALO: m_dimMax.y *= 0.9f; m_dimMin.y *= 0.9f; - }else if(phys->GetModelIndex() == MI_SPEEDER){ + break; + case MI_TROPIC: + m_dimMax.y *= 1.3f; + m_dimMin.y *= 0.82f; + m_dimMin.z -= 0.2f; + break; + case MI_SKIMMER: + m_dimMin.y = -m_dimMax.y; + m_dimMax.y *= 1.2f; + break; + case MI_COASTG: m_dimMax.y *= 1.1f; m_dimMin.y *= 0.9f; - }else if(phys->GetModelIndex() == MI_REEFER){ + m_dimMin.z -= 0.3f; + break; + case MI_DINGHY: + m_dimMax.y *= 1.3f; m_dimMin.y *= 0.9f; - }else{ - m_dimMax.y *= 0.9f; + m_dimMin.z -= 0.2f; + break; + case MI_MARQUIS: + m_dimMax.y *= 1.3f; m_dimMin.y *= 0.9f; + break; + case MI_JETMAX: + m_dimMin.y *= 0.9f; + break; } } @@ -92,22 +222,17 @@ void cBuoyancy::SimpleCalcBuoyancy(void) { float x, y; - int ix, i; tWaterLevel waterPosition; // Floater is divided into 3x3 parts. Process and sum each of them - ix = 0; for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ - i = ix; for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ CVector waterLevel(x, y, 0.0f); FindWaterLevel(m_positionZ, &waterLevel, &waterPosition); - fVolMultiplier = m_isBoat ? fBoatVolumeDistribution[i] : 1.0f; + fVolMultiplier = 1.0f; if(waterPosition != FLOATER_ABOVE_WATER) SimpleSumBuoyancyData(waterLevel, waterPosition); - i += 3; } - ix++; } m_volumeUnderWater /= (m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f); @@ -129,10 +254,6 @@ cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition) if(m_isBoat){ fThisVolume *= fVolMultiplier; - if(fThisVolume < 0.5f) - fThisVolume = 2.0f*sq(fThisVolume); - if(fThisVolume < 1.0f) - fThisVolume = sq(fThisVolume); fThisVolume = sq(fThisVolume); } @@ -173,6 +294,26 @@ cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel } } +// Same as above but also get normal +void +cBuoyancy::FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal) +{ + *waterPosition = FLOATER_IN_WATER; + CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); + CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, + &waterLevel->z, true); + waterLevel->z -= xWaterLevel.z + zpos.z; // make local + if(waterLevel->z >= m_dimMin.z) + *normal = CWaterLevel::GetWaterNormal(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y); + if(waterLevel->z > m_dimMax.z){ + waterLevel->z = m_dimMax.z; + *waterPosition = FLOATER_UNDER_WATER; + }else if(waterLevel->z < m_dimMin.z){ + waterLevel->z = m_dimMin.z; + *waterPosition = FLOATER_ABOVE_WATER; + } +} + bool cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse) { diff --git a/src/vehicles/Floater.h b/src/vehicles/Floater.h index 1cfb46fb..91ab70ae 100644 --- a/src/vehicles/Floater.h +++ b/src/vehicles/Floater.h @@ -36,10 +36,12 @@ public: CVector m_impulsePoint; bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse); + bool ProcessBuoyancyBoat(CVehicle *phys, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce); void PreCalcSetup(CPhysical *phys, float buoyancy); void SimpleCalcBuoyancy(void); float SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition); void FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition); + void FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal); bool CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point); }; extern cBuoyancy mod_Buoyancy; 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 2316cbef..a770f83d 100644 --- a/src/vehicles/Heli.cpp +++ b/src/vehicles/Heli.cpp @@ -14,20 +14,26 @@ #include "Shadows.h" #include "Coronas.h" #include "Explosion.h" +#include "WindModifiers.h" #include "Timecycle.h" #include "TempColModels.h" #include "World.h" #include "WaterLevel.h" +#include "Population.h" #include "PlayerPed.h" +#include "CopPed.h" #include "Wanted.h" #include "DMAudio.h" #include "Object.h" #include "HandlingMgr.h" +#include "Ropes.h" #include "Heli.h" #ifdef FIX_BUGS #include "Replay.h" #endif +//--MIAMI: done + enum { HELI_STATUS_HOVER, @@ -40,7 +46,6 @@ enum CHeli *CHeli::pHelis[NUM_HELIS]; int16 CHeli::NumRandomHelis; uint32 CHeli::TestForNewRandomHelisTimer; -int16 CHeli::NumScriptHelis; // unused bool CHeli::CatalinaHeliOn; bool CHeli::CatalinaHasBeenShotDown; bool CHeli::ScriptHeliOn; @@ -67,6 +72,9 @@ CHeli::CHeli(int32 id, uint8 CreatedBy) m_nBulletDamage = 0; m_fAngularSpeed = 0.0f; m_fRotation = 0.0f; + + m_numSwat = 4; + m_nSearchLightTimer = CTimer::GetTimeInMilliseconds(); for(i = 0; i < 6; i++){ m_aSearchLightHistoryX[i] = 0.0f; @@ -82,6 +90,8 @@ CHeli::CHeli(int32 id, uint8 CreatedBy) m_fTargetOffset = 0.0f; m_fSearchLightX = m_fSearchLightY = 0.0f; + m_aSwatState[0] = m_aSwatState[1] = m_aSwatState[2] = m_aSwatState[3] = 0; + // BUG: not in game but gets initialized to CDCDCDCD in debug m_nLastShotTime = 0; } @@ -120,6 +130,8 @@ CHeli::ProcessControl(void) if(gbModelViewer) return; + CWindModifiers::RegisterOne(GetPosition(), 1); + // Find target CVector target(0.0f, 0.0f, 0.0f); CVector2D vTargetDist; @@ -266,9 +278,9 @@ CHeli::ProcessControl(void) if(fTargetDist > targetHeight) m_heliStatus = HELI_STATUS_CHASE_PLAYER; } -#ifdef FIX_BUGS + if(m_numSwat) + SendDownSwat(); break; -#endif case HELI_STATUS_CHASE_PLAYER:{ float targetHeight; if(m_heliType == HELI_TYPE_CATALINA) @@ -279,6 +291,7 @@ CHeli::ProcessControl(void) fTargetDist < targetHeight && CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)) m_heliStatus = HELI_STATUS_HOVER; } + break; } // Find xy speed @@ -348,13 +361,6 @@ CHeli::ProcessControl(void) if(m_fTargetOffset >= 2.0f) m_fTargetOffset -= 2.0f; - if(m_heliType == HELI_TYPE_CATALINA) - if(m_pathState == 9 || m_pathState == 11 || m_pathState == 10){ - float f = Pow(0.997f, CTimer::GetTimeStep()); - m_vecMoveSpeed.x *= f; - m_vecMoveSpeed.y *= f; - } - CVector2D speedDir = targetSpeed - m_vecMoveSpeed; float speedDiff = speedDir.Magnitude(); if(speedDiff != 0.0f) @@ -526,28 +532,17 @@ CHeli::ProcessControl(void) } } - // Drop Catalina's bombs - if(m_heliType == HELI_TYPE_CATALINA && m_pathState > 8 && (CTimer::GetTimeInMilliseconds()>>9) != (CTimer::GetPreviousTimeInMilliseconds()>>9)){ - CVector bombPos = GetPosition() - 60.0f*m_vecMoveSpeed; - if(sq(FindPlayerCoors().x-bombPos.x) + sq(FindPlayerCoors().y-bombPos.y) < sq(35.0f)){ - bool found; - float groundZ = CWorld::FindGroundZFor3DCoord(bombPos.x, bombPos.y, bombPos.z, &found); - float waterZ; - if(!CWaterLevel::GetWaterLevelNoWaves(bombPos.x, bombPos.y, bombPos.z, &waterZ)) - waterZ = 0.0f; - if(groundZ > waterZ){ - bombPos.z = groundZ + 2.0f; - CExplosion::AddExplosion(nil, this, EXPLOSION_HELI_BOMB, bombPos, 0); - }else{ - bombPos.z = waterZ; - CVector dir; - for(i = 0; i < 16; i++){ - dir.x = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f; - dir.y = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f; - dir.z = 0.5f; - CParticle::AddParticle(PARTICLE_BOAT_SPLASH, bombPos, dir, nil, 0.2f); - } - } + // Process ropes + for(i = 0; i < 4; i++){ + if(m_aSwatState[i] == 0) + continue; + + m_aSwatState[i]--; + CRopes::RegisterRope((uintptr)this + i, GetMatrix()*FindSwatPositionRelativeToHeli(i), false); + if(m_aSwatState[i] == 0){ + CVector speed = Multiply3x3(GetMatrix(), 0.05f*FindSwatPositionRelativeToHeli(i)); + speed.z = 0.0f; + CRopes::SetSpeedOfTopNode((uintptr)this + i, speed); } } @@ -560,60 +555,9 @@ 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)); + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); } void @@ -649,7 +593,7 @@ CHeli::PreRenderAlways(void) CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &shadowPos, 6.0f, 0.0f, 0.0f, -6.0f, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, - 50.0f, true, 1.0f); + 50.0f, true, 1.0f, nil, false); CVector front = GetMatrix() * CVector(0.0f, 7.0f, 0.0f); CVector toPlayer = FindPlayerCoors() - front; @@ -718,6 +662,7 @@ CHeli::SpawnFlyingComponent(int32 component) RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; // init object obj->m_fMass = 10.0f; @@ -763,6 +708,42 @@ CHeli::SpawnFlyingComponent(int32 component) return obj; } +CVector +CHeli::FindSwatPositionRelativeToHeli(int n) +{ + switch(n){ + case 0: return CVector(-1.2f, -1.0f, -0.5f); + case 1: return CVector( 1.2f, -1.0f, -0.5f); + case 2: return CVector(-1.2f, 1.0f, -0.5f); + case 3: return CVector( 1.2f, 1.0f, -0.5f); + default: return CVector(0.0f, 0.0f, 0.0f); + } +} + +bool +CHeli::SendDownSwat(void) +{ + if(m_numSwat == 0 || !CStreaming::HasModelLoaded(MI_SWAT) || + CGeneral::GetRandomNumber() & 0x7F || (GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) + return false; + + CMatrix mat(GetMatrix()); + CVector pos = Multiply3x3(mat, FindSwatPositionRelativeToHeli(m_numSwat-1)) + GetPosition(); + + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, nil); + if(Abs(FindPlayerCoors().z - groundZ) < 2.5f && CRopes::RegisterRope((uintptr)this + m_numSwat-1, pos, false)){ + CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos); + swat->bUsesCollision = false; + swat->m_pRopeEntity = this; + RegisterReference(&swat->m_pRopeEntity); + m_numSwat--; + swat->m_nRopeID = (uintptr)this + m_numSwat; + m_aSwatState[m_numSwat] = 255; + CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_ABSEIL, 4.0f); + return true; + } + return false; +} void @@ -772,13 +753,11 @@ CHeli::InitHelis(void) NumRandomHelis = 0; TestForNewRandomHelisTimer = 0; - NumScriptHelis = 0; CatalinaHeliOn = false; ScriptHeliOn = false; 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); } @@ -790,7 +769,7 @@ GenerateHeli(bool catalina) int i; if(catalina) - heli = new CHeli(MI_ESCAPE, PERMANENT_VEHICLE); + assert(0 && "can't create catalina's heli"); else heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE); @@ -801,7 +780,7 @@ GenerateHeli(bool catalina) float angle = (float)(CGeneral::GetRandomNumber() & 0xFF)/0x100 * 6.28f; heliPos.x += 250.0f*Sin(angle); heliPos.y += 250.0f*Cos(angle); - if(heliPos.x < -2000.0f || heliPos.x > 2000.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){ + if(heliPos.x < -2000.0f-400.0f || heliPos.x > 2000.0f-400.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){ heliPos = FindPlayerCoors(); heliPos.x -= 250.0f*Sin(angle); heliPos.y -= 250.0f*Cos(angle); @@ -842,6 +821,8 @@ CHeli::UpdateHelis(void) CReplay::IsPlayingBack() ? 0 : #endif FindPlayerPed()->m_pWanted->NumOfHelisRequired(); + if(CCullZones::PlayerNoRain() || CGame::IsInInterior()) + numHelisRequired = 0; if(CStreaming::HasModelLoaded(MI_CHOPPER) && CTimer::GetTimeInMilliseconds() > TestForNewRandomHelisTimer){ // Spawn a police heli TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 15000; @@ -870,18 +851,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){ @@ -902,7 +871,7 @@ CHeli::UpdateHelis(void) TheCamera.CamShake(0.7f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); colors[0] = CRGBA(0, 0, 0, 255); - colors[1] = CRGBA(224, 230, 238, 255); + colors[1] = CRGBA(224, 224, 224, 255); colors[2] = CRGBA(0, 0, 0, 255); colors[3] = CRGBA(0, 0, 0, 255); colors[4] = CRGBA(66, 162, 252, 255); @@ -922,7 +891,7 @@ CHeli::UpdateHelis(void) int f = ++nFrameGen & 3; CParticle::AddParticle(PARTICLE_HELI_DEBRIS, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), - colors[nFrameGen], rotSpeed, 0, f, 0); + colors[nFrameGen&7], rotSpeed, 0, f, 0); } CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0); @@ -940,7 +909,6 @@ CHeli::UpdateHelis(void) if(i == HELI_CATALINA) CatalinaHasBeenShotDown = true; - CStats::HelisDestroyed++; CStats::PeopleKilledByPlayer += 2; CStats::PedsKilledOfThisType[PEDTYPE_COP] += 2; CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250; @@ -960,7 +928,7 @@ CHeli::UpdateHelis(void) TheCamera.CamShake(0.4f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); CVector pos = pHelis[i]->GetPosition() - 2.5f*pHelis[i]->GetForward(); - CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0); + CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI2, pos, 0); }else pHelis[i]->m_fAngularSpeed *= 1.03f; } @@ -1036,6 +1004,26 @@ CHeli::TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, i return hit; } +bool +CHeli::TestSniperCollision(CVector *line0, CVector *line1) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++){ + CVector pilotPos = pHelis[i]->GetMatrix() * CVector(-0.43f, 1.49f, 1.5f); + if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pilotPos) < 0.8f){ + pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 9999999; + pHelis[i]->m_numSwat = 0; + + hit = true; + } + } + return hit; +} + void CHeli::StartCatalinaFlyBy(void) { CatalinaHeliOn = true; diff --git a/src/vehicles/Heli.h b/src/vehicles/Heli.h index cf3f791f..1f372e42 100644 --- a/src/vehicles/Heli.h +++ b/src/vehicles/Heli.h @@ -21,7 +21,7 @@ enum HELI_RANDOM0, HELI_RANDOM1, HELI_SCRIPT, - HELI_CATALINA, + HELI_CATALINA, // TODO 2 in VC NUM_HELIS }; @@ -36,7 +36,6 @@ enum class CHeli : public CVehicle { public: - // 0x288 RwFrame *m_aHeliNodes[NUM_HELI_NODES]; int8 m_heliStatus; float m_fSearchLightX; @@ -49,6 +48,8 @@ public: int8 m_nHeliId; int8 m_heliType; int8 m_pathState; + int8 m_numSwat; + uint8 m_aSwatState[4]; float m_aSearchLightHistoryX[6]; float m_aSearchLightHistoryY[6]; uint32 m_nSearchLightTimer; @@ -64,7 +65,6 @@ public: static CHeli *pHelis[NUM_HELIS]; static int16 NumRandomHelis; static uint32 TestForNewRandomHelisTimer; - static int16 NumScriptHelis; // unused static bool CatalinaHeliOn; static bool CatalinaHasBeenShotDown; static bool ScriptHeliOn; @@ -79,12 +79,15 @@ public: void PreRenderAlways(void); CObject *SpawnFlyingComponent(int32 component); + CVector FindSwatPositionRelativeToHeli(int n); + bool SendDownSwat(void); static void InitHelis(void); static void UpdateHelis(void); static void SpecialHeliPreRender(void); static bool TestRocketCollision(CVector *coors); static bool TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage); + static bool TestSniperCollision(CVector *line0, CVector *line1); static void StartCatalinaFlyBy(void); static void RemoveCatalinaHeli(void); @@ -95,6 +98,3 @@ public: static void ActivateHeli(bool activate); }; - -VALIDATE_SIZE(CHeli, 0x33C); - diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp index b8a957cf..ffe9ffbf 100644 --- a/src/vehicles/Plane.cpp +++ b/src/vehicles/Plane.cpp @@ -2,6 +2,7 @@ #include "main.h" #include "General.h" +#include "CutsceneMgr.h" #include "ModelIndices.h" #include "FileMgr.h" #include "Streaming.h" @@ -12,10 +13,14 @@ #include "Coronas.h" #include "Particle.h" #include "Explosion.h" +#include "Fluff.h" #include "World.h" #include "HandlingMgr.h" +#include "Heli.h" #include "Plane.h" +//--MIAMI: file done + CPlaneNode *pPathNodes; CPlaneNode *pPath2Nodes; CPlaneNode *pPath3Nodes; @@ -39,12 +44,10 @@ CPlaneInterpolationLine aPlaneLineBits[6]; float PlanePathPosition[3]; float OldPlanePathPosition[3]; float PlanePathSpeed[3]; -float PlanePath2Position[3]; -float PlanePath3Position; -float PlanePath4Position; -float PlanePath2Speed[3]; -float PlanePath3Speed; -float PlanePath4Speed; +float PlanePath2Position[5]; +float PlanePath3Position[4]; +float PlanePath2Speed[5]; +float PlanePath3Speed[4]; enum @@ -62,7 +65,6 @@ int32 DropOffCesnaMissionStatus; int32 DropOffCesnaMissionStartTime; CPlane *pDropOffCesna; - CPlane::CPlane(int32 id, uint8 CreatedBy) : CVehicle(CreatedBy) { @@ -80,13 +82,15 @@ CPlane::CPlane(int32 id, uint8 CreatedBy) m_bHasBeenHit = false; m_bIsDrugRunCesna = false; m_bIsDropOffCesna = false; + m_bTempPlane = false; SetStatus(STATUS_PLANE); bIsBIGBuilding = true; m_level = LEVEL_GENERIC; -#ifdef FIX_BUGS m_isFarAway = false; +#ifdef CPLANE_ROTORS + m_fRotorRotation = 0.0f; #endif } @@ -99,6 +103,21 @@ void CPlane::SetModelIndex(uint32 id) { CVehicle::SetModelIndex(id); +#ifdef CPLANE_ROTORS + int i; + for(i = 0; i < NUM_PLANE_NODES; i++) + m_aPlaneNodes[i] = nil; + if(GetModelIndex() == MI_CHOPPER){ + // This is surprisingly annoying... + RwFrame *heliNodes[NUM_HELI_NODES]; + for(i = 0; i < NUM_HELI_NODES; i++) + heliNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), heliNodes); + m_aPlaneNodes[PLANE_TOPROTOR] = heliNodes[HELI_TOPROTOR]; + m_aPlaneNodes[PLANE_BACKROTOR] = heliNodes[HELI_BACKROTOR]; + }else + CClumpModelInfo::FillFrameArray(GetClump(), m_aPlaneNodes); +#endif } void @@ -123,6 +142,15 @@ CPlane::ProcessControl(void) int i; CVector pos; + if(CReplay::IsPlayingBack()) + return; + + if(GetModelIndex() == MI_AIRTRAIN){ + if(GetPosition().z > 100.0f) + CPlaneTrails::RegisterPoint(GetPosition(), m_nPlaneId); + }else if(GetModelIndex() == MI_DEADDODO) + CPlaneBanners::RegisterPoint(GetPosition(), m_nPlaneId); + // Explosion if(m_bHasBeenHit){ // BUG: since this is all based on frames, you can skip the explosion processing when you go into the menu @@ -383,7 +411,6 @@ CPlane::ProcessControl(void) GetMatrix().GetRight() = right; GetMatrix().GetUp() = up; GetMatrix().GetForward() = fwd; - // Set speed m_vecMoveSpeed = fwd*PlanePathSpeed[m_nPlaneId]/60.0f; m_fSpeed = PlanePathSpeed[m_nPlaneId]/60.0f; @@ -397,26 +424,12 @@ CPlane::ProcessControl(void) float planePathSpeed; int numPathNodes; - if(m_bIsDrugRunCesna){ - planePathPosition = PlanePath3Position; + if(GetModelIndex() == MI_CHOPPER){ + planePathPosition = PlanePath3Position[m_nPlaneId]; totalLengthOfFlightPath = TotalLengthOfFlightPath3; pathNodes = pPath3Nodes; - planePathSpeed = PlanePath3Speed; + planePathSpeed = PlanePath3Speed[m_nPlaneId]; numPathNodes = NumPath3Nodes; - if(CesnaMissionStatus == CESNA_STATUS_LANDED){ - pDrugRunCesna = nil; - FlagToDestroyWhenNextProcessed(); - } - }else if(m_bIsDropOffCesna){ - planePathPosition = PlanePath4Position; - totalLengthOfFlightPath = TotalLengthOfFlightPath4; - pathNodes = pPath4Nodes; - planePathSpeed = PlanePath4Speed; - numPathNodes = NumPath4Nodes; - if(DropOffCesnaMissionStatus == CESNA_STATUS_LANDED){ - pDropOffCesna = nil; - FlagToDestroyWhenNextProcessed(); - } }else{ planePathPosition = PlanePath2Position[m_nPlaneId]; totalLengthOfFlightPath = TotalLengthOfFlightPath2; @@ -635,12 +648,36 @@ CPlane::PreRender(void) CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); } + +#ifdef CPLANE_ROTORS + CMatrix mat; + CVector pos; + m_fRotorRotation += 3.14f/6.5f; + if(m_fRotorRotation > 6.28f) + m_fRotorRotation -= 6.28f; + + if(m_aPlaneNodes[PLANE_TOPROTOR]){ + mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_TOPROTOR])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + } + if(m_aPlaneNodes[PLANE_BACKROTOR]){ + mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_BACKROTOR])); + pos = mat.GetPosition(); + mat.SetRotateX(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + } +#endif } void CPlane::Render(void) { - CEntity::Render(); + if(!CCutsceneMgr::IsRunning()) + CEntity::Render(); } #define CRUISE_SPEED (50.0f) @@ -658,11 +695,9 @@ CPlane::InitPlanes(void) pPathNodes = LoadPath("data\\paths\\flight.dat", NumPathNodes, TotalLengthOfFlightPath, true); // Figure out which nodes are on ground - CColPoint colpoint; - CEntity *entity; for(i = 0; i < NumPathNodes; i++){ - if(CWorld::ProcessVerticalLine(pPathNodes[i].p, 1000.0f, colpoint, entity, true, false, false, false, true, false, nil)){ - pPathNodes[i].p.z = colpoint.point.z; + if(pPathNodes[i].p.z < 14.0f){ + pPathNodes[i].p.z = 14.0f; pPathNodes[i].bOnGround = true; }else pPathNodes[i].bOnGround = false; @@ -689,7 +724,7 @@ CPlane::InitPlanes(void) aPlaneLineBits[0].position = position; aPlaneLineBits[0].speed = TAXI_SPEED; aPlaneLineBits[0].acceleration = 0.0f; - float dist = (TakeOffPoint-600.0f) - position; + float dist = (TakeOffPoint-500.0f) - position; time += dist/TAXI_SPEED; position += dist; @@ -698,9 +733,9 @@ CPlane::InitPlanes(void) aPlaneLineBits[1].time = time; aPlaneLineBits[1].position = position; aPlaneLineBits[1].speed = TAXI_SPEED; - aPlaneLineBits[1].acceleration = 618.75f/600.0f; - time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); - position += 600.0f; + aPlaneLineBits[1].acceleration = 618.75f/500.0f; + time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 500.0f; // Fly at cruise speed aPlaneLineBits[2].type = 1; @@ -717,9 +752,9 @@ CPlane::InitPlanes(void) aPlaneLineBits[3].time = time; aPlaneLineBits[3].position = position; aPlaneLineBits[3].speed = CRUISE_SPEED; - aPlaneLineBits[3].acceleration = -618.75f/600.0f; - time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); - position += 600.0f; + aPlaneLineBits[3].acceleration = -618.75f/500.0f; + time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 500.0f; // Taxi aPlaneLineBits[4].type = 1; @@ -740,22 +775,17 @@ CPlane::InitPlanes(void) TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED; } - // Mission Cesna + // Heli if(pPath3Nodes == nil){ pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false); TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED; } - // Mission Cesna - if(pPath4Nodes == nil){ - pPath4Nodes = LoadPath("data\\paths\\flight4.dat", NumPath4Nodes, TotalLengthOfFlightPath4, false); - TotalDurationOfFlightPath4 = TotalLengthOfFlightPath4/CRUISE_SPEED; - } - CStreaming::LoadAllRequestedModels(false); CStreaming::RequestModel(MI_AIRTRAIN, 0); CStreaming::LoadAllRequestedModels(false); + // NB: 3 hardcoded also in CPlaneTrails for(i = 0; i < 3; i++){ CPlane *plane = new CPlane(MI_AIRTRAIN, PERMANENT_VEHICLE); plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); @@ -765,20 +795,6 @@ CPlane::InitPlanes(void) plane->m_nCurPathNode = 0; CWorld::Add(plane); } - - - CStreaming::RequestModel(MI_DEADDODO, 0); - CStreaming::LoadAllRequestedModels(false); - - for(i = 0; i < 3; i++){ - CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); - plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); - plane->SetStatus(STATUS_ABANDONED); - plane->bIsLocked = true; - plane->m_nPlaneId = i; - plane->m_nCurPathNode = 0; - CWorld::Add(plane); - } } void @@ -810,8 +826,7 @@ CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool CPlaneNode *nodes = new CPlaneNode[numNodes]; 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 @@ -832,6 +847,10 @@ CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool return nodes; } +int32 LastTimeInPlane, LastTimeNotInPlane; +bool bCesnasActivated; +bool bHelisActivated; + void CPlane::UpdatePlanes(void) { @@ -874,25 +893,87 @@ CPlane::UpdatePlanes(void) t = TotalDurationOfFlightPath2/0x80000; PlanePath2Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; - PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/3) & 0x7FFFF)*t; - PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/3*2) & 0x7FFFF)*t; + PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/5) & 0x7FFFF)*t; + PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/5*2) & 0x7FFFF)*t; + PlanePath2Position[3] = CRUISE_SPEED * ((time + 0x80000/5*3) & 0x7FFFF)*t; + PlanePath2Position[4] = CRUISE_SPEED * ((time + 0x80000/5*4) & 0x7FFFF)*t; PlanePath2Speed[0] = CRUISE_SPEED*t; PlanePath2Speed[1] = CRUISE_SPEED*t; PlanePath2Speed[2] = CRUISE_SPEED*t; + PlanePath2Speed[3] = CRUISE_SPEED*t; + PlanePath2Speed[4] = CRUISE_SPEED*t; + + t = TotalDurationOfFlightPath3/0x80000; + PlanePath3Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; + PlanePath3Position[1] = CRUISE_SPEED * ((time + 0x80000/4) & 0x7FFFF)*t; + PlanePath3Position[2] = CRUISE_SPEED * ((time + 0x80000/4*2) & 0x7FFFF)*t; + PlanePath3Position[3] = CRUISE_SPEED * ((time + 0x80000/4*3) & 0x7FFFF)*t; + PlanePath3Speed[0] = CRUISE_SPEED*t; + PlanePath3Speed[1] = CRUISE_SPEED*t; + PlanePath3Speed[2] = CRUISE_SPEED*t; + PlanePath3Speed[3] = CRUISE_SPEED*t; + + if(FindPlayerVehicle() && (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)) + LastTimeInPlane = CTimer::GetTimeInMilliseconds(); + else + LastTimeNotInPlane = CTimer::GetTimeInMilliseconds(); + + if(CTimer::GetTimeInMilliseconds() - LastTimeNotInPlane > 10000){ + if(!bCesnasActivated){ + if(CStreaming::HasModelLoaded(MI_DEADDODO)){ + for(i = 0; i < 5; i++){ + CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->SetStatus(STATUS_ABANDONED); + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + plane->m_bTempPlane = true; + CWorld::Add(plane); + } + bCesnasActivated = true; + }else + CStreaming::RequestModel(MI_DEADDODO, 0); + } - if(CesnaMissionStatus == CESNA_STATUS_FLYING){ - PlanePath3Speed = CRUISE_SPEED*TotalDurationOfFlightPath3/0x20000; - PlanePath3Position = PlanePath3Speed * ((time - CesnaMissionStartTime) & 0x1FFFF); - if(time - CesnaMissionStartTime >= 128072) - CesnaMissionStatus = CESNA_STATUS_LANDED; - } + if(!bHelisActivated){ + if(CStreaming::HasModelLoaded(MI_CHOPPER)){ + for(i = 0; i < 4; i++){ + CPlane *plane = new CPlane(MI_CHOPPER, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->SetStatus(STATUS_ABANDONED); + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + plane->m_bTempPlane = true; + CWorld::Add(plane); + } + bHelisActivated = true; + }else + CStreaming::RequestModel(MI_CHOPPER, 0); + } + }else if(CTimer::GetTimeInMilliseconds() - LastTimeInPlane > 10000) + RemoveTemporaryPlanes(); +} - if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){ - PlanePath4Speed = CRUISE_SPEED*TotalDurationOfFlightPath4/0x80000; - PlanePath4Position = PlanePath4Speed * ((time - DropOffCesnaMissionStartTime) & 0x7FFFF); - if(time - DropOffCesnaMissionStartTime >= 521288) - DropOffCesnaMissionStatus = CESNA_STATUS_LANDED; +void +CPlane::RemoveTemporaryPlanes(void) +{ + int i; + if(!bHelisActivated && !bCesnasActivated) + return; + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i); + if(plane && plane->IsPlane() && plane->m_bTempPlane){ + CWorld::Remove(plane); + delete plane; + } } + bCesnasActivated = false; + bHelisActivated = false; } bool @@ -920,6 +1001,7 @@ CPlane::TestRocketCollision(CVector *rocketPos) return false; } +//--MIAMI: unused // BUG: not in CPlane in the game void CPlane::CreateIncomingCesna(void) @@ -943,6 +1025,7 @@ CPlane::CreateIncomingCesna(void) printf("CPlane::CreateIncomingCesna(void)\n"); } +//--MIAMI: unused void CPlane::CreateDropOffCesna(void) { @@ -965,8 +1048,21 @@ CPlane::CreateDropOffCesna(void) printf("CPlane::CreateDropOffCesna(void)\n"); } +//--MIAMI: all unused const CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); } const CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDropOffCesna->GetPosition(); } bool CPlane::HasCesnaLanded(void) { return CesnaMissionStatus == CESNA_STATUS_LANDED; } bool CPlane::HasCesnaBeenDestroyed(void) { return CesnaMissionStatus == CESNA_STATUS_DESTROYED; } bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatus == CESNA_STATUS_DESTROYED; } + +void +CPlane::Load(void) +{ + RemoveTemporaryPlanes(); +} + +void +CPlane::Save(void) +{ + RemoveTemporaryPlanes(); +} diff --git a/src/vehicles/Plane.h b/src/vehicles/Plane.h index 783c53b3..c8f02048 100644 --- a/src/vehicles/Plane.h +++ b/src/vehicles/Plane.h @@ -4,6 +4,11 @@ enum ePlaneNodes { +#ifdef CPLANE_ROTORS + // for heli + PLANE_TOPROTOR, + PLANE_BACKROTOR, +#endif PLANE_WHEEL_FRONT = 2, PLANE_WHEEL_READ, NUM_PLANE_NODES @@ -29,7 +34,10 @@ struct CPlaneInterpolationLine class CPlane : public CVehicle { public: - // 0x288 +#ifdef CPLANE_ROTORS + RwFrame *m_aPlaneNodes[NUM_PLANE_NODES]; + float m_fRotorRotation; +#endif int16 m_nPlaneId; int16 m_isFarAway; int16 m_nCurPathNode; @@ -38,6 +46,7 @@ public: bool m_bHasBeenHit; bool m_bIsDrugRunCesna; bool m_bIsDropOffCesna; + bool m_bTempPlane; CPlane(int32 id, uint8 CreatedBy); ~CPlane(void); @@ -53,6 +62,7 @@ public: static void InitPlanes(void); static void Shutdown(void); static CPlaneNode *LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop); + static void RemoveTemporaryPlanes(void); static void UpdatePlanes(void); static bool TestRocketCollision(CVector *rocketPos); static void CreateIncomingCesna(void); @@ -62,10 +72,10 @@ public: static bool HasCesnaLanded(void); static bool HasCesnaBeenDestroyed(void); static bool HasDropOffCesnaBeenShotDown(void); + static void Load(void); + static void Save(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 26d0dee7..546f72c2 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); @@ -67,22 +68,28 @@ CTrain::CTrain(int32 id, uint8 CreatedBy) #ifdef FIX_BUGS m_isFarAway = true; #endif +#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; @@ -285,11 +292,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){ @@ -349,17 +358,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; @@ -368,11 +381,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; @@ -385,11 +400,13 @@ CTrain::AddPassenger(CPed *ped) return; } } +#endif } void CTrain::OpenTrainDoor(float ratio) { +#ifdef GTA_TRAIN if(m_rwObject == nil) return; @@ -414,6 +431,7 @@ CTrain::OpenTrainDoor(float ratio) doorL.UpdateRW(); doorR.UpdateRW(); +#endif } @@ -421,6 +439,7 @@ CTrain::OpenTrainDoor(float ratio) void CTrain::InitTrains(void) { +#ifdef GTA_TRAIN int i, j; CTrain *train; @@ -487,21 +506,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; @@ -623,6 +646,7 @@ CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int1 // end interpLines[j].time = *totalDuration; +#endif } void @@ -672,6 +696,7 @@ ProcessTrainAnnouncements(void) void CTrain::UpdateTrains(void) { +#ifdef GTA_TRAIN int i, j; uint32 time; float t, deltaT; @@ -735,4 +760,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 9aa90845..378a4c0a 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,20 +16,39 @@ #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; bool CVehicle::bCheat3; bool CVehicle::bCheat4; bool CVehicle::bCheat5; +bool CVehicle::bCheat8; #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 +76,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; @@ -102,6 +124,26 @@ CVehicle::CVehicle(uint8 CreatedBy) m_bSirenOrAlarm = false; 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; @@ -109,7 +151,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; @@ -121,6 +182,7 @@ CVehicle::CVehicle(uint8 CreatedBy) AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); AutoPilot.m_bStayInCurrentLevel = false; AutoPilot.m_bIgnorePathfinding = false; + AutoPilot.m_nSwitchDistance = 20; } CVehicle::~CVehicle() @@ -170,7 +232,7 @@ CVehicle::SetupLighting(void) }else{ CVector coors = GetPosition(); float lighting = CPointLights::GenerateLightsAffectingObject(&coors); - if(!bHasBlip && lighting != 1.0f){ + if(lighting != 1.0f){ SetAmbientAndDirectionalColours(lighting); return true; } @@ -185,57 +247,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: { @@ -276,6 +311,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); @@ -288,38 +325,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; @@ -327,38 +379,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) @@ -369,83 +408,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; @@ -457,8 +520,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, @@ -530,7 +813,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; @@ -570,15 +857,196 @@ 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_SIGNIFICANT_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()); } } @@ -600,35 +1068,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; @@ -645,6 +1159,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) && @@ -657,24 +1217,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_CAR, this); - } - for (int i = 0; i < m_nNumMaxPassengers; i++) { - if (pPassengers[i]) { - pPassengers[i]->bFleeAfterExitingCar = true; - pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, 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_CAR){ + 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_CAR, 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_CAR && + pPassengers[i]->CharCreatedBy != MISSION_CHAR) { + pPassengers[i]->bFleeAfterExitingCar = true; + pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); + pPassengers[i]->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; + pPassengers[i]->Say(SOUND_PED_FLEE_SPRINT); + time += 200; + } } } break; @@ -716,56 +1293,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()){ @@ -836,6 +1455,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_CAR, 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]->bHeldHostageInCar = true; + peds2[i]->bFleeAfterExitingCar = true; + } +} + +void CVehicle::ProcessDelayedExplosion(void) { if(m_nBombTimer == 0) @@ -854,8 +1536,6 @@ CVehicle::ProcessDelayedExplosion(void) if (m_nBombTimer != 0) return; - if(FindPlayerVehicle() != this && m_pBlowUpEntity == FindPlayerPed()) - CWorld::Players[CWorld::PlayerInFocus].AwardMoneyForExplosion(this); BlowUpCar(m_pBlowUpEntity); } @@ -863,12 +1543,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; @@ -876,9 +1557,9 @@ CVehicle::IsLawEnforcementVehicle(void) } bool -CVehicle::UsesSiren(uint32 id) +CVehicle::UsesSiren(void) { - switch(id){ + switch(GetModelIndex()){ case MI_FIRETRUCK: case MI_AMBULAN: case MI_FBICAR: @@ -886,6 +1567,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; @@ -897,24 +1580,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 @@ -922,9 +1588,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 @@ -991,7 +1656,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; @@ -999,10 +1665,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; @@ -1014,16 +1688,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 || @@ -1046,6 +1718,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) { @@ -1070,7 +1759,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; @@ -1083,15 +1772,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]; @@ -1104,23 +1809,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 @@ -1128,10 +1852,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){ @@ -1148,10 +1875,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; @@ -1165,6 +1895,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; } @@ -1190,6 +1936,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) { @@ -1199,9 +1996,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; } @@ -1229,6 +2027,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].Get(); + 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].Get(); + } + if(b2 != a1 && b2 != b1 && b2 != c1){ + // b2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[b2].Get(); + } + if(c2 != a1 && c2 != b1 && c2 != c1){ + // c2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[c2].Get(); + } + // Need exactly one vertex from tri2 for a quad with tri1 + if(numTri2Verts != 1) + continue; + + CVector mid = (vert1 + colmodel->vertices[b1].Get() + colmodel->vertices[c1].Get() + 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) { @@ -1370,3 +2449,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..34106327 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; @@ -17,6 +20,38 @@ enum { PERMANENT_VEHICLE = 4, }; +enum eCarNodes +{ + CAR_WHEEL_RF = 1, + CAR_WHEEL_RM, + CAR_WHEEL_RB, + CAR_WHEEL_LF, + CAR_WHEEL_LM, + CAR_WHEEL_LB, + CAR_BUMP_FRONT, + CAR_BUMP_REAR, + CAR_WING_RF, + CAR_WING_RR, + CAR_DOOR_RF, + CAR_DOOR_RR, + CAR_WING_LF, + CAR_WING_LR, + CAR_DOOR_LF, + CAR_DOOR_LR, + CAR_BONNET, + CAR_BOOT, + CAR_WINDSCREEN, + NUM_CAR_NODES, +}; + +enum { + CAR_DOOR_FLAG_UNKNOWN = 0x0, + CAR_DOOR_FLAG_LF = 0x1, + CAR_DOOR_FLAG_LR = 0x2, + CAR_DOOR_FLAG_RF = 0x4, + CAR_DOOR_FLAG_RR = 0x8 +}; + enum eCarLock { CARLOCK_NOT_USED, CARLOCK_UNLOCKED, @@ -28,6 +63,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 +142,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 +238,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 +270,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 +307,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 +323,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,22 +332,31 @@ 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); + bool UsesSiren(void); bool IsVehicleNormal(void); bool CarHasRoof(void); bool IsUpsideDown(void); 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,32 +365,90 @@ 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; static bool bCheat3; static bool bCheat4; static bool bCheat5; + static bool bCheat8; #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); + +// Names of functions below are made up by us. + +// Used in III and VC. +inline int8 GetCarDoorFlag(int32 carnode) { + switch (carnode) { + case CAR_DOOR_LF: + return CAR_DOOR_FLAG_LF; + case CAR_DOOR_LR: + return CAR_DOOR_FLAG_LR; + case CAR_DOOR_RF: + return CAR_DOOR_FLAG_RF; + case CAR_DOOR_RR: + return CAR_DOOR_FLAG_RR; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} + +// VC. Accounts the case numMaxPassengers == 0, only for m_nGettingInFlags. +inline int8 GetEnterCarDoorFlag(int32 carnode, uint8 numMaxPassengers) { + switch (carnode) { + case CAR_DOOR_RF: + return CAR_DOOR_FLAG_RF; + case CAR_DOOR_RR: + return CAR_DOOR_FLAG_RR; + case CAR_DOOR_LF: + if (numMaxPassengers != 0) + return CAR_DOOR_FLAG_LF; + else + return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + case CAR_DOOR_LR: + if (numMaxPassengers != 0) + return CAR_DOOR_FLAG_LR; + else + return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} |