From 2141247e0829baec36c9011f0c660ad7d8e40dd8 Mon Sep 17 00:00:00 2001 From: aap Date: Wed, 1 Jul 2020 18:03:39 +0200 Subject: [PATCH] CBoat and friends --- src/core/re3.cpp | 2 + src/modelinfo/VehicleModelInfo.cpp | 10 + src/render/Particle.h | 1 + src/render/Weather.cpp | 2 +- src/rw/VisibilityPlugins.cpp | 8 + src/rw/VisibilityPlugins.h | 1 + src/vehicles/Automobile.cpp | 2 +- src/vehicles/Bike.cpp | 2 +- src/vehicles/Boat.cpp | 961 ++++++++++++++++++++--------- src/vehicles/Boat.h | 28 +- src/vehicles/Floater.cpp | 177 +++++- src/vehicles/Floater.h | 2 + 12 files changed, 883 insertions(+), 313 deletions(-) diff --git a/src/core/re3.cpp b/src/core/re3.cpp index 6032fc4a..d49eff50 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -474,6 +474,8 @@ DebugMenuPopulate(void) DebugMenuAddCmd("Spawn", "Spawn PCJ 600", [](){ SpawnCar(MI_PCJ600); }); DebugMenuAddCmd("Spawn", "Spawn Faggio", [](){ SpawnCar(MI_FAGGIO); }); DebugMenuAddCmd("Spawn", "Spawn Freeway", [](){ SpawnCar(MI_FREEWAY); }); + DebugMenuAddCmd("Spawn", "Spawn Squalo", [](){ SpawnCar(MI_SQUALO); }); + DebugMenuAddCmd("Spawn", "Spawn Skimmer", [](){ SpawnCar(MI_SKIMMER); }); DebugMenuAddVarBool8("Render", "Draw hud", &CHud::m_Wants_To_Draw_Hud, nil); DebugMenuAddVarBool8("Render", "Backface Culling", &gBackfaceCulling, nil); diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp index d1994c06..f80e908a 100644 --- a/src/modelinfo/VehicleModelInfo.cpp +++ b/src/modelinfo/VehicleModelInfo.cpp @@ -89,7 +89,17 @@ RwObjectNameIdAssocation boatIds[] = { { "boat_flap_right", BOAT_FLAP_RIGHT, 0 }, { "boat_rearflap_left", BOAT_REARFLAP_LEFT, 0 }, { "boat_rearflap_right", BOAT_REARFLAP_RIGHT, 0 }, +#ifdef FIX_BUGS + // let's just accept both + { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#else +#ifdef GTA_PS2 + { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#else + { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#endif +#endif { "ped_frontseat", BOAT_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; diff --git a/src/render/Particle.h b/src/render/Particle.h index f2c3a459..9c496fe1 100644 --- a/src/render/Particle.h +++ b/src/render/Particle.h @@ -98,5 +98,6 @@ public: }; extern bool clearWaterDrop; +extern int32 numWaterDropOnScreen; VALIDATE_SIZE(CParticle, 0x58); diff --git a/src/render/Weather.cpp b/src/render/Weather.cpp index 106e2f89..945e1f5e 100644 --- a/src/render/Weather.cpp +++ b/src/render/Weather.cpp @@ -279,7 +279,7 @@ void CWeather::Update(void) } Wind = InterpolationValue * Windiness[NewWeatherType] + (1.0f - InterpolationValue) * Windiness[OldWeatherType]; - WindClipped = Max(1.0f, Wind); + WindClipped = Min(1.0f, Wind); if (CClock::GetHours() == 20) TrafficLightBrightness = CClock::GetMinutes() / 60.0f; diff --git a/src/rw/VisibilityPlugins.cpp b/src/rw/VisibilityPlugins.cpp index cff1ff16..25dffb4e 100644 --- a/src/rw/VisibilityPlugins.cpp +++ b/src/rw/VisibilityPlugins.cpp @@ -625,6 +625,14 @@ CVisibilityPlugins::RenderPedCB(RpAtomic *atomic) return atomic; } +float +CVisibilityPlugins::GetDistanceSquaredFromCamera(RwV3d *pos) +{ + RwV3d dist; + RwV3dSub(&dist, pos, ms_pCameraPosn); + return RwV3dDotProduct(&dist, &dist); +} + float CVisibilityPlugins::GetDistanceSquaredFromCamera(RwFrame *frame) { diff --git a/src/rw/VisibilityPlugins.h b/src/rw/VisibilityPlugins.h index 89583432..d426abec 100644 --- a/src/rw/VisibilityPlugins.h +++ b/src/rw/VisibilityPlugins.h @@ -83,6 +83,7 @@ public: static bool VehicleVisibilityCB(RpClump *clump); static bool VehicleVisibilityCB_BigVehicle(RpClump *clump); + static float GetDistanceSquaredFromCamera(RwV3d *pos); static float GetDistanceSquaredFromCamera(RwFrame *frame); static float GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags); diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index 925bc884..217b383f 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -3796,7 +3796,7 @@ void CAutomobile::DoDriveByShootings(void) { CAnimBlendAssociation *anim = nil; - CPlayerInfo* playerInfo = ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed(); + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; diff --git a/src/vehicles/Bike.cpp b/src/vehicles/Bike.cpp index 6f52c33f..e52027f9 100644 --- a/src/vehicles/Bike.cpp +++ b/src/vehicles/Bike.cpp @@ -1989,7 +1989,7 @@ void CBike::DoDriveByShootings(void) { CAnimBlendAssociation *anim; - CPlayerInfo* playerInfo = ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed(); + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index 673372b0..786b5f4d 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -2,6 +2,7 @@ #include "General.h" #include "Timecycle.h" +#include "Weather.h" #include "HandlingMgr.h" #include "CarAI.h" #include "CarCtrl.h" @@ -13,9 +14,11 @@ #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" @@ -23,6 +26,8 @@ #include "RpAnimBlend.h" #include "Record.h" +//--MIAMI: file done + #define INVALID_ORIENTATION (-9999.99f) float fShapeLength = 0.4f; @@ -51,11 +56,15 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_fBrake = 0.0f; m_fSteeringLeftRight = 0.0f; m_nPadID = 0; - m_fMovingHiRotation = 0.0f; + 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; @@ -68,11 +77,7 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; - m_fPropellerZ = 0.25f; - m_fPropellerY = 0.35f; - m_waterMoveDrag = CVector(0.7f, 0.998f, 0.999f); - m_waterTurnDrag = CVector(0.85f, 0.96f, 0.96f); - _unk2 = false; + m_boat_unused3 = false; m_fVolumeUnderWater = 7.0f; m_fPrevVolumeUnderWater = 7.0f; @@ -85,6 +90,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; bTouchingWater = true; @@ -96,6 +102,11 @@ 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 @@ -118,32 +129,51 @@ CBoat::ProcessControl(void) 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()){ @@ -181,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; @@ -192,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(); @@ -244,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); @@ -261,23 +300,26 @@ 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) + if (pDriver){ + pDriver->bTouchingWater = true; pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } } else bIsDrowning = false; @@ -289,32 +331,61 @@ CBoat::ProcessControl(void) 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.Magnitude() > 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_fPropellerY, -pHandling->Dimension.z*m_fPropellerZ); + 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; @@ -330,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) @@ -345,80 +420,75 @@ 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, + // 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.25f, jetColor, + CGeneral::GetRandomNumberInRange(0, 5), + CGeneral::GetRandomNumberInRange(0, 90), 1, 500); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.75f * jetDir, nil, 0.5f, splashColor, CGeneral::GetRandomNumberInRange(0, 30), - CGeneral::GetRandomNumberInRange(0, 90), 3); -#endif - //TODO: MIAMI: - //if(!cameraHack) - // CParticle::AddParticle(PARTICLE_BOAT_WAKE, wakePos, wakeDir, nil, 0.0f, jetColor); - }else if((CTimer::GetFrameCounter() + m_randomSeed) & 1){ -#ifdef PC_PARTICLE - jetDir.z = 0.018f; - jetDir.x *= 0.01f; - jetDir.y *= 0.01f; - propellerWorld.z += 1.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, 0.0f, jetColor); -#endif - -#ifdef PC_PARTICLE - CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.1f * 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()); - if(force > 10.0f) 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 @@ -427,111 +497,249 @@ CBoat::ProcessControl(void) 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_waterTurnDrag.x, CTimer::GetTimeStep()); - float fy = Pow(m_waterTurnDrag.y, CTimer::GetTimeStep()); - float fz = Pow(m_waterTurnDrag.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); + 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); - 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->fBrakeBias; - CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); - CVector splashImpulse = speed * m_fMass; - ApplyMoveForce(splashImpulse); - ApplyTurnForce(splashImpulse, buoyancePoint); + 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; + // 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; - CVector dir, pos; + 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); + } - // 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 - + 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); + } + } - // 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 + // 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; @@ -542,29 +750,7 @@ CBoat::ProcessControl(void) // is this some inlined CPlaceable method? CVector pos = GetPosition(); GetMatrix().RotateZ(m_fOrientation - GetForward().Heading()); - GetMatrix().GetPosition() = pos; - } - } - - if (m_modelIndex == MI_SKIMMER && CTimer::GetTimeStep() > 0.0f) { - if (GetStatus() == STATUS_PLAYER) { - if (m_fPropellerY <= CTimer::GetTimeStep() * 0.0005f) { - m_fPropellerY = 0.0f; - } - else { - m_fPropellerY -= CTimer::GetTimeStep() * 0.0005f; - } - } - else { - if (m_fPropellerY < 0.22f) { - m_fPropellerY += CTimer::GetTimeStep() * 0.001f; - } - FlyingControl(FLIGHT_MODEL_SEAPLANE); - } - } - else { - if (bCheat8) { - FlyingControl(FLIGHT_MODEL_PLANE); + GetMatrix().SetTranslateOnly(pos); } } @@ -596,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 magic = (SQR(fwdSpeed) + 0.05f) * (0.001f * SQR(m_fVolumeUnderWater) * m_fMass) + 1.0f; + 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); - // FRAMETIME - float fx = Pow(m_waterMoveDrag.x/magic, 0.5f*CTimer::GetTimeStep()); - float fy = Pow(m_waterMoveDrag.y/magic, 0.5f*CTimer::GetTimeStep()); - float fz = Pow(m_waterMoveDrag.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; @@ -655,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_HELI, GetPosition(), 0); + CDarkel::RegisterCarBlownUpByPlayer(this); if(m_aBoatNodes[BOAT_MOVING] == nil) return; @@ -695,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; @@ -724,7 +910,7 @@ CBoat::BlowUpCar(CEntity *culprit) dist.Normalise(); if(GetUp().z > 0.0f) dist += GetUp(); - obj->GetMatrix().GetPosition() += GetUp(); + obj->GetMatrix().GetPosition() += dist; CWorld::Add(obj); @@ -734,41 +920,200 @@ CBoat::BlowUpCar(CEntity *culprit) RpAtomicSetFlags(atomic, 0); } +void +CBoat::PreRender(void) +{ + CMatrix matrix; + CVector pos; + RpAtomic *atomic; + + 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(); + } + } + + 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(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(); + } +} + RwIm3DVertex KeepWaterOutVertices[4]; RwImVertexIndex KeepWaterOutIndices[6]; void CBoat::Render() { - CMatrix matrix; - - if (m_aBoatNodes[BOAT_MOVING] != nil) { - matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); - - CVector pos = matrix.GetPosition(); - matrix.SetRotateZ(m_fMovingHiRotation); - matrix.Translate(pos); - - matrix.UpdateRW(); - if (CVehicle::bWheelsOnlyCheat) { - RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); - } - } - m_fMovingHiRotation += 0.05f; ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->SetVehicleColour(m_currentColour1, m_currentColour2); + m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; if (!CVehicle::bWheelsOnlyCheat) CEntity::Render(); - RwIm3DVertexSetRGBA(&KeepWaterOutVertices[0], 255, 255, 255, 255); + if(GetModelIndex() == MI_SKIMMER) + return; KeepWaterOutIndices[0] = 0; - RwIm3DVertexSetRGBA(&KeepWaterOutVertices[1], 255, 255, 255, 255); KeepWaterOutIndices[1] = 2; - RwIm3DVertexSetRGBA(&KeepWaterOutVertices[2], 255, 255, 255, 255); KeepWaterOutIndices[2] = 1; - RwIm3DVertexSetRGBA(&KeepWaterOutVertices[3], 255, 255, 255, 255); KeepWaterOutIndices[3] = 1; KeepWaterOutIndices[4] = 2; KeepWaterOutIndices[5] = 3; + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[0], 255, 255, 255, 255); + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[1], 255, 255, 255, 255); + 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); @@ -782,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; @@ -806,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); @@ -822,6 +1214,7 @@ CBoat::Teleport(CVector v) CWorld::Add(this); } +//--MIAMI: unused bool CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) { @@ -853,6 +1246,7 @@ CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) return numVerts != 0; } +//--MIAMI: unused float CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) { @@ -889,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; @@ -935,33 +1342,28 @@ void CBoat::AddWakePoint(CVector point) { int i; - if (m_afWakePointLifeTime[0] > 0.0f) { - if ((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(2.0f)) { - if (GetStatus() == STATUS_PHYSICS) { - if (VehicleCreatedBy == MISSION_VEHICLE) { - if (m_nNumWakePoints >= 20) - m_nNumWakePoints = 20; - } - else { - if (m_nNumWakePoints >= 15) - m_nNumWakePoints = 15; - } - } - else { - if (m_nNumWakePoints >= 31) + if(m_afWakePointLifeTime[0] > 0.0f){ + 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]; + 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] = 150.0f; - if (m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime)) + if(m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime)) m_nNumWakePoints++; } - } - else { + }else{ m_avec2dWakePoints[0] = point; m_afWakePointLifeTime[0] = 150.0f; m_nNumWakePoints = 1; @@ -972,7 +1374,7 @@ void CBoat::DoDriveByShootings(void) { CAnimBlendAssociation *anim = nil; - CPlayerInfo* playerInfo = ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed(); + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; @@ -984,7 +1386,8 @@ CBoat::DoDriveByShootings(void) 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()) diff --git a/src/vehicles/Boat.h b/src/vehicles/Boat.h index c6f4b7ad..5f8cc8a8 100644 --- a/src/vehicles/Boat.h +++ b/src/vehicles/Boat.h @@ -1,6 +1,7 @@ #pragma once #include "Vehicle.h" +#include "Door.h" enum eBoatNodes { @@ -10,33 +11,34 @@ enum eBoatNodes BOAT_FLAP_LEFT, BOAT_FLAP_RIGHT, BOAT_REARFLAP_LEFT, - BOAT_REARFLAP_RIGHT + BOAT_REARFLAP_RIGHT, + NUM_BOAT_NODES }; class CBoat : public CVehicle { public: - // 0x288 - float m_fPropellerZ; - float m_fPropellerY; - CVector m_waterMoveDrag; - CVector m_waterTurnDrag; - float m_fMovingHiRotation; - int32 _unk0; - RwFrame *m_aBoatNodes[4]; + 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; - int32 _unk1; + uint32 m_nPoliceShoutTimer; + int32 m_boat_unused2; float m_fDamage; CEntity *m_pSetOnFireEntity; - bool _unk2; + float m_skimmerThingTimer; + bool m_boat_unused3; float m_fAccelerate; float m_fBrake; float m_fSteeringLeftRight; uint8 m_nPadID; - int32 _unk3; + int32 m_boat_unused4; float m_fVolumeUnderWater; CVector m_vecBuoyancePoint; float m_fPrevVolumeUnderWater; @@ -54,7 +56,7 @@ public: 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); diff --git a/src/vehicles/Floater.cpp b/src/vehicles/Floater.cpp index 1ae1c5c3..92e3d80e 100644 --- a/src/vehicles/Floater.cpp +++ b/src/vehicles/Floater.cpp @@ -7,18 +7,40 @@ #include "Vehicle.h" #include "Floater.h" +//--MIAMI: done + cBuoyancy mod_Buoyancy; -static float fVolMultiplier = 1.0f; +float fVolMultiplier = 1.0f; // amount of boat volume in bounding box // 1.0-volume is the empty space in the bbox -static float fBoatVolumeDistribution[9] = { +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; - }else if(phys->GetModelIndex() == MI_SPEEDER){ + m_dimMax.z += 0.25f; + m_dimMin.z -= 0.2f; + break; + case MI_SQUALO: + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + 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;