diff options
Diffstat (limited to 'src/vehicles')
-rw-r--r-- | src/vehicles/Automobile.cpp | 44 | ||||
-rw-r--r-- | src/vehicles/Bike.h | 15 | ||||
-rw-r--r-- | src/vehicles/Boat.cpp | 693 | ||||
-rw-r--r-- | src/vehicles/Boat.h | 49 | ||||
-rw-r--r-- | src/vehicles/CarGen.cpp | 258 | ||||
-rw-r--r-- | src/vehicles/CarGen.h | 54 | ||||
-rw-r--r-- | src/vehicles/Cranes.cpp | 671 | ||||
-rw-r--r-- | src/vehicles/Cranes.h | 97 | ||||
-rw-r--r-- | src/vehicles/Floater.cpp | 2 | ||||
-rw-r--r-- | src/vehicles/Vehicle.cpp | 49 | ||||
-rw-r--r-- | src/vehicles/Vehicle.h | 5 |
11 files changed, 1818 insertions, 119 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index e6b936f6..257c8d33 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -182,17 +182,17 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_weaponDoorTimerRight = m_weaponDoorTimerLeft; if(GetModelIndex() == MI_DODO){ - RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); CMatrix mat1; mat1.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); 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){ - RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); - RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); - RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); - RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + 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); }else if(GetModelIndex() == MI_RHINO){ bExplosionProof = true; bBulletProof = true; @@ -253,7 +253,7 @@ CAutomobile::ProcessControl(void) ProcessCarAlarm(); - // Scan if this car is committing a crime that the police can see + // Scan if this car sees the player committing any crimes if(m_status != STATUS_ABANDONED && m_status != STATUS_WRECKED && m_status != STATUS_PLAYER && m_status != STATUS_PLAYER_REMOTE && m_status != STATUS_PLAYER_DISABLED){ switch(GetModelIndex()) @@ -356,7 +356,7 @@ CAutomobile::ProcessControl(void) PruneReferences(); - if(m_status == STATUS_PLAYER && CRecordDataForChase::Status != RECORDSTATE_1) + if(m_status == STATUS_PLAYER && CRecordDataForChase::IsRecording()) DoDriveByShootings(); } break; @@ -667,7 +667,7 @@ CAutomobile::ProcessControl(void) if(!strongGrip1 && !CVehicle::bCheat3) gripCheat = false; float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); - acceleration /= fForceMultiplier; + acceleration /= m_fForceMultiplier; // unused if(GetModelIndex() == MI_MIAMI_RCBARON || @@ -718,7 +718,7 @@ CAutomobile::ProcessControl(void) else traction = 0.004f; traction *= pHandling->fTractionMultiplier / 4.0f; - traction /= fForceMultiplier; + traction /= m_fForceMultiplier; if(CVehicle::bCheat3) traction *= 4.0f; @@ -1727,9 +1727,9 @@ CAutomobile::PreRender(void) // bright lights if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) - CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) - CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); // Taillights @@ -1798,9 +1798,9 @@ CAutomobile::PreRender(void) // bright lights if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); // Light shadows if(!alarmOff){ @@ -1873,9 +1873,9 @@ CAutomobile::PreRender(void) CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_FRONT); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_FRONT); }else{ // braking if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) @@ -1889,9 +1889,9 @@ CAutomobile::PreRender(void) CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) - CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); } }else{ if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) @@ -2814,7 +2814,7 @@ CAutomobile::ProcessBuoyancy(void) CVector impulse, point; if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ - m_flagD8 = true; + bTouchingWater = true; ApplyMoveForce(impulse); ApplyTurnForce(impulse, point); @@ -2899,7 +2899,7 @@ CAutomobile::ProcessBuoyancy(void) } }else{ bIsInWater = false; - m_flagD8 = false; + bTouchingWater = false; static RwRGBA splashCol = {155, 155, 185, 196}; static RwRGBA smokeCol = {255, 255, 255, 255}; @@ -3791,7 +3791,7 @@ CAutomobile::BlowUpCar(CEntity *culprit) } ChangeLawEnforcerState(false); - gFireManager.StartFire(this, culprit, 0.8f, 1); // TODO + gFireManager.StartFire(this, culprit, 0.8f, true); CDarkel::RegisterCarBlownUpByPlayer(this); if(GetModelIndex() == MI_RCBANDIT) CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR_QUICK, GetPosition(), 0); @@ -4054,7 +4054,7 @@ CAutomobile::HasCarStoppedBecauseOfLight(void) if(ThePaths.m_connections[curnode->firstLink + i] == AutoPilot.m_nNextRouteNode) break; if(i < curnode->numLinks && - ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) // TODO + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) return true; } @@ -4064,7 +4064,7 @@ CAutomobile::HasCarStoppedBecauseOfLight(void) if(ThePaths.m_connections[curnode->firstLink + i] == AutoPilot.m_nPrevRouteNode) break; if(i < curnode->numLinks && - ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) // TODO + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) return true; } diff --git a/src/vehicles/Bike.h b/src/vehicles/Bike.h new file mode 100644 index 00000000..4e7e5a0e --- /dev/null +++ b/src/vehicles/Bike.h @@ -0,0 +1,15 @@ +#pragma once + +// some miami bike leftovers + +enum eBikeNodes { + BIKE_NODE_NONE, + BIKE_CHASSIS, + BIKE_FORKS_FRONT, + BIKE_FORKS_REAR, + BIKE_WHEEL_FRONT, + BIKE_WHEEL_REAR, + BIKE_MUDGUARD, + BIKE_HANDLEBARS, + BIKE_NUM_NODES +};
\ No newline at end of file diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index 6d584017..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_flagD8 = true; - field_2CC = 0.0f; - field_2D0 = 0; + m_fOrientation = INVALID_ORIENTATION; + bTouchingWater = true; + 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,7 +99,542 @@ CBoat::GetComponentWorldPosition(int32 component, CVector &pos) pos = *RwMatrixGetPos(RwFrameGetLTM(m_aBoatNodes[component])); } -RxObjSpace3DVertex KeepWaterOutVertices[4]; +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]; void @@ -102,8 +642,8 @@ CBoat::Render() { CMatrix matrix; - if (m_aBoatNodes[1] != nil) { - matrix.Attach(&m_aBoatNodes[1]->modelling); + 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) { - RpAtomicRenderMacro((RpAtomic*)GetFirstObject(m_aBoatNodes[1])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); } } m_fMovingHiRotation += 0.05f; @@ -130,47 +670,23 @@ CBoat::Render() KeepWaterOutIndices[5] = 3; switch (GetModelIndex()) { case MI_SPEEDER: - KeepWaterOutVertices[0].objVertex.x = -1.15f; - KeepWaterOutVertices[0].objVertex.y = 3.61f; - KeepWaterOutVertices[0].objVertex.z = 1.03f; - KeepWaterOutVertices[1].objVertex.x = 1.15f; - KeepWaterOutVertices[1].objVertex.y = 3.61f; - KeepWaterOutVertices[1].objVertex.z = 1.03f; - KeepWaterOutVertices[2].objVertex.x = -1.15f; - KeepWaterOutVertices[2].objVertex.y = 0.06f; - KeepWaterOutVertices[2].objVertex.z = 1.03f; - KeepWaterOutVertices[3].objVertex.x = 1.15f; - KeepWaterOutVertices[3].objVertex.y = 0.06f; - KeepWaterOutVertices[3].objVertex.z = 1.03f; + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.15f, 3.61f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.15f, 3.61f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.15f, 0.06f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.15f, 0.06f, 1.03f); break; case MI_REEFER: - KeepWaterOutVertices[2].objVertex.x = -1.9f; - KeepWaterOutVertices[2].objVertex.y = 2.83f; - KeepWaterOutVertices[2].objVertex.z = 1.0f; - KeepWaterOutVertices[3].objVertex.x = 1.9f; - KeepWaterOutVertices[3].objVertex.y = 2.83f; - KeepWaterOutVertices[3].objVertex.z = 1.0f; - KeepWaterOutVertices[0].objVertex.x = -1.66f; - KeepWaterOutVertices[0].objVertex.y = -4.48f; - KeepWaterOutVertices[0].objVertex.z = 0.83f; - KeepWaterOutVertices[1].objVertex.x = 1.66f; - KeepWaterOutVertices[1].objVertex.y = -4.48f; - KeepWaterOutVertices[1].objVertex.z = 0.83f; + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.9f, 2.83f, 1.0f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.9f, 2.83f, 1.0f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.66f, -4.48f, 0.83f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.66f, -4.48f, 0.83f); break; case MI_PREDATOR: default: - KeepWaterOutVertices[0].objVertex.x = -1.45f; - KeepWaterOutVertices[0].objVertex.y = 1.9f; - KeepWaterOutVertices[0].objVertex.z = 0.96f; - KeepWaterOutVertices[1].objVertex.x = 1.45f; - KeepWaterOutVertices[1].objVertex.y = 1.9f; - KeepWaterOutVertices[1].objVertex.z = 0.96f; - KeepWaterOutVertices[2].objVertex.x = -1.45f; - KeepWaterOutVertices[2].objVertex.y = -3.75f; - KeepWaterOutVertices[2].objVertex.z = 0.96f; - KeepWaterOutVertices[3].objVertex.x = 1.45f; - KeepWaterOutVertices[3].objVertex.y = -3.75f; - KeepWaterOutVertices[3].objVertex.z = 0.96f; + 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; } KeepWaterOutVertices[0].u = 0.0f; @@ -258,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); } @@ -299,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 <new> class CBoat_ : public CBoat @@ -315,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 diff --git a/src/vehicles/Boat.h b/src/vehicles/Boat.h index d3a2ac13..f4c6a747 100644 --- a/src/vehicles/Boat.h +++ b/src/vehicles/Boat.h @@ -2,47 +2,41 @@ #include "Vehicle.h" +enum eBoatNodes +{ + BOAT_MOVING = 1, + BOAT_RUDDER, + BOAT_WINDSCREEN +}; + class CBoat : public CVehicle { public: // 0x288 - float field_288; - float field_28C; - float field_290; - float field_294; - float field_298; - float field_29C; - float field_2A0; - float field_2A4; + float m_fPropellerZ; + float m_fPropellerY; + CVector m_waterMoveDrag; + CVector m_waterTurnDrag; float m_fMovingHiRotation; int32 _unk0; RwFrame *m_aBoatNodes[4]; - uint8 m_bBoatFlag1 : 1; - uint8 m_bBoatFlag2 : 1; - uint8 m_bBoatFlag3 : 1; - uint8 m_bBoatFlag4 : 1; - uint8 m_bBoatFlag5 : 1; - uint8 m_bBoatFlag6 : 1; - uint8 m_bBoatFlag7 : 1; - uint8 m_bBoatFlag8 : 1; + uint8 bBoatInWater : 1; + uint8 bPropellerInWater : 1; bool m_bIsAnchored; - char _pad0[2]; - float field_2C4; + float m_fOrientation; int32 _unk1; - float field_2CC; - CEntity *field_2D0; + float m_fDamage; + CEntity *m_pSetOnFireEntity; bool _unk2; - char _pad1[3]; float m_fAccelerate; float m_fBrake; float m_fSteeringLeftRight; uint8 m_nPadID; - char _pad2[3]; int32 _unk3; - float m_fTurnForceZ; - CVector m_vecMoveForce; - float field_2FC; - uint16 field_300; + float m_fVolumeUnderWater; + CVector m_vecBuoyancePoint; + float m_fPrevVolumeUnderWater; + int16 m_nDeltaVolumeUnderWater; uint16 m_nNumWakePoints; CVector2D m_avec2dWakePoints[32]; float m_afWakePointLifeTime[32]; @@ -59,7 +53,10 @@ public: virtual bool IsComponentPresent(int32 component) { return true; } virtual void BlowUpCar(CEntity *ent); + void ApplyWaterResistance(void); void SetupModelNodes(); + void PruneWakeTrail(void); + void AddWakePoint(CVector point); static CBoat *(&apFrameWakeGeneratingBoats)[4]; diff --git a/src/vehicles/CarGen.cpp b/src/vehicles/CarGen.cpp new file mode 100644 index 00000000..c35005a1 --- /dev/null +++ b/src/vehicles/CarGen.cpp @@ -0,0 +1,258 @@ +#include "common.h" +#include "patcher.h" +#include "CarGen.h" + +#include "Automobile.h" +#include "Boat.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CutsceneMgr.h" +#include "General.h" +#include "Pools.h" +#include "Streaming.h" +#include "Timer.h" +#include "Vehicle.h" +#include "World.h" + +uint8 CTheCarGenerators::ProcessCounter; +uint32 CTheCarGenerators::NumOfCarGenerators; +CCarGenerator CTheCarGenerators::CarGeneratorArray[NUM_CARGENS]; +uint8 CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter; +uint32 CTheCarGenerators::CurrentActiveCount; + +void CCarGenerator::SwitchOff() +{ + m_nUsesRemaining = 0; + --CTheCarGenerators::CurrentActiveCount; +} + +void CCarGenerator::SwitchOn() +{ + m_nUsesRemaining = -1; + m_nTimer = CalcNextGen(); + ++CTheCarGenerators::CurrentActiveCount; +} + +uint32 CCarGenerator::CalcNextGen() +{ + return CTimer::GetTimeInMilliseconds() + 4; +} + +void CCarGenerator::DoInternalProcessing() +{ + if (CheckForBlockage()) { + m_nTimer += 4; + if (m_nUsesRemaining == 0) + --CTheCarGenerators::CurrentActiveCount; + return; + } + if (CCarCtrl::NumParkedCars >= 10) + return; + CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); + if (!CStreaming::HasModelLoaded(m_nModelIndex)) + return; + if (CModelInfo::IsBoatModel(m_nModelIndex)){ + CBoat* pBoat = new CBoat(m_nModelIndex, PARKED_VEHICLE); + pBoat->bIsStatic = false; + pBoat->bEngineOn = false; + CVector pos = m_vecPos; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += pBoat->GetDistanceFromCentreOfMassToBaseOfModel(); + pBoat->GetPosition() = pos; + pBoat->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pBoat->m_status = 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; + if (pos.z > -100.0f){ + pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &groundFound); + }else{ + CColPoint cp; + CEntity* pEntity; + groundFound = CWorld::ProcessVerticalLine(CVector(pos.x, pos.y, 1000.0f), -1000.0f, + cp, pEntity, true, false, false, false, false, false, nil); + if (groundFound) + pos.z = cp.point.z; + } + 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->GetPosition() = pos; + pCar->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pCar->m_status = 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); + } + } + if (m_nUsesRemaining < -1) /* I don't think this is a correct comparasion */ + --m_nUsesRemaining; + m_nTimer = CalcNextGen(); + if (m_nUsesRemaining == 0) + --CTheCarGenerators::CurrentActiveCount; +} + +void CCarGenerator::Process() +{ + if (m_nVehicleHandle == -1 && + (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) && + m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayer()) + DoInternalProcessing(); + if (m_nVehicleHandle == -1) + return; + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_nVehicleHandle); + if (!pVehicle){ + m_nVehicleHandle = -1; + return; + } + if (pVehicle->m_status != STATUS_PLAYER) + return; + m_nTimer += 60000; + m_nVehicleHandle = -1; + m_bIsBlocking = true; + pVehicle->bExtendedRange = false; +} + +void CCarGenerator::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) +{ + CMatrix m1, m2, m3; /* Unused but present on stack, so I'll leave them. */ + m_vecPos = CVector(x, y, z); + m_fAngle = angle; + m_nModelIndex = mi; + m_nColor1 = color1; + m_nColor2 = color2; + m_bForceSpawn = force; + m_nAlarm = alarm; + m_nDoorlock = lock; + m_nMinDelay = min_delay; + m_nMaxDelay = max_delay; + m_nVehicleHandle = -1; + 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() +{ + int16 entities; + CWorld::FindObjectsKindaColliding(CVector(m_vecPos), m_fSize, 1, &entities, 2, nil, false, true, true, false, false); + return entities > 0; +} + +bool CCarGenerator::CheckIfWithinRangeOfAnyPlayer() +{ + CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos; + float distance = direction.Magnitude(); + float farclip = 120.0f * TheCamera.GenerationDistMultiplier; + float nearclip = farclip - 20.0f; + if (distance >= farclip){ + if (m_bIsBlocking) + m_bIsBlocking = false; + return false; + } + if (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter) + return true; + if (m_bIsBlocking) + return false; + if (distance < nearclip) + return false; + return DotProduct2D(direction, FindPlayerSpeed()) <= 0; +} + +void CTheCarGenerators::Process() +{ + if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing()) + return; + if (++CTheCarGenerators::ProcessCounter == 4) + CTheCarGenerators::ProcessCounter = 0; + for (uint32 i = ProcessCounter; i < NumOfCarGenerators; i += 4) + CTheCarGenerators::CarGeneratorArray[i].Process(); + if (GenerateEvenIfPlayerIsCloseCounter) + GenerateEvenIfPlayerIsCloseCounter--; +} + +int32 CTheCarGenerators::CreateCarGenerator(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) +{ + CarGeneratorArray[NumOfCarGenerators].Setup(x, y, z, angle, mi, color1, color2, force, alarm, lock, min_delay, max_delay); + return NumOfCarGenerators++; +} + +void CTheCarGenerators::Init() +{ + GenerateEvenIfPlayerIsCloseCounter = 0; + NumOfCarGenerators = 0; + ProcessCounter = 0; + CurrentActiveCount = 0; +} + +void CTheCarGenerators::SaveAllCarGenerators(uint8 *buffer, uint32 *size) +{ + const uint32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + *size = sizeof(int) + nGeneralDataSize + sizeof(uint32) + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE; +INITSAVEBUF + WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE); + + WriteSaveBuf(buffer, nGeneralDataSize); + WriteSaveBuf(buffer, NumOfCarGenerators); + WriteSaveBuf(buffer, CurrentActiveCount); + WriteSaveBuf(buffer, ProcessCounter); + WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter); + WriteSaveBuf(buffer, (int16)0); // alignment + WriteSaveBuf(buffer, sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + WriteSaveBuf(buffer, CarGeneratorArray[i]); +VALIDATESAVEBUF(*size) +} + +void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) +{ + const int32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + Init(); +INITSAVEBUF + CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE); + assert(ReadSaveBuf<uint32>(buffer) == nGeneralDataSize); + NumOfCarGenerators = ReadSaveBuf<uint32>(buffer); + CurrentActiveCount = ReadSaveBuf<uint32>(buffer); + ProcessCounter = ReadSaveBuf<uint8>(buffer); + GenerateEvenIfPlayerIsCloseCounter = ReadSaveBuf<uint8>(buffer); + ReadSaveBuf<int16>(buffer); // alignment + assert(ReadSaveBuf<uint32>(buffer) == sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + CarGeneratorArray[i] = ReadSaveBuf<CCarGenerator>(buffer); +VALIDATESAVEBUF(size) +} + +STARTPATCHES +InjectHook(0x543020, CTheCarGenerators::Init, PATCH_JUMP); +InjectHook(0x542F40, CTheCarGenerators::Process, PATCH_JUMP); +InjectHook(0x543050, CTheCarGenerators::SaveAllCarGenerators, PATCH_JUMP); +InjectHook(0x5431E0, CTheCarGenerators::LoadAllCarGenerators, PATCH_JUMP); +ENDPATCHES diff --git a/src/vehicles/CarGen.h b/src/vehicles/CarGen.h new file mode 100644 index 00000000..9d645318 --- /dev/null +++ b/src/vehicles/CarGen.h @@ -0,0 +1,54 @@ +#pragma once +#include "common.h" +#include "config.h" + +enum { + CARGEN_MAXACTUALLIMIT = 100 +}; + +class CCarGenerator +{ + int32 m_nModelIndex; + CVector m_vecPos; + float m_fAngle; + int16 m_nColor1; + int16 m_nColor2; + uint8 m_bForceSpawn; + uint8 m_nAlarm; + uint8 m_nDoorlock; + int16 m_nMinDelay; + int16 m_nMaxDelay; + uint32 m_nTimer; + int32 m_nVehicleHandle; + uint16 m_nUsesRemaining; + bool m_bIsBlocking; + CVector m_vecInf; + CVector m_vecSup; + float m_fSize; +public: + void SwitchOff(); + void SwitchOn(); + uint32 CalcNextGen(); + 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(); + void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } +}; + +class CTheCarGenerators +{ +public: + static uint8 ProcessCounter; + static uint32 NumOfCarGenerators; + static CCarGenerator CarGeneratorArray[NUM_CARGENS]; + static uint8 GenerateEvenIfPlayerIsCloseCounter; + static uint32 CurrentActiveCount; + + static void Process(); + static int32 CreateCarGenerator(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); + static void Init(); + static void SaveAllCarGenerators(uint8 *, uint32 *); + static void LoadAllCarGenerators(uint8 *, uint32); +}; diff --git a/src/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp new file mode 100644 index 00000000..dbc3c340 --- /dev/null +++ b/src/vehicles/Cranes.cpp @@ -0,0 +1,671 @@ +#include "common.h" +#include "patcher.h" +#include "Cranes.h" + +#include "Camera.h" +#include "DMAudio.h" +#include "Garages.h" +#include "General.h" +#include "Entity.h" +#include "ModelIndices.h" +#include "Replay.h" +#include "Object.h" +#include "World.h" + +#define MAX_DISTANCE_TO_FIND_CRANE (10.0f) +#define CRANE_UPDATE_RADIUS (300.0f) +#define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f) +#define CRUSHER_Z (-0.951f) +#define MILITARY_Z (10.7862f) +#define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f) +#define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f) +#define CAR_REWARD_MILITARY_CRANE (1500) +#define CAR_MOVING_SPEED_THRESHOLD (0.01f) +#define CRANE_SLOWDOWN_MULTIPLIER (0.3f) + +#define OSCILLATION_SPEED (0.002f) +#define CAR_ROTATION_SPEED (0.0035f) +#define CRANE_MOVEMENT_SPEED (0.001f) +#define HOOK_ANGLE_MOVEMENT_SPEED (0.004f) +#define HOOK_OFFSET_MOVEMENT_SPEED (0.1f) +#define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f) + +#define MESSAGE_SHOW_DURATION (4000) + +#define MAX_DISTANCE (99999.9f) +#define MIN_VALID_POSITION (-10000.0f) +#define DEFAULT_OFFSET (20.0f) + +uint32 TimerForCamInterpolation; + +uint32 CCranes::CarsCollectedMilitaryCrane; +int32 CCranes::NumCranes; +CCrane CCranes::aCranes[NUM_CRANES]; + +void CCranes::InitCranes(void) +{ + CarsCollectedMilitaryCrane = 0; + NumCranes = 0; + for (int i = 0; i < NUMSECTORS_X; i++) { + for (int j = 0; j < NUMSECTORS_Y; j++) { + for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } + } + } + for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_INDUSTRIAL).first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } +} + +void CCranes::AddThisOneCrane(CEntity* pEntity) +{ + pEntity->GetMatrix().ResetOrientation(); + if (NumCranes >= NUM_CRANES) + return; + CCrane* pCrane = &aCranes[NumCranes]; + pCrane->Init(); + pCrane->m_pCraneEntity = (CBuilding*)pEntity; + pCrane->m_nCraneStatus = CCrane::NONE; + pCrane->m_fHookAngle = NumCranes; // lol wtf + while (pCrane->m_fHookAngle > TWOPI) + pCrane->m_fHookAngle -= TWOPI; + pCrane->m_fHookOffset = DEFAULT_OFFSET; + pCrane->m_fHookHeight = DEFAULT_OFFSET; + 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()); + // Is this used to avoid military crane? + if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) { + CObject* pHook = new CObject(MI_MAGNET, false); + pHook->ObjectCreatedBy = MISSION_OBJECT; + pHook->bUsesCollision = false; + pHook->bExplosionProof = true; + pHook->bAffectedByGravity = false; + pCrane->m_pHook = pHook; + pCrane->CalcHookCoordinates(&pCrane->m_vecHookCurPos.x, &pCrane->m_vecHookCurPos.y, &pCrane->m_vecHookCurPos.z); + pCrane->SetHookMatrix(); + } + else + pCrane->m_pHook = nil; + NumCranes++; +} + +void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY) +{ + float fMinDistance = MAX_DISTANCE; + float X = fPosX, Y = fPosY; + if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) { + X = fDropOffX; + Y = fDropOffY; + } + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + CCrane* pCrane = &aCranes[index]; + pCrane->m_fPickupX1 = fInfX; + pCrane->m_fPickupX2 = fSupX; + pCrane->m_fPickupY1 = fInfY; + pCrane->m_fPickupY2 = fSupY; + pCrane->m_vecDropoffTarget.x = fDropOffX; + pCrane->m_vecDropoffTarget.y = fDropOffY; + pCrane->m_vecDropoffTarget.z = fDropOffZ; + pCrane->m_nCraneStatus = CCrane::ACTIVATED; + pCrane->m_pVehiclePickedUp = nil; + pCrane->m_nVehiclesCollected = 0; + pCrane->m_fDropoffHeading = fHeading; + pCrane->m_bIsCrusher = bIsCrusher; + pCrane->m_bIsMilitaryCrane = bIsMilitary; + bool military = true; + if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) + military = false; + pCrane->m_bWasMilitaryCrane = military; + pCrane->m_nTimeForNextCheck = 0; + pCrane->m_nCraneState = CCrane::IDLE; + float Z; + if (bIsCrusher) + Z = CRUSHER_Z; + else if (bIsMilitary) + Z = MILITARY_Z; + else + Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2); + pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight); + pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight); +} + +void CCranes::DeActivateCrane(float X, float Y) +{ + float fMinDistance = MAX_DISTANCE; + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED; + aCranes[index].m_nCraneState = CCrane::IDLE; +} + +bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle) +{ + int index = 0; + bool result = false; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { + if (aCranes[i].m_nCraneStatus == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneStatus == CCrane::ROTATING_TARGET) + result = true; + } + } + return true; +} + +void CCranes::UpdateCranes(void) +{ + for (int i = 0; i < NumCranes; i++) { + if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher || + (TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y)) + aCranes[i].Update(); + } +} + +void CCrane::Update(void) +{ + if (CReplay::IsPlayingBack()) + return; + if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) && + Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && + Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) || + m_nCraneState != IDLE) { + switch (m_nCraneState) { + case IDLE: + if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) && + CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) { + CWorld::AdvanceCurrentScanCode(); +#ifdef FIX_BUGS + int xstart = max(0, CWorld::GetSectorIndexX(m_fPickupX1)); + int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2)); + int ystart = max(0, CWorld::GetSectorIndexY(m_fPickupY1)); + int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2)); +#else + int xstart = CWorld::GetSectorIndexX(m_fPickupX1); + int xend = CWorld::GetSectorIndexX(m_fPickupX2); + int ystart = CWorld::GetSectorIndexY(m_fPickupY1); + int yend = CWorld::GetSectorIndexY(m_fPickupY1); +#endif + assert(xstart <= xend); + assert(ystart <= yend); + for (int i = xstart; i <= xend; i++) { + for (int j = ystart; j <= yend; j++) { + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]); + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + } + } + } + break; + case GOING_TOWARDS_TARGET: + if (m_pVehiclePickedUp){ + if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 || + m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 || + m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 || + m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 || + m_pVehiclePickedUp->pDriver || + Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD || + FindPlayerPed()->GetPedState() == PED_ENTER_CAR && // TODO: fix carjack bug + FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + else { + float fAngle, fOffset, fHeight; + FindParametersForTarget( + m_pVehiclePickedUp->GetPosition().x, + m_pVehiclePickedUp->GetPosition().y, + m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z, + &fAngle, &fOffset, &fHeight); + if (GoTowardsTarget(fAngle, fOffset, fHeight)) { + CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos; + distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z; + if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) { + m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT; + m_vecHookVelocity *= 0.4f; + m_pVehiclePickedUp->bLightsOn = false; + m_pVehiclePickedUp->bUsesCollision = false; + if (m_bIsCrusher) + m_pVehiclePickedUp->bCollisionProof = true; + DMAudio.PlayOneShot(m_nAudioEntity, SOUND_CRANE_PICKUP, 0.0f); + } + } + } + } + else + m_nCraneState = IDLE; + break; + case LIFTING_TARGET: + RotateCarriedCarProperly(); + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = ROTATING_TARGET; + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + RotateCarriedCarProperly(); + if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = LIFTING_TARGET; + TimerForCamInterpolation = CTimer::GetTimeInMilliseconds(); + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case ROTATING_TARGET: + { + bool bRotateFinished = RotateCarriedCarProperly(); + bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f); + if (bMovementFinished && bRotateFinished) { + float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f; + if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) { + m_nCraneState = DROPPING_TARGET; + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->bUsesCollision = true; + m_pVehiclePickedUp->m_nStaticFrames = 0; + ++m_nVehiclesCollected; + if (m_bIsMilitaryCrane) { + CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex()); + if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE; + CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1); + } + CWorld::Remove(m_pVehiclePickedUp); + delete m_pVehiclePickedUp; + } + } + m_pVehiclePickedUp = nil; + } + } + break; + } + case DROPPING_TARGET: + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) { + m_nCraneState = IDLE; + m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000; + } + break; + default: + break; + } + CVector vecHook; + CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z); + m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED; + m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep()); + m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep(); + m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep(); + m_vecHookCurPos.z = vecHook.z; + switch (m_nCraneState) { + case LIFTING_TARGET: + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case ROTATING_TARGET: + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->GetPosition() = CVector(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z); + m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f); + CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up); + m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight()); + m_pVehiclePickedUp->GetUp() = up; + } + break; + default: + break; + } + } + else { + int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF; + // 16 options, lasting 2048 ms each + // a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug? + if (rnd < 4) { + m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else if (rnd > 5 && rnd < 12) { + m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z); + m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f; + } + float fCos = Cos(m_fHookAngle); + float fSin = Sin(m_fHookAngle); + m_pCraneEntity->GetRight().x = fCos; + m_pCraneEntity->GetForward().y = fCos; + m_pCraneEntity->GetRight().y = fSin; + m_pCraneEntity->GetForward().x = -fSin; + m_pCraneEntity->GetMatrix().UpdateRW(); + m_pCraneEntity->UpdateRwFrame(); + SetHookMatrix(); +} + +bool CCrane::RotateCarriedCarProperly() +{ + if (m_fDropoffHeading <= 0.0f) + return true; + if (!m_pVehiclePickedUp) + return true; + float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y); + while (fAngleDelta < -HALFPI) + fAngleDelta += PI; + while (fAngleDelta > HALFPI) + fAngleDelta -= PI; + float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep(); + if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied? + return true; + m_pVehiclePickedUp->GetMatrix().RotateZ(Abs(fDeltaThisFrame)); + return false; +} + +void CCrane::FindCarInSectorList(CPtrList* pList) +{ + CPtrNode* node; + for (node = pList->first; node; node = node->next) { + CVehicle* pVehicle = (CVehicle*)node->item; + if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + pVehicle->m_scanCode = CWorld::GetCurrentScanCode(); + if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 || + pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2) + continue; + if (pVehicle->pDriver) + continue; + if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD) + continue; + if (!pVehicle->IsCar() || pVehicle->m_status == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f) + continue; + if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) || + m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) { + if (!pVehicle->bCraneMessageDone) { + pVehicle->bCraneMessageDone = true; + if (!m_bIsMilitaryCrane) + CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle. + else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex())) + CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal. + else + CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model. + } + } + else { + m_pVehiclePickedUp = pVehicle; + pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp); + m_nCraneState = GOING_TOWARDS_TARGET; + } + } +} + +bool CCrane::DoesCranePickUpThisCarType(uint32 mi) +{ + if (m_bIsCrusher) { + return mi != MI_FIRETRUCK && + mi != MI_TRASH && +#ifndef FIX_BUGS // why + mi != MI_BLISTA && +#endif + mi != MI_SECURICA && + mi != MI_BUS && + mi != MI_DODO && + mi != MI_RHINO; + } + if (m_bIsMilitaryCrane) { + return mi == MI_FIRETRUCK || + mi == MI_AMBULAN || + mi == MI_ENFORCER || + mi == MI_FBICAR || + mi == MI_RHINO || + mi == MI_BARRACKS || + mi == MI_POLICE; + } + return true; +} + +bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) +{ + switch (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_RHINO: return (CarsCollectedMilitaryCrane & 0x10); + case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20); + case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40); + default: break; + } + return false; +} + +void CCranes::RegisterCarForMilitaryCrane(uint32 mi) +{ + switch (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_RHINO: CarsCollectedMilitaryCrane |= 0x10; break; + case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break; + case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break; + default: break; + } +} + +bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() +{ + return (CarsCollectedMilitaryCrane & 0x7F) == 0x7F; +} + +bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier) +{ + bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished; + float fHookAngleDelta = fAngleToTarget - m_fHookAngle; + while (fHookAngleDelta > PI) + fHookAngleDelta -= TWOPI; + while (fHookAngleDelta < -PI) + fHookAngleDelta += TWOPI; + float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED; + if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) { + m_fHookAngle = fAngleToTarget; + bAngleMovementFinished = true; + } else { + if (fHookAngleDelta < 0.0f) { + m_fHookAngle -= fHookAngleChangeThisFrame; + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else { + m_fHookAngle += fHookAngleChangeThisFrame; + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + bAngleMovementFinished = false; + } + float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset; + float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED; + if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) { + m_fHookOffset = fDistanceToTarget; + bOffsetMovementFinished = true; + } else { + if (fHookOffsetDelta < 0.0f) + m_fHookOffset -= fHookOffsetChangeThisFrame; + else + m_fHookOffset += fHookOffsetChangeThisFrame; + bOffsetMovementFinished = false; + } + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished; +} + +bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier) +{ + bool bHeightMovementFinished; + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bHeightMovementFinished; +} + +void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight) +{ + *pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y); + *pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude(); + *pHeight = Z; +} + +void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ) +{ + *pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x; + *pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y; + *pZ = m_fHookHeight; +} + +void CCrane::SetHookMatrix() +{ + if (m_pHook == nil) + return; + m_pHook->GetPosition() = m_vecHookCurPos; + CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up); + m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight()); + m_pHook->GetUp() = up; + m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI); + m_pHook->GetMatrix().UpdateRW(); + m_pHook->UpdateRwFrame(); + CWorld::Remove(m_pHook); + CWorld::Add(m_pHook); +} + +bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) { + switch (aCranes[i].m_nCraneState) { + case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case CCrane::LIFTING_TARGET: + case CCrane::ROTATING_TARGET: + return true; + default: + break; + } + } + } + return false; +} + +bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) + return true; + } + return false; +} + +void CCranes::Save(uint8* buf, uint32* size) +{ + INITSAVEBUF + + *size = 2 * sizeof(uint32) + sizeof(aCranes); + WriteSaveBuf(buf, NumCranes); + WriteSaveBuf(buf, CarsCollectedMilitaryCrane); + for (int i = 0; i < NUM_CRANES; i++) { + CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]); + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex(pCrane->m_pCraneEntity) + 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex(pCrane->m_pHook) + 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex(pCrane->m_pVehiclePickedUp) + 1); + } + + VALIDATESAVEBUF(*size); +} + +void CCranes::Load(uint8* buf, uint32 size) +{ + INITSAVEBUF + + NumCranes = ReadSaveBuf<int32>(buf); + CarsCollectedMilitaryCrane = ReadSaveBuf<uint32>(buf); + for (int i = 0; i < NUM_CRANES; i++) + aCranes[i] = ReadSaveBuf<CCrane>(buf); + for (int i = 0; i < NUM_CRANES; i++) { + CCrane *pCrane = &aCranes[i]; + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uint32)pCrane->m_pCraneEntity - 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uint32)pCrane->m_pHook - 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uint32)pCrane->m_pVehiclePickedUp - 1); + } + 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/Cranes.h b/src/vehicles/Cranes.h new file mode 100644 index 00000000..c0502638 --- /dev/null +++ b/src/vehicles/Cranes.h @@ -0,0 +1,97 @@ +#pragma once +#include "common.h" + +#include "World.h" + +class CVehicle; +class CEntity; +class CObject; +class CBuilding; + +class CCrane +{ +public: + enum CraneState : uint8 { + IDLE = 0, + GOING_TOWARDS_TARGET = 1, + LIFTING_TARGET = 2, + GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3, + ROTATING_TARGET = 4, + DROPPING_TARGET = 5 + }; + enum CraneStatus : uint8 { + NONE = 0, + ACTIVATED = 1, + DEACTIVATED = 2 + }; + CBuilding *m_pCraneEntity; + CObject *m_pHook; + int32 m_nAudioEntity; + float m_fPickupX1; + float m_fPickupX2; + float m_fPickupY1; + float m_fPickupY2; + CVector m_vecDropoffTarget; + float m_fDropoffHeading; + float m_fPickupAngle; + float m_fDropoffAngle; + float m_fPickupDistance; + float m_fDropoffDistance; + float m_fPickupHeight; + float m_fDropoffHeight; + float m_fHookAngle; + float m_fHookOffset; + float m_fHookHeight; + CVector m_vecHookInitPos; + CVector m_vecHookCurPos; + CVector2D m_vecHookVelocity; + CVehicle *m_pVehiclePickedUp; + uint32 m_nTimeForNextCheck; + CraneStatus m_nCraneStatus; + CraneState m_nCraneState; + uint8 m_nVehiclesCollected; + bool m_bIsCrusher; + bool m_bIsMilitaryCrane; + bool m_bWasMilitaryCrane; + bool m_bIsTop; + + void Init(void) { memset(this, 0, sizeof(*this)); } + void Update(void); + bool RotateCarriedCarProperly(void); + void FindCarInSectorList(CPtrList* pList); + bool DoesCranePickUpThisCarType(uint32 mi); + bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f); + bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f); + void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight); + void CalcHookCoordinates(float* pX, float* pY, float* pZ); + void SetHookMatrix(void); + + float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); }; + float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); } + float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); } + float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); } +}; + +static_assert(sizeof(CCrane) == 128, "CCrane: error"); + +class CCranes +{ +public: + static void InitCranes(void); + static void AddThisOneCrane(CEntity* pCraneEntity); + static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY); + static void DeActivateCrane(float fX, float fY); + static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle); + static void UpdateCranes(void); + static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi); + static void RegisterCarForMilitaryCrane(uint32 mi); + static bool HaveAllCarsBeenCollectedByMilitaryCrane(void); + static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle); + static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle); + static void Save(uint8* buf, uint32* size); + static void Load(uint8* buf, uint32 size); // on mobile it's CranesLoad outside of the class + + static uint32 CarsCollectedMilitaryCrane; + static int32 NumCranes; + static CCrane aCranes[NUM_CRANES]; +}; diff --git a/src/vehicles/Floater.cpp b/src/vehicles/Floater.cpp index 6b8bf755..62d55925 100644 --- a/src/vehicles/Floater.cpp +++ b/src/vehicles/Floater.cpp @@ -26,7 +26,7 @@ cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVec { m_numSteps = 2.0f; - if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->m_flagD8)) + if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->bTouchingWater)) return false; m_matrix = phys->GetMatrix(); diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index adeba19e..f47fd131 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -482,6 +482,55 @@ 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){ + m_nAmmoInClip = 20; + m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400; + } + } + }else{ + if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400) + m_nAmmoInClip = 20; + } +} + +void CVehicle::ExtinguishCarFire(void) { m_fHealth = max(m_fHealth, 300.0f); diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h index 4639f3e1..f9becda0 100644 --- a/src/vehicles/Vehicle.h +++ b/src/vehicles/Vehicle.h @@ -130,7 +130,8 @@ public: int8 m_nGettingInFlags; int8 m_nGettingOutFlags; uint8 m_nNumMaxPassengers; - char field_1CD[19]; + char field_1CD[3]; + float field_1D0[4]; CEntity *m_pCurGroundEntity; CFire *m_pCarFire; float m_fSteerAngle; @@ -238,6 +239,7 @@ public: bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } + bool IsBike(void) { return m_vehType == VEHICLE_TYPE_BIKE; } void FlyingControl(eFlightModel flightModel); void ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, @@ -268,6 +270,7 @@ public: bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius); bool ShufflePassengersToMakeSpace(void); void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage); + void DoFixedMachineGuns(void); bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; } CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); } |