From 3aae52d220e163243c2ee47fde354687f16acb03 Mon Sep 17 00:00:00 2001 From: aap Date: Mon, 13 Apr 2020 00:02:11 +0200 Subject: finished CBoat --- src/vehicles/Boat.cpp | 641 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 610 insertions(+), 31 deletions(-) (limited to 'src/vehicles/Boat.cpp') diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index 0b3dab3d..0159d168 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -1,12 +1,25 @@ #include "common.h" #include "patcher.h" -#include "Boat.h" +#include "General.h" +#include "Timecycle.h" #include "HandlingMgr.h" +#include "CarCtrl.h" #include "RwHelper.h" #include "ModelIndices.h" +#include "VisibilityPlugins.h" +#include "DMAudio.h" +#include "Camera.h" +#include "Darkel.h" +#include "Explosion.h" +#include "Particle.h" #include "WaterLevel.h" -#include "Pools.h" +#include "Floater.h" #include "World.h" +#include "Pools.h" +#include "Pad.h" +#include "Boat.h" + +#define INVALID_ORIENTATION (-9999.99f) float &fShapeLength = *(float*)0x600E78; float &fShapeTime = *(float*)0x600E7C; @@ -19,10 +32,6 @@ float WAKE_LIFETIME = 400.0f; CBoat * (&CBoat::apFrameWakeGeneratingBoats)[4] = *(CBoat * (*)[4])*(uintptr*)0x8620E0; -WRAPPER void CBoat::ProcessControl() { EAXJMP(0x53EF10); } -WRAPPER void CBoat::ProcessControlInputs(uint8) { EAXJMP(0x53EC70); } -WRAPPER void CBoat::BlowUpCar(CEntity* ent) { EAXJMP(0x541CB0); } - CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) { CVehicleModelInfo *minfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi); @@ -47,35 +56,31 @@ CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; - field_288 = 0.25f; - field_28C = 0.35f; - field_290 = 0.7f; - field_294 = 0.998f; - field_298 = 0.999f; - field_29C = 0.85f; - field_2A0 = 0.96f; - field_2A4 = 0.96f; + 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_fTurnForceZ = 7.0f; - field_2FC = 7.0f; - m_vecMoveForce = CVector(0.0f, 0.0f, 0.0f); + m_fVolumeUnderWater = 7.0f; + m_fPrevVolumeUnderWater = 7.0f; + m_vecBuoyancePoint = CVector(0.0f, 0.0f, 0.0f); - field_300 = 0; - m_bBoatFlag1 = true; - m_bBoatFlag2 = true; + m_nDeltaVolumeUnderWater = 0; + bBoatInWater = true; + bPropellerInWater = true; bIsInWater = true; unk1 = 0.0f; m_bIsAnchored = true; - field_2C4 = -9999.99f; + m_fOrientation = INVALID_ORIENTATION; bTouchingWater = true; - field_2CC = 0.0f; - field_2D0 = 0; + m_fDamage = 0.0f; + m_pSetOnFireEntity = nil; m_nNumWakePoints = 0; - for (int16 i = 0; i < 32; i++) + for (int16 i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++) m_afWakePointLifeTime[i] = 0.0f; m_nAmmoInClip = 20; @@ -94,6 +99,541 @@ CBoat::GetComponentWorldPosition(int32 component, CVector &pos) pos = *RwMatrixGetPos(RwFrameGetLTM(m_aBoatNodes[component])); } +void +CBoat::ProcessControl(void) +{ + if(m_nZoneLevel > LEVEL_NONE && m_nZoneLevel != CCollision::ms_collisionInMemory) + return; + + bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f; + + PruneWakeTrail(); + + int r, g, b; + 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 = 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); + + 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 = 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); + + CGeneral::GetRandomNumber(); // unused + + ProcessCarAlarm(); + + switch(m_status){ + case STATUS_PLAYER: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + ProcessControlInputs(0); + if(GetModelIndex() == MI_PREDATOR) + DoFixedMachineGuns(); + break; + case STATUS_SIMPLE: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + CPhysical::ProcessControl(); + bBoatInWater = true; + bPropellerInWater = true; + bIsInWater = true; + return; + case STATUS_PHYSICS: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + CCarCtrl::SteerAIBoatWithPhysics(this); + break; + case STATUS_ABANDONED: + case STATUS_WRECKED: + bBoatInWater = true; + bPropellerInWater = true; + bIsInWater = true; + m_fSteerAngle = 0.0; + bIsHandbrakeOn = false; + m_fBrakePedal = 0.5f; + m_fGasPedal = 0.0f; + if((GetPosition() - CWorld::Players[CWorld::PlayerInFocus].GetPos()).Magnitude() > 150.0f){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + return; + } + break; + } + + float collisionDamage = pHandling->fCollisionDamageMultiplier * m_fDamageImpulse; + if(collisionDamage > 25.0f && m_status != STATUS_WRECKED && m_fHealth >= 150.0f){ + 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(m_fHealth <= 0.0f && prevHealth > 0.0f){ + m_fHealth = 1.0f; + m_pSetOnFireEntity = m_pDamageEntity; + } + } + + // Damage particles + if(m_fHealth <= 600.0f && m_status != STATUS_WRECKED && + Abs(GetPosition().x - TheCamera.GetPosition().x) < 200.0f && + Abs(GetPosition().y - TheCamera.GetPosition().y) < 200.0f){ + float speedSq = m_vecMoveSpeed.MagnitudeSqr(); + CVector smokeDir = 0.8f*m_vecMoveSpeed; + CVector smokePos; + switch(GetModelIndex()){ + case MI_SPEEDER: + smokePos = CVector(0.4f, -2.4f, 0.8f); + smokeDir += 0.05f*GetRight(); + smokeDir.z += 0.2f*m_vecMoveSpeed.z; + break; + case MI_REEFER: + smokePos = CVector(2.0f, -1.0f, 0.5f); + smokeDir += 0.07f*GetRight(); + break; + case MI_PREDATOR: + default: + smokePos = CVector(-1.5f, -0.5f, 1.2f); + smokeDir += -0.08f*GetRight(); + break; + } + + smokePos = GetMatrix() * smokePos; + + // On fire + if(m_fHealth < 150.0f){ + CParticle::AddParticle(PARTICLE_CARFLAME, smokePos, + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(2.25f/200.0f, 0.09f)), + nil, 0.9f); + CVector smokePos2 = smokePos; + smokePos2.x += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); + smokePos2.y += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); + smokePos2.z += CGeneral::GetRandomNumberInRange(2.25f/4.0f, 2.25f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, smokePos2, CVector(0.0f, 0.0f, 0.0f)); + + m_fDamage += CTimer::GetTimeStepInMilliseconds(); + if(m_fDamage > 5000.0f) + BlowUpCar(m_pSetOnFireEntity); + } + + if(speedSq < 0.25f && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, smokePos, smokeDir); + if(speedSq < 0.25f && m_fHealth <= 350.0f) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, smokePos, 1.25f*smokeDir); + } + + 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)){ + // Process boat in water + if(0.1f * m_fMass * GRAVITY*CTimer::GetTimeStep() < buoyanceImpulse.z){ + bBoatInWater = true; + bIsInWater = true; + }else{ + bBoatInWater = false; + bIsInWater = false; + } + + m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater; + m_vecBuoyancePoint = buoyancePoint; + ApplyMoveForce(buoyanceImpulse); + if(!onLand) + ApplyTurnForce(buoyanceImpulse, buoyancePoint); + + 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; + else + impulse = 0.0f; + impulse = min(impulse, GRAVITY*pHandling->fSuspensionDampingLevel*m_fMass*CTimer::GetTimeStep()); + ApplyMoveForce(impulse*GetUp()); + ApplyTurnForce(impulse*GetUp(), buoyancePoint - pHandling->fSuspensionBias*GetForward()); + } + + // Handle boat moving forward + if(Abs(m_fGasPedal) > 0.05f || m_vecMoveSpeed.Magnitude() > 0.01f){ + if(bBoatInWater) + AddWakePoint(GetPosition()); + + float steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward()); + if(m_modelIndex == MI_GHOST) + steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward())*0.3f; + if(steerFactor < 0.0f) steerFactor = 0.0f; + + CVector propeller(0.0f, -pHandling->Dimension.y*m_fPropellerY, -pHandling->Dimension.z*m_fPropellerZ); + propeller = Multiply3x3(GetMatrix(), propeller); + CVector propellerWorld = GetPosition() + propeller; + + float steerSin = Sin(-m_fSteerAngle * steerFactor); + float steerCos = Cos(-m_fSteerAngle * steerFactor); + float waterLevel; + CWaterLevel::GetWaterLevel(propellerWorld, &waterLevel, true); + if(propellerWorld.z-0.5f < waterLevel){ + float propellerDepth = waterLevel - (propellerWorld.z - 0.5f); + if(propellerDepth > 1.0f) + propellerDepth = 1.0f; + else + propellerDepth = SQR(propellerDepth); + bPropellerInWater = true; + + if(Abs(m_fGasPedal) > 0.05f){ + 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) + force.z = SQR(1.2f - force.z) + 0.2f; + if(onLand){ + if(m_fGasPedal < 0.0f){ + force.x *= 5.0f; + force.y *= 5.0f; + } + if(force.z < 0.0f) + force.z = 0.0f; + 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()); + } + + // Spray some particles + CVector jetDir = -0.04f * force; + if(m_fGasPedal > 0.0f){ + if(m_status == 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; + + CVector wakePos = GetPosition() + sternPos; + wakePos.z -= 0.65f; + + CVector wakeDir = 0.75f * jetDir; + + CParticle::AddParticle(PARTICLE_BOAT_THRUSTJET, jetPos, jetDir, nil, 0.0f, jetColor); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, jetPos, 0.25f * jetDir, nil, 1.0f, splashColor, + CGeneral::GetRandomNumberInRange(0, 30), + CGeneral::GetRandomNumberInRange(0, 90), 3); + if(!cameraHack) + CParticle::AddParticle(PARTICLE_BOAT_WAKE, wakePos, wakeDir, nil, 0.0f, jetColor); + }else if((CTimer::GetFrameCounter() + m_randomSeed) & 1){ + 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); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.1f * jetDir, nil, 0.5f, splashColor, + CGeneral::GetRandomNumberInRange(0, 30), + CGeneral::GetRandomNumberInRange(0, 90), 3); + } + } + }else if(!onLand){ + float force = 50.0f*DotProduct(m_vecMoveSpeed, GetForward()); + if(force > 10.0f) force = 10.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); + } + }else + bPropellerInWater = false; + } + + // 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 + + if(!onLand && bBoatInWater) + 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()); + + 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); + } + + // Spray particles on sides of boat + if(m_nDeltaVolumeUnderWater > 75){ + float speed = m_vecMoveSpeed.Magnitude(); + float splash1Size = speed; + float splash2Size = 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 + 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); + 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); + + // left + 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); + 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); + } + + m_fPrevVolumeUnderWater = m_fVolumeUnderWater; + }else{ + bBoatInWater = false; + bIsInWater = false; + } + + if(m_bIsAnchored){ + m_vecMoveSpeed.x = 0.0f; + m_vecMoveSpeed.y = 0.0f; + + if(m_fOrientation == INVALID_ORIENTATION){ + m_fOrientation = GetForward().Heading(); + }else{ + // is this some inlined CPlaceable method? + CVector pos = GetPosition(); + GetMatrix().RotateZ(m_fOrientation - GetForward().Heading()); + GetPosition() = pos; + } + } + + ProcessDelayedExplosion(); +} + +void +CBoat::ProcessControlInputs(uint8 pad) +{ + m_nPadID = pad; + if(m_nPadID > 3) + m_nPadID = 3; + + m_fBrake += (CPad::GetPad(pad)->GetBrake()/255.0f - m_fBrake)*0.1f; + m_fBrake = clamp(m_fBrake, 0.0f, 1.0f); + + if(m_fBrake < 0.05f){ + m_fBrake = 0.0f; + 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_fSteeringLeftRight += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteeringLeftRight)*0.2f; + m_fSteeringLeftRight = clamp(m_fSteeringLeftRight, -1.0f, 1.0f); + + float steeringSq = m_fSteeringLeftRight < 0.0f ? -SQR(m_fSteeringLeftRight) : SQR(m_fSteeringLeftRight); + m_fSteerAngle = pHandling->fSteeringLock * DEGTORAD(steeringSq); + m_fGasPedal = m_fAccelerate; +} + +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; + 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()); + + m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // invert - to local space + m_vecMoveSpeed.x *= fx; + m_vecMoveSpeed.y *= fy; + m_vecMoveSpeed.z *= fz; + float force = (fy - 1.0f) * m_vecMoveSpeed.y * m_fMass; + m_vecMoveSpeed = Multiply3x3(GetMatrix(), m_vecMoveSpeed); // back to world + + ApplyTurnForce(force*GetForward(), -GetUp()); + + if(m_vecMoveSpeed.z > 0.0f) + m_vecMoveSpeed.z *= fz; + else + m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz; +} + +RwObject* +GetBoatAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; + + +} + +void +CBoat::BlowUpCar(CEntity *culprit) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(!bCanBeDamaged) + return; + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + m_status = STATUS_WRECKED; + bRenderScorched = true; + + m_fHealth = 0.0; + 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(); + } + + bEngineOn = false; + bLightsOn = false; + ChangeLawEnforcerState(false); + + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); + if(m_aBoatNodes[BOAT_MOVING] == nil) + return; + + // much like CAutomobile::SpawnFlyingComponent from here on + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic == nil) + return; + + obj = new CObject(); + if(obj == nil) + return; + + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aBoatNodes[BOAT_MOVING]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.99f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->bIsStatic = false; + obj->bIsPickup = false; + + // life time + CObject::nNoTempObjects++; + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(GetUp().z > 0.0f) + obj->m_vecMoveSpeed.z = 0.3f; + else + obj->m_vecMoveSpeed.z = 0.0f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + obj->m_vecTurnSpeed.x = 0.5f; + + // push component away from boat + CVector dist = obj->GetPosition() - GetPosition(); + dist.Normalise(); + if(GetUp().z > 0.0f) + dist += GetUp(); + obj->GetPosition() += GetUp(); + + CWorld::Add(obj); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); +} + RwIm3DVertex KeepWaterOutVertices[4]; RwImVertexIndex KeepWaterOutIndices[6]; @@ -102,8 +642,8 @@ CBoat::Render() { CMatrix matrix; - if (m_aBoatNodes[1] != nil) { - matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[1])); + if (m_aBoatNodes[BOAT_MOVING] != nil) { + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); CVector pos = matrix.GetPosition(); matrix.SetRotateZ(m_fMovingHiRotation); @@ -111,7 +651,7 @@ CBoat::Render() matrix.UpdateRW(); if (CVehicle::bWheelsOnlyCheat) { - RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[1])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); } } m_fMovingHiRotation += 0.05f; @@ -234,10 +774,9 @@ CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) void CBoat::SetupModelNodes() { - m_aBoatNodes[0] = nil; - m_aBoatNodes[1] = nil; - m_aBoatNodes[2] = nil; - m_aBoatNodes[3] = nil; + int i; + for(i = 0; i < ARRAY_SIZE(m_aBoatNodes); i++) + m_aBoatNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), m_aBoatNodes); } @@ -275,6 +814,43 @@ CBoat::FillBoatList() } } +void +CBoat::PruneWakeTrail(void) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++){ + if(m_afWakePointLifeTime[i] <= 0.0f) + break; + if(m_afWakePointLifeTime[i] <= CTimer::GetTimeStep()){ + m_afWakePointLifeTime[i] = 0.0f; + break; + } + m_afWakePointLifeTime[i] -= CTimer::GetTimeStep(); + } + m_nNumWakePoints = i; +} + +void +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--){ + m_avec2dWakePoints[i] = m_avec2dWakePoints[i-1]; + m_afWakePointLifeTime[i] = m_afWakePointLifeTime[i-1]; + } + m_avec2dWakePoints[0] = point; + m_afWakePointLifeTime[0] = 400.0f; + } + }else{ + m_avec2dWakePoints[0] = point; + m_afWakePointLifeTime[0] = 400.0f; + m_nNumWakePoints = 1; + } +} + #include class CBoat_ : public CBoat @@ -291,4 +867,7 @@ STARTPATCHES InjectHook(0x542370, CBoat::IsSectorAffectedByWake, PATCH_JUMP); InjectHook(0x5424A0, CBoat::IsVertexAffectedByWake, PATCH_JUMP); InjectHook(0x542250, CBoat::FillBoatList, PATCH_JUMP); + InjectHook(0x542140, &CBoat::AddWakePoint, PATCH_JUMP); + InjectHook(0x5420D0, &CBoat::PruneWakeTrail, PATCH_JUMP); + InjectHook(0x541A30, &CBoat::ApplyWaterResistance, PATCH_JUMP); ENDPATCHES -- cgit v1.2.3