From 9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Mon, 13 Jul 2026 12:27:07 +0000 Subject: Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation) Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami. --- src/objects/CutsceneObject.cpp | 182 ++++++ src/objects/CutsceneObject.h | 24 + src/objects/DummyObject.cpp | 14 + src/objects/DummyObject.h | 12 + src/objects/Object.cpp | 836 +++++++++++++++++++++++++++ src/objects/Object.h | 116 ++++ src/objects/ObjectData.cpp | 161 ++++++ src/objects/ObjectData.h | 27 + src/objects/ParticleObject.cpp | 1246 ++++++++++++++++++++++++++++++++++++++++ src/objects/ParticleObject.h | 112 ++++ src/objects/Projectile.cpp | 15 + src/objects/Projectile.h | 9 + src/objects/Stinger.cpp | 232 ++++++++ src/objects/Stinger.h | 40 ++ 14 files changed, 3026 insertions(+) create mode 100644 src/objects/CutsceneObject.cpp create mode 100644 src/objects/CutsceneObject.h create mode 100644 src/objects/DummyObject.cpp create mode 100644 src/objects/DummyObject.h create mode 100644 src/objects/Object.cpp create mode 100644 src/objects/Object.h create mode 100644 src/objects/ObjectData.cpp create mode 100644 src/objects/ObjectData.h create mode 100644 src/objects/ParticleObject.cpp create mode 100644 src/objects/ParticleObject.h create mode 100644 src/objects/Projectile.cpp create mode 100644 src/objects/Projectile.h create mode 100644 src/objects/Stinger.cpp create mode 100644 src/objects/Stinger.h (limited to 'src/objects') diff --git a/src/objects/CutsceneObject.cpp b/src/objects/CutsceneObject.cpp new file mode 100644 index 0000000..7d9fe64 --- /dev/null +++ b/src/objects/CutsceneObject.cpp @@ -0,0 +1,182 @@ +#include "common.h" + +#include "main.h" +#include "RwHelper.h" +#include "Lights.h" +#include "PointLights.h" +#include "RpAnimBlend.h" +#include "AnimBlendClumpData.h" +#include "Bones.h" +#include "Renderer.h" +#include "ModelIndices.h" +#include "Shadows.h" +#include "Timecycle.h" +#include "CutsceneShadow.h" +#include "CutsceneObject.h" +#include "ModelIndices.h" +#include "RpAnimBlend.h" + + +CCutsceneObject::CCutsceneObject(void) +{ + SetStatus(STATUS_SIMPLE); + bUsesCollision = false; + bStreamingDontDelete = true; + ObjectCreatedBy = CUTSCENE_OBJECT; + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + + m_pAttachTo = nil; + m_pAttachmentObject = nil; + m_pShadow = nil; +} + +CCutsceneObject::~CCutsceneObject(void) +{ + if ( m_pShadow ) + { + delete m_pShadow; + m_pShadow = nil; + } +} + +void +CCutsceneObject::SetModelIndex(uint32 id) +{ + CEntity::SetModelIndex(id); + assert(RwObjectGetType(m_rwObject) == rpCLUMP); + RpAnimBlendClumpInit((RpClump*)m_rwObject); + (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity3d = &m_vecMoveSpeed; + (*RPANIMBLENDCLUMPDATA(m_rwObject))->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION_3D; +} + +void +CCutsceneObject::CreateShadow(void) +{ + if ( IsPedModel(GetModelIndex()) ) + { + m_pShadow = new CCutsceneShadow(); + if (!m_pShadow->IsInitialized()) + m_pShadow->Create(m_rwObject, 6, true, 4, true); + } +} + +void +CCutsceneObject::ProcessControl(void) +{ + CPhysical::ProcessControl(); + + if ( m_pAttachTo ) + { + if ( m_pAttachmentObject ) + GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); + else + GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); + } + else + { + if(CTimer::GetTimeStep() < 1/100.0f) + m_vecMoveSpeed *= 100.0f; + else + m_vecMoveSpeed *= 1.0f/CTimer::GetTimeStep(); + + ApplyMoveSpeed(); + } +} + +static RpMaterial* +MaterialSetAlpha(RpMaterial *material, void *data) +{ + ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; + return material; +} + +void +CCutsceneObject::PreRender(void) +{ + if ( m_pAttachTo ) + { + if ( m_pAttachmentObject ) + { + m_pAttachmentObject->UpdateRpHAnim(); + GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); + } + else + GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); + + if ( RwObjectGetType(m_rwObject) == rpCLUMP && IsClumpSkinned(GetClump()) ) + { + RpAtomic *atomic = GetFirstAtomic(GetClump()); + atomic->boundingSphere.center = (*RPANIMBLENDCLUMPDATA(GetClump()))->frames[0].hanimFrame->t; + } + } + + if ( RwObjectGetType(m_rwObject) == rpCLUMP ) + UpdateRpHAnim(); + + if(IsPedModel(GetModelIndex())) + { + if ( m_pShadow == nil ) + { + CShadows::StoreShadowForPedObject(this, + CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); + } + else + { + if ( m_pShadow->IsInitialized() ) + m_pShadow->UpdateForCutscene(); + + CShadows::StoreShadowForCutscenePedObject(this, + CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); + } + + // For some reason xbox/android limbs are transparent here... + RpGeometry *geometry = RpAtomicGetGeometry(GetFirstAtomic(GetClump())); + RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geometry, MaterialSetAlpha, (void*)255); + } +} + +void +CCutsceneObject::Render(void) +{ + SetCullMode(rwCULLMODECULLNONE); + CObject::Render(); + SetCullMode(rwCULLMODECULLBACK); +} + +bool +CCutsceneObject::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + + if(bRenderScorched){ + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + }else{ + CVector coors = GetPosition(); + float lighting = CPointLights::GenerateLightsAffectingObject(&coors); + if(lighting != 1.0f){ + SetAmbientAndDirectionalColours(lighting); + return true; + } + } + + return false; +} + +void +CCutsceneObject::RemoveLighting(bool reset) +{ + CRenderer::RemoveVehiclePedLights(this, reset); +} diff --git a/src/objects/CutsceneObject.h b/src/objects/CutsceneObject.h new file mode 100644 index 0000000..af24c0a --- /dev/null +++ b/src/objects/CutsceneObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Object.h" + +class CCutsceneShadow; + +class CCutsceneObject : public CObject +{ +public: + CCutsceneShadow *m_pShadow; + void *m_pAttachTo; + CObject *m_pAttachmentObject; + + CCutsceneObject(void); + ~CCutsceneObject(void); + + void SetModelIndex(uint32 id); + void CreateShadow(void); + void ProcessControl(void); + void PreRender(void); + void Render(void); + bool SetupLighting(void); + void RemoveLighting(bool reset); +}; diff --git a/src/objects/DummyObject.cpp b/src/objects/DummyObject.cpp new file mode 100644 index 0000000..8dd1643 --- /dev/null +++ b/src/objects/DummyObject.cpp @@ -0,0 +1,14 @@ +#include "common.h" + +#include "DummyObject.h" +#include "Pools.h" + +CDummyObject::CDummyObject(CObject *obj) +{ + SetModelIndexNoCreate(obj->GetModelIndex()); + if(obj->m_rwObject) + AttachToRwObject(obj->m_rwObject); + obj->DetachFromRwObject(); + m_level = obj->m_level; + m_area = obj->m_area; +} diff --git a/src/objects/DummyObject.h b/src/objects/DummyObject.h new file mode 100644 index 0000000..680df68 --- /dev/null +++ b/src/objects/DummyObject.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Dummy.h" + +class CObject; + +class CDummyObject : public CDummy +{ +public: + CDummyObject(void) {} + CDummyObject(CObject *obj); +}; diff --git a/src/objects/Object.cpp b/src/objects/Object.cpp new file mode 100644 index 0000000..0605f54 --- /dev/null +++ b/src/objects/Object.cpp @@ -0,0 +1,836 @@ +#include "common.h" + +#include "main.h" +#include "Lights.h" +#include "Pools.h" +#include "Radar.h" +#include "Object.h" +#include "DummyObject.h" +#include "Particle.h" +#include "General.h" +#include "ObjectData.h" +#include "World.h" +#include "Floater.h" +#include "soundlist.h" +#include "WaterLevel.h" +#include "Timecycle.h" +#include "Stats.h" +#include "SpecialFX.h" + +#define BEACHBALL_MAX_SCORE 250 +// the proportion of the ball speed compared to the player speed when it hits the player +#define BEACHBALL_SPEED_PROPORTION 0.4f + +int16 CObject::nNoTempObjects; +//int16 CObject::nBodyCastHealth = 1000; +float CObject::fDistToNearestTree; + +void *CObject::operator new(size_t sz) { return CPools::GetObjectPool()->New(); } +void *CObject::operator new(size_t sz, int handle) { return CPools::GetObjectPool()->New(handle);}; +void CObject::operator delete(void *p, size_t sz) { CPools::GetObjectPool()->Delete((CObject*)p); } +void CObject::operator delete(void *p, int handle) { CPools::GetObjectPool()->Delete((CObject*)p); } + +CObject::CObject(void) +{ + m_type = ENTITY_TYPE_OBJECT; + m_fUprootLimit = 0.0f; + m_nCollisionDamageEffect = 0; + m_nSpecialCollisionResponseCases = COLLRESPONSE_NONE; + m_bCameraToAvoidThisObject = false; + ObjectCreatedBy = UNKNOWN_OBJECT; + m_nEndOfLifeTime = 0; + // m_nRefModelIndex = -1; // duplicate + // bUseVehicleColours = false; // duplicate + m_colour2 = 0; + m_colour1 = m_colour2; + m_nBonusValue = 0; + m_nCostValue = 0; + bIsPickup = false; + bPickupObjWithMessage = false; + bOutOfStock = false; + bGlassCracked = false; + bGlassBroken = false; + bHasBeenDamaged = false; + m_nRefModelIndex = -1; + bUseVehicleColours = false; + // bIsStreetLight = false; // duplicate + m_pCurSurface = nil; + m_pCollidingEntity = nil; + m_nBeachballBounces = 0; + bIsStreetLight = false; + m_area = AREA_EVERYWHERE; +} + +CObject::CObject(int32 mi, bool createRW) +{ + if (createRW) + SetModelIndex(mi); + else + SetModelIndexNoCreate(mi); + Init(); +} + +CObject::CObject(CDummyObject *dummy) +{ + SetModelIndexNoCreate(dummy->GetModelIndex()); + + if (dummy->m_rwObject) + AttachToRwObject(dummy->m_rwObject); + else + GetMatrix() = dummy->GetMatrix(); + + m_objectMatrix = dummy->GetMatrix(); + dummy->DetachFromRwObject(); + Init(); + m_level = dummy->m_level; + m_area = dummy->m_area; +} + +CObject::~CObject(void) +{ + CRadar::ClearBlipForEntity(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(this)); + + if (m_nRefModelIndex != -1) + CModelInfo::GetModelInfo(m_nRefModelIndex)->RemoveRef(); + + if (ObjectCreatedBy == TEMP_OBJECT && nNoTempObjects != 0) + nNoTempObjects--; +} + +void +CObject::ProcessControl(void) +{ + CVector point, impulse; + if (m_nCollisionDamageEffect) + ObjectDamage(m_fDamageImpulse); + CPhysical::ProcessControl(); + if (mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)) { + bIsInWater = true; + SetIsStatic(false); + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + float fTimeStep = Pow(0.97f, CTimer::GetTimeStep()); + m_vecMoveSpeed *= fTimeStep; + m_vecTurnSpeed *= fTimeStep; + } + int16 mi = GetModelIndex(); + if ((mi == MI_EXPLODINGBARREL || mi == MI_PETROLPUMP || mi == MI_PETROLPUMP2) && bHasBeenDamaged && bIsVisible + && (CGeneral::GetRandomNumber() & 0x1F) == 10) { + bExplosionProof = true; + bIsVisible = false; + bUsesCollision = false; + bAffectedByGravity = false; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + if (mi == MI_RCBOMB) { + float fTurnForce = -(m_fTurnMass / 20.0f); + CPhysical::ApplyTurnForce(m_vecMoveSpeed * fTurnForce, -GetForward()); + float fScalar = 1.0f - m_vecMoveSpeed.MagnitudeSqr() / 5.0f; + float fScalarTimed = Pow(fScalar, CTimer::GetTimeStep()); + m_vecMoveSpeed *= fScalarTimed; + } + if (mi == MI_BEACHBALL) { + float fTimeStep = Pow(0.95f, CTimer::GetTimeStep()); + float fPreviousVecSpeedMag = m_vecMoveSpeed.Magnitude2D(); + m_vecMoveSpeed.x *= fTimeStep; + m_vecMoveSpeed.y *= fTimeStep; + m_vecMoveSpeed.z += fPreviousVecSpeedMag - m_vecMoveSpeed.Magnitude2D(); + if (!FindPlayerVehicle()) { + CVector distance; + distance.x = FindPlayerCoors().x - GetPosition().x; + distance.y = FindPlayerCoors().y - GetPosition().y; + distance.z = FindPlayerCoors().z - GetPosition().z; + if (distance.z > 0.0 && distance.z < 1.5f && distance.Magnitude2D() < 1.0f) { + CVector playerSpeed = FindPlayerSpeed(); + if (fPreviousVecSpeedMag < 0.05f && playerSpeed.Magnitude() > 0.1f) { + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + playerSpeed.z = 0.3f; + m_vecMoveSpeed = CVector( + playerSpeed.x * BEACHBALL_SPEED_PROPORTION, + playerSpeed.y * BEACHBALL_SPEED_PROPORTION, + 0.3f * BEACHBALL_SPEED_PROPORTION + ); + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); + m_vecTurnSpeed += CVector( + ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, + ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, + 0.0f); + if (m_nBeachballBounces > 0) { + m_nBeachballBounces++; + } + if (m_nBeachballBounces > 0) { + sprintf(gString, "%d", m_nBeachballBounces); + CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); + CStats::RegisterHighestScore(3, m_nBeachballBounces); + } + } + } + if (distance.z > -1.05 && distance.z < -0.6 && m_vecMoveSpeed.z < 0.0f && distance.Magnitude2D() < 0.9f) { + m_vecMoveSpeed.x += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; + m_vecMoveSpeed.y += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; + m_vecMoveSpeed.z = Max(m_vecMoveSpeed.z + 0.3f, 0.2f); + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); + m_vecTurnSpeed.x += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; + m_vecTurnSpeed.y += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; + m_nBeachballBounces++; + m_nBeachballBounces = Min(m_nBeachballBounces, BEACHBALL_MAX_SCORE); + sprintf(gString, "%d", m_nBeachballBounces); + CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); + CStats::RegisterHighestScore(3, m_nBeachballBounces); + } + } + } + if (bIsBIGBuilding) { + bIsInSafePosition = true; + } +} + +void +CObject::Teleport(CVector vecPos) +{ + CWorld::Remove(this); + m_matrix.GetPosition() = vecPos; + m_matrix.UpdateRW(); + UpdateRwFrame(); + CWorld::Add(this); +} + +void +CObject::Render(void) +{ + if (bDoNotRender) + return; + + if (m_nRefModelIndex != -1 && ObjectCreatedBy == TEMP_OBJECT && bUseVehicleColours) { + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nRefModelIndex); + assert(mi->GetModelType() == MITYPE_VEHICLE); + mi->SetVehicleColour(m_colour1, m_colour2); + } + + float red = (0.8f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj()) * 165.75f; + float green = (0.8f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj()) * 165.75f; + float blue = (0.8f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj()) * 165.75f; + + red = clamp(red, 0.0f, 255.0f); + green = clamp(green, 0.0f, 255.0f); + blue = clamp(blue, 0.0f, 255.0f); + + int alpha = CGeneral::GetRandomNumberInRange(196, 225); + + RwRGBA color = { (uint8)red, (uint8)green, (uint8)blue, (uint8)alpha }; + + if (this->GetModelIndex() == MI_YT_MAIN_BODY) { + float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); + if (moveSpeedMagnitude > 0.0f) { + float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; + + CVector dir = this->GetMoveSpeed() + 0.3f * this->GetRight() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + CVector pos = scaleMax * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); + + float fWaterLevel; + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.75f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + float scaleMin = GetColModel()->boundingBox.min.y; + + dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + pos = scaleMin * this->GetForward() + 4.5f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() - 0.05f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + } + } + + if (this->GetModelIndex() == MI_YT_MAIN_BODY2) { + float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); + if (moveSpeedMagnitude > 0.0f) { + float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; + + CVector dir = this->GetMoveSpeed() - 0.3f * this->GetRight() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + CVector pos = scaleMax * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); + + float fWaterLevel; + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.75f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + float scaleMin = GetColModel()->boundingBox.min.y; + + dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + pos = scaleMin * this->GetForward() - 4.5f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + } + } + + CEntity::Render(); +} + +bool +CObject::SetupLighting(void) +{ + if (bRenderScorched) { + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + return true; + } else if (bIsPickup) { + SetFullAmbient(); + return true; + } else if (bIsWeapon) { + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + return true; + } + return false; +} + +void +CObject::RemoveLighting(bool reset) +{ + if (reset) { + SetAmbientColours(); + DeActivateDirectional(); + } +} + +void +CObject::ObjectDamage(float amount) +{ + if (!m_nCollisionDamageEffect || !bUsesCollision) + return; + static int8 nFrameGen = 0; + bool bBodyCastDamageEffect = false; +#if 0 + if (GetModelIndex() == MI_BODYCAST) { + if (amount > 50.0f) + nBodyCastHealth = (int16)(nBodyCastHealth - 0.5f * amount); + if (nBodyCastHealth < 0) + nBodyCastHealth = 0; + if (nBodyCastHealth < 200) + bBodyCastDamageEffect = true; + amount = 0.0f; + } +#endif + if ((amount * m_fCollisionDamageMultiplier > 150.0f || bBodyCastDamageEffect) && m_nCollisionDamageEffect) { + const CVector &vecPos = m_matrix.GetPosition(); + const float fDirectionZ = 0.0002f * amount; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_CHANGE_MODEL: + bRenderDamaged = true; + return; + case DAMAGE_EFFECT_SPLIT_MODEL: + return; + case DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS: + { + static RwRGBA debrisColor = { 0xc8,0xc8,0xc8,0xff }; + if (bRenderDamaged) { + break; + } + bRenderDamaged = true; + CVector min = 0.85f * GetColModel()->boundingBox.min; + CVector max = 0.85f * GetColModel()->boundingBox.max; + min.z = max.z; + min = GetMatrix() * min; + max = GetMatrix() * max; + CVector temp = (max - min) * 0.02f; + for (int32 i = 0; i < 50; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + CVector pos = min + temp * (float)i; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + float fColorFactor = CGeneral::GetRandomNumberInRange(0.6f, 1.2f); + RwRGBA color = debrisColor; + color.red *= fColorFactor; + color.green *= fColorFactor; + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-0.40f, 0.40f); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, min); + break; + } + case DAMAGE_EFFECT_CHANGE_THEN_SMASH: { + if (!bRenderDamaged) { + bRenderDamaged = true; + return; + } + // fall through + } + case DAMAGE_EFFECT_SMASH_COMPLETELY: { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + break; + } + case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: + case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color = { 96, 48, 0, 255 }; + for (int32 i = 0; i < 25; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + RwRGBA randomColor = color; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: { + float fRandom = CGeneral::GetRandomNumberInRange(0.01f, 1.0f); + randomColor.red *= fRandom; + randomColor.green *= fRandom; + randomColor.blue *= fRandom; + break; + } + case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: { + randomColor.red = 0xff; + randomColor.green = 0xfc; + break; + } + } + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + static const RwRGBA color = { 128, 128, 128, 255 }; + CVector position = GetPosition(); + for (int32 i = 0; i < 45; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + float fRandom = CGeneral::GetRandomNumberInRange(0.5f, 1.0f); + RwRGBA randomColor = { uint8(color.red * fRandom), uint8(color.green * fRandom), uint8(color.blue * fRandom), color.alpha }; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_1, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY: + case DAMAGE_EFFECT_BURST_BEACHBALL: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color1 = { 200, 0, 0, 255 }; + const RwRGBA color2 = { 200, 200, 200, 255 }; + for (int32 i = 0; i < 10; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + RwRGBA color = color2; + if (nFrameGen & 1) + color = color1; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + if (m_nCollisionDamageEffect == DAMAGE_EFFECT_BURST_BEACHBALL) { + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, vecPos); + } else { + PlayOneShotScriptObject(SCRIPT_SOUND_TIRE_COLLISION, vecPos); + } + break; + } + case DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color1 = { 200, 0, 0, 255 }; + const RwRGBA color2 = { 200, 200, 200, 255 }; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + for (int32 i = 0; i < 32; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + const RwRGBA &color = nFrameGen & 1 ? color1 : color2; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + CRGBA possibleColor1; + CRGBA possibleColor2; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: + possibleColor1 = CRGBA(0xC0, 0x3E, 0xC, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: + possibleColor1 = CRGBA(0xA3, 0x36, 0x21, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: + possibleColor1 = CRGBA(0x12, 0x31, 0x24, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: + possibleColor1 = CRGBA(0xC0, 0xC8, 0xBE, 0xFF); + possibleColor2 = CRGBA(0x10, 0x57, 0x85, 0xFF); + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: + possibleColor1 = CRGBA(0xD0, 0x94, 0x1B, 0xFF); + possibleColor2 = possibleColor1; + break; + } + for (int32 i = 0; i < 16; i++) { + CVector vecDir( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.15f) + fDirectionZ + ); + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + ++nFrameGen; + int32 nCurFrame = nFrameGen & 0x3; + CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); + if (!(i % 7)) { + static CRGBA secondParticleColors[4] = { + CRGBA(0xA0, 0x60, 0x60, 0xFF), + CRGBA(0x60, 0xA0, 0x60, 0xFF), + CRGBA(0x60, 0x60, 0xA0, 0xFF), + CRGBA(0xA0, 0xA0, 0xA0, 0xFF) + }; + vecDir *= 0.5f; + CRGBA &secondParticleColor = secondParticleColors[nFrameGen & 3]; + int32 nSecondRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_DEBRIS, vecPos, vecDir, nil, 0.1f, secondParticleColor, nSecondRotationSpeed, 0, 1, 0); + } + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_VEGPALM: + { + static RwRGBA primaryColor1 = { 0x39, 0x4D, 0x29, 0xff }; + static RwRGBA primaryColor2 = { 0x94, 0x7D, 0x73, 0xff }; + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + float fRadius = GetColModel()->boundingSphere.radius; + for (int32 i = 0; i < 32; i++) { + CVector particleDir = CVector( + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.05f, 0.05f) + fDirectionZ + ); + CVector particlePos = vecPos; + particlePos.z += CGeneral::GetRandomNumberInRange(0.0f, 1.0f) * fRadius; + ++nFrameGen; + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + int32 nCurFrame = nFrameGen & 0x3; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + RwRGBA& particleColor = nFrameGen & 1 ? primaryColor1 : primaryColor2; + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, particlePos, particleDir, nil, fSize, particleColor, nRotationSpeed, 0, nCurFrame, 0); + if ((i % 7) == 0) { + static RwRGBA secondaryColor = { 0x9A, 0x99, 0x99, 0x3E }; + CParticle::AddParticle(PARTICLE_DEBRIS, particlePos, particleDir, nil, 0.3, secondaryColor, nRotationSpeed, 0, 0, 0); + } + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_BLACKBAG: + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + CRGBA possibleColor1; + CRGBA possibleColor2; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_BLACKBAG: + possibleColor1 = CRGBA(0, 0, 0, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: + possibleColor1 = CRGBA(0x8F, 0x8A, 0x8C, 0xFF); + possibleColor2 = CRGBA(0x73, 0x75, 0x7B, 0xFF); + break; + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: + possibleColor1 = CRGBA(0x52, 0x92, 0x4A, 0xFF); + possibleColor2 = CRGBA(0xCE, 0xCF, 0xCE, 0xFF); + break; + } + for (int32 i = 0; i < 16; i++) { + CVector vecDir( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 nCurFrame = nFrameGen & 0x3; + CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); + } + if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BLACKBAG) { + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + } else if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD) { + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + } + break; + } + default: + DEV("Unhandled collision damage effect id: %d\n", m_nCollisionDamageEffect); + return; + } + } +} + +void +CObject::RefModelInfo(int32 modelId) +{ + m_nRefModelIndex = modelId; + CModelInfo::GetModelInfo(modelId)->AddRef(); +} + +void +CObject::Init(void) +{ + m_type = ENTITY_TYPE_OBJECT; + CObjectData::SetObjectData(GetModelIndex(), *this); + m_nEndOfLifeTime = 0; + ObjectCreatedBy = GAME_OBJECT; + SetIsStatic(true); + bIsPickup = false; + bPickupObjWithMessage = false; + bOutOfStock = false; + bGlassCracked = false; + bGlassBroken = false; + bHasBeenDamaged = false; + bUseVehicleColours = false; + m_nRefModelIndex = -1; + m_colour1 = 0; + m_colour2 = 0; + m_nBonusValue = 0; + bIsWeapon = false; + m_nCostValue = 0; + m_pCollidingEntity = nil; + CColPoint point; + CEntity *outEntity = nil; + const CVector &vecPos = m_matrix.GetPosition(); + if (CWorld::ProcessVerticalLine(vecPos, vecPos.z - 10.0f, point, outEntity, true, false, false, false, false, false, nil)) + m_pCurSurface = outEntity; + else + m_pCurSurface = nil; + + if (GetModelIndex() == MI_BUOY) + bTouchingWater = true; + + if (CModelInfo::GetModelInfo(GetModelIndex())->GetModelType() == MITYPE_WEAPON) + bIsWeapon = true; + bIsStreetLight = IsLightObject(GetModelIndex()); + + m_area = AREA_EVERYWHERE; +} + +bool +CObject::CanBeDeleted(void) +{ + switch (ObjectCreatedBy) { + case GAME_OBJECT: + return true; + case MISSION_OBJECT: + return false; + case TEMP_OBJECT: + return true; + case CUTSCENE_OBJECT: + return false; + case CONTROLLED_SUB_OBJECT: + return false; + default: + return true; + } +} + +void +CObject::DeleteAllMissionObjects() +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == MISSION_OBJECT) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +void +CObject::DeleteAllTempObjects() +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +void +CObject::DeleteAllTempObjectsInArea(CVector point, float fRadius) +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT && (point - pObject->GetPosition()).MagnitudeSqr() < SQR(fRadius)) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +bool +IsObjectPointerValid(CObject *pObject) +{ + if (!pObject) + return false; + int index = CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pObject); +#ifdef FIX_BUGS + if (index < 0 || index >= CPools::GetObjectPool()->GetSize()) +#else + if (index < 0 || index > CPools::GetObjectPool()->GetSize()) +#endif + return false; + return pObject->bIsBIGBuilding || pObject->m_entryInfoList.first; +} diff --git a/src/objects/Object.h b/src/objects/Object.h new file mode 100644 index 0000000..e34043a --- /dev/null +++ b/src/objects/Object.h @@ -0,0 +1,116 @@ +#pragma once + +#include "Physical.h" + +enum { + UNKNOWN_OBJECT = 0, + GAME_OBJECT = 1, + MISSION_OBJECT = 2, + TEMP_OBJECT = 3, + CUTSCENE_OBJECT = 4, + CONTROLLED_SUB_OBJECT = 5, +}; + +enum CollisionSpecialResponseCase +{ + COLLRESPONSE_NONE, + COLLRESPONSE_LAMPOST, + COLLRESPONSE_SMALLBOX, + COLLRESPONSE_BIGBOX, + COLLRESPONSE_FENCEPART, + COLLRESPONSE_UNKNOWN5 +}; + +enum CollisionDamageEffect +{ + DAMAGE_EFFECT_NONE, + DAMAGE_EFFECT_CHANGE_MODEL, + DAMAGE_EFFECT_SPLIT_MODEL, + DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS, + + DAMAGE_EFFECT_SMASH_COMPLETELY = 20, + DAMAGE_EFFECT_CHANGE_THEN_SMASH, + + DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY = 50, + DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY = 51, + + DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY = 60, + DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY = 70, + DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY = 80, + + DAMAGE_EFFECT_SMASH_NEWSTANDNEW1 = 91, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW2 = 92, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW3 = 93, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW4 = 94, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW5 = 95, + + DAMAGE_EFFECT_SMASH_BLACKBAG = 100, + DAMAGE_EFFECT_SMASH_VEGPALM = 110, + DAMAGE_EFFECT_BURST_BEACHBALL = 120, + DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD = 131, + DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL = 132, +}; + +class CVehicle; +class CDummyObject; + +class CObject : public CPhysical +{ +public: + CMatrix m_objectMatrix; + float m_fUprootLimit; + int8 ObjectCreatedBy; + uint8 bIsPickup : 1; + uint8 bAmmoCollected : 1; + uint8 bPickupObjWithMessage : 1; + uint8 bOutOfStock : 1; + uint8 bGlassCracked : 1; + uint8 bGlassBroken : 1; + uint8 bHasBeenDamaged : 1; + uint8 bUseVehicleColours : 1; + uint8 bIsWeapon : 1; + uint8 bIsStreetLight : 1; + int8 m_nBonusValue; + uint16 m_nCostValue; + float m_fCollisionDamageMultiplier; + uint8 m_nCollisionDamageEffect; + uint8 m_nSpecialCollisionResponseCases; + bool m_bCameraToAvoidThisObject; + uint8 m_nBeachballBounces; + uint32 m_obj_unused1; + uint32 m_nEndOfLifeTime; + int16 m_nRefModelIndex; + CEntity *m_pCurSurface; + CEntity *m_pCollidingEntity; + int8 m_colour1, m_colour2; + + static int16 nNoTempObjects; + static float fDistToNearestTree; + + static void *operator new(size_t); + static void *operator new(size_t, int); + static void operator delete(void*, size_t); + static void operator delete(void*, int); + + CObject(void); + CObject(int32, bool); + CObject(CDummyObject*); + ~CObject(void); + + void ProcessControl(void); + void Teleport(CVector vecPos); + void Render(void); + bool SetupLighting(void); + void RemoveLighting(bool reset); + + void ObjectDamage(float amount); + void RefModelInfo(int32 modelId); + void Init(void); + bool CanBeDeleted(void); + + static void DeleteAllMissionObjects(); + static void DeleteAllTempObjects(); + static void DeleteAllTempObjectsInArea(CVector point, float fRadius); +}; + +bool IsObjectPointerValid(CObject* pObject); diff --git a/src/objects/ObjectData.cpp b/src/objects/ObjectData.cpp new file mode 100644 index 0000000..8b23f0a --- /dev/null +++ b/src/objects/ObjectData.cpp @@ -0,0 +1,161 @@ +#include "common.h" + +#include "main.h" +#include "ModelInfo.h" +#include "Object.h" +#include "FileMgr.h" +#include "ObjectData.h" + +CObjectInfo CObjectData::ms_aObjectInfo[NUMOBJECTINFO]; + +// Another ugly file reader +void +CObjectData::Initialise(const char *filename) +{ + char *p, *lp; + char line[1024], name[256]; + int id; + float percentSubmerged; + int damageEffect, responseCase, camAvoid; + CBaseModelInfo *mi; + + ms_aObjectInfo[0].m_fMass = 99999.0f; + ms_aObjectInfo[0].m_fTurnMass = 99999.0f; + ms_aObjectInfo[0].m_fAirResistance = 0.99f; + ms_aObjectInfo[0].m_fElasticity = 0.1f; + ms_aObjectInfo[0].m_fBuoyancy = GRAVITY * ms_aObjectInfo[0].m_fMass * 2.0f; + ms_aObjectInfo[0].m_fUprootLimit = 0.0f; + ms_aObjectInfo[0].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[0].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[0].m_nSpecialCollisionResponseCases = 0; + ms_aObjectInfo[0].m_bCameraToAvoidThisObject = false; + + ms_aObjectInfo[1].m_fMass = 99999.0f; + ms_aObjectInfo[1].m_fTurnMass = 99999.0f; + ms_aObjectInfo[1].m_fAirResistance = 0.99f; + ms_aObjectInfo[1].m_fElasticity = 0.1f; + ms_aObjectInfo[1].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[1].m_fUprootLimit = 0.0f; + ms_aObjectInfo[1].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[1].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[1].m_nSpecialCollisionResponseCases = 0; + ms_aObjectInfo[1].m_bCameraToAvoidThisObject = true; + + ms_aObjectInfo[2].m_fMass = 99999.0f; + ms_aObjectInfo[2].m_fTurnMass = 99999.0f; + ms_aObjectInfo[2].m_fAirResistance = 0.99f; + ms_aObjectInfo[2].m_fElasticity = 0.1f; + ms_aObjectInfo[2].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[2].m_fUprootLimit = 0.0f; + ms_aObjectInfo[2].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[2].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[2].m_bCameraToAvoidThisObject = false; + ms_aObjectInfo[2].m_nSpecialCollisionResponseCases = 4; + + ms_aObjectInfo[3].m_fMass = 99999.0f; + ms_aObjectInfo[3].m_fTurnMass = 99999.0f; + ms_aObjectInfo[3].m_fAirResistance = 0.99f; + ms_aObjectInfo[3].m_fElasticity = 0.1f; + ms_aObjectInfo[3].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[3].m_fUprootLimit = 0.0f; + ms_aObjectInfo[3].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[3].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[3].m_nSpecialCollisionResponseCases = 4; + ms_aObjectInfo[3].m_bCameraToAvoidThisObject = true; + + CFileMgr::SetDir(""); + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + + id = 4; + p = (char*)work_buff; + while(*p != '*'){ + // skip over white space and comments + while(*p == ' ' || *p == '\n' || *p == '\r' || *p == ';') + if(*p == ';') + while(*p != '\n' && *p != '*') + p++; + else + p++; + + if(*p == '*') + break; + + // read one line + lp = line; + while(*p != '\n' && *p != '*'){ + *lp++ = *p == ',' ? ' ' : *p; + p++; + } + if(*p == '\n') + p++; +#ifdef FIX_BUGS + *lp = '\0'; // FIX: game wrote '\n' here +#else + *lp = '\n'; +#endif + + assert(id < NUMOBJECTINFO); + sscanf(line, "%s %f %f %f %f %f %f %f %d %d %d", name, + &ms_aObjectInfo[id].m_fMass, + &ms_aObjectInfo[id].m_fTurnMass, + &ms_aObjectInfo[id].m_fAirResistance, + &ms_aObjectInfo[id].m_fElasticity, + &percentSubmerged, + &ms_aObjectInfo[id].m_fUprootLimit, + &ms_aObjectInfo[id].m_fCollisionDamageMultiplier, + &damageEffect, &responseCase, &camAvoid); + + ms_aObjectInfo[id].m_fBuoyancy = 100.0f/percentSubmerged * GRAVITY *ms_aObjectInfo[id].m_fMass; + ms_aObjectInfo[id].m_nCollisionDamageEffect = damageEffect; + ms_aObjectInfo[id].m_nSpecialCollisionResponseCases = responseCase; + ms_aObjectInfo[id].m_bCameraToAvoidThisObject = camAvoid; + + mi = CModelInfo::GetModelInfo(name, nil); + if (mi) { + if (ms_aObjectInfo[0].m_fMass != ms_aObjectInfo[id].m_fMass + || ms_aObjectInfo[0].m_fCollisionDamageMultiplier != ms_aObjectInfo[id].m_fCollisionDamageMultiplier + || ms_aObjectInfo[0].m_nCollisionDamageEffect != ms_aObjectInfo[id].m_nCollisionDamageEffect + || ((ms_aObjectInfo[0].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) + && (ms_aObjectInfo[2].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases))) { + mi->SetObjectID(id++); + } else if (ms_aObjectInfo[0].m_nSpecialCollisionResponseCases == ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) { + if (ms_aObjectInfo[0].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) + mi->SetObjectID(0); + else + mi->SetObjectID(1); + } else if (ms_aObjectInfo[2].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) + mi->SetObjectID(2); + else + mi->SetObjectID(3); + } else + debug("CObjectData: Cannot find object %s\n", name); + } +} + +void +CObjectData::SetObjectData(int32 modelId, CObject &object) +{ + CObjectInfo *objinfo; + + if(CModelInfo::GetModelInfo(modelId)->GetObjectID() == -1) + return; + + objinfo = &ms_aObjectInfo[CModelInfo::GetModelInfo(modelId)->GetObjectID()]; + + object.m_fMass = objinfo->m_fMass; + object.m_fTurnMass = objinfo->m_fTurnMass; + object.m_fAirResistance = objinfo->m_fAirResistance; + object.m_fElasticity = objinfo->m_fElasticity; + object.m_fBuoyancy = objinfo->m_fBuoyancy; + object.m_fUprootLimit = objinfo->m_fUprootLimit; + object.m_fCollisionDamageMultiplier = objinfo->m_fCollisionDamageMultiplier; + object.m_nCollisionDamageEffect = objinfo->m_nCollisionDamageEffect; + object.m_nSpecialCollisionResponseCases = objinfo->m_nSpecialCollisionResponseCases; + object.m_bCameraToAvoidThisObject = objinfo->m_bCameraToAvoidThisObject; + if(object.m_fMass >= 99998.0f){ + object.bInfiniteMass = true; + object.m_phy_flagA08 = true; + object.bAffectedByGravity = false; + object.bExplosionProof = true; + } +} diff --git a/src/objects/ObjectData.h b/src/objects/ObjectData.h new file mode 100644 index 0000000..e25c1ae --- /dev/null +++ b/src/objects/ObjectData.h @@ -0,0 +1,27 @@ +#pragma once + +class CObject; + +class CObjectInfo +{ +public: + float m_fMass; + float m_fTurnMass; + float m_fAirResistance; + float m_fElasticity; + float m_fBuoyancy; + float m_fUprootLimit; + float m_fCollisionDamageMultiplier; + uint8 m_nCollisionDamageEffect; + uint8 m_nSpecialCollisionResponseCases; + bool m_bCameraToAvoidThisObject; +}; +VALIDATE_SIZE(CObjectInfo, 0x20); + +class CObjectData +{ + static CObjectInfo ms_aObjectInfo[NUMOBJECTINFO]; +public: + static void Initialise(const char *filename); + static void SetObjectData(int32 modelId, CObject &object); +}; diff --git a/src/objects/ParticleObject.cpp b/src/objects/ParticleObject.cpp new file mode 100644 index 0000000..4d080d1 --- /dev/null +++ b/src/objects/ParticleObject.cpp @@ -0,0 +1,1246 @@ +#include "common.h" + +#include "ParticleObject.h" +#include "Timer.h" +#include "General.h" +#include "ParticleMgr.h" +#include "Particle.h" +#include "Camera.h" +#include "Game.h" +#include "DMAudio.h" +#include "screendroplets.h" + + +CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; + +CParticleObject *CParticleObject::pCloseListHead; +CParticleObject *CParticleObject::pFarListHead; +CParticleObject *CParticleObject::pUnusedListHead; + +CAudioHydrant List[MAX_AUDIOHYDRANTS]; + +CAudioHydrant *CAudioHydrant::Get(int n) { return &List[n]; } + +bool +CAudioHydrant::Add(CParticleObject *particleobject) +{ + for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) + { + if ( List[i].AudioEntity == AEHANDLE_NONE ) + { + List[i].AudioEntity = DMAudio.CreateEntity(AUDIOTYPE_FIREHYDRANT, particleobject); + + if ( AEHANDLE_IS_FAILED(List[i].AudioEntity) ) + return false; + + DMAudio.SetEntityStatus(List[i].AudioEntity, true); + + List[i].pParticleObject = particleobject; + + return true; + } + } + + return false; +} + +void +CAudioHydrant::Remove(CParticleObject *particleobject) +{ + for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) + { + if ( List[i].pParticleObject == particleobject ) + { + DMAudio.DestroyEntity(List[i].AudioEntity); + List[i].AudioEntity = AEHANDLE_NONE; + List[i].pParticleObject = nil; + } + } +} + +CParticleObject::CParticleObject() : + CPlaceable(), + m_nFrameCounter(0), + m_nState(POBJECTSTATE_INITIALISED), + m_pNext(nil), + m_pPrev(nil), + m_nRemoveTimer(0) + +{ + ; +} + +CParticleObject::~CParticleObject() +{ + +} + +void +CParticleObject::Initialise() +{ + pCloseListHead = nil; + pFarListHead = nil; + + pUnusedListHead = &gPObjectArray[0]; + + for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) + { + if ( i == 0 ) + gPObjectArray[i].m_pPrev = nil; + else + gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; + + if ( i == MAX_PARTICLEOBJECTS-1 ) + gPObjectArray[i].m_pNext = nil; + else + gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; + + gPObjectArray[i].m_nState = POBJECTSTATE_FREE; + } +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + CVector target(0.0f, 0.0f, 0.0f); + return AddObject(type, pos, target, 0.0f, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, float size, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + CVector target(0.0f, 0.0f, 0.0f); + return AddObject(type, pos, target, size, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + return AddObject(type, pos, target, size, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove) +{ + CParticleObject *pobj = pUnusedListHead; + + if ( pobj == nil ) + { + printf("Error: No particle objects available!\n"); + return nil; + } + + MoveToList(&pUnusedListHead, &pCloseListHead, pobj); + + pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; + pobj->m_Type = (eParticleObjectType)type; + + pobj->SetPosition(pos); + pobj->m_vecTarget = target; + + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_nFrameCounter = 0; + + pobj->m_bRemove = remove; + + pobj->m_pParticle = nil; + + if ( lifeTime != 0 ) + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; + else + pobj->m_nRemoveTimer = 0; + + if ( color.alpha != 0 ) + pobj->m_Color = color; + else + pobj->m_Color.alpha = 0; + + pobj->m_fSize = size; + pobj->m_fRandVal = 0.0f; + + switch ( type ) + { + case POBJECT_PAVEMENT_STEAM: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_PAVEMENT_STEAM_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_WALL_STEAM: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_WALL_STEAM_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_DARK_SMOKE: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + pobj->m_Color = CRGBA(16, 16, 16, 255); + break; + } + + case POBJECT_WATER_FOUNTAIN_VERT: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.1f); + break; + } + + case POBJECT_WATER_FOUNTAIN_HORIZ: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + break; + } + + case POBJECT_FIRE_HYDRANT: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.3f); + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + 5000; + CAudioHydrant::Add(pobj); + break; + } + + case POBJECT_CAR_WATER_SPLASH: + case POBJECT_PED_WATER_SPLASH: + { + pobj->m_ParticleType = PARTICLE_CAR_SPLASH; + pobj->m_nNumEffectCycles = 0; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; +#ifdef SCREEN_DROPLETS + ScreenDroplets::RegisterSplash(pobj); +#endif + break; + } + + case POBJECT_SPLASHES_AROUND: + { + pobj->m_ParticleType = PARTICLE_SPLASH; + pobj->m_nNumEffectCycles = 15; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 0; + break; + } + + case POBJECT_SMALL_FIRE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 2; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_BIG_FIRE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 4; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_DRY_ICE: + { + pobj->m_ParticleType = PARTICLE_SMOKE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_DRY_ICE_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_SMOKE_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_FIRE_TRAIL: + { + pobj->m_ParticleType = PARTICLE_EXPLOSION_MEDIUM; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 2; + pobj->m_fRandVal = 0.01f; + break; + } + + case POBJECT_SMOKE_TRAIL: + { + pobj->m_ParticleType = PARTICLE_FIREBALL_SMOKE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 2; + pobj->m_fRandVal = 0.02f; + break; + } + + case POBJECT_FIREBALL_AND_SMOKE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_fRandVal = 0.1f; + break; + } + + case POBJECT_ROCKET_TRAIL: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 8; + pobj->m_fRandVal = 0.1f; + break; + } + + case POBJECT_EXPLOSION_ONCE: + { + pobj->m_ParticleType = PARTICLE_EXPLOSION_LARGE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds(); + break; + } + } + + return pobj; +} + +CParticleObject * +CParticleObject::AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove) +{ + CParticleObject *pobj = pUnusedListHead; + + ASSERT(pobj != nil); + + if ( pobj == nil ) + { + printf("Error: No particle objects available!\n"); + return nil; + } + + MoveToList(&pUnusedListHead, &pCloseListHead, pobj); + + pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; + pobj->m_Type = (eParticleObjectType)-1; + pobj->m_ParticleType = type; + + pobj->SetPosition(pos); + pobj->m_vecTarget = target; + + pobj->m_nNumEffectCycles = numEffectCycles; + pobj->m_nSkipFrames = skipFrames; + pobj->m_nCreationChance = creationChance; + pobj->m_nFrameCounter = 0; + + pobj->m_bRemove = remove; + + if ( lifeTime != 0 ) + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; + else + pobj->m_nRemoveTimer = 0; + + pobj->m_Color.alpha = 0; + + pobj->m_fSize = size; + pobj->m_fRandVal = 0.0f; + + return pobj; +} + +void +CParticleObject::RemoveObject(void) +{ + switch ( this->m_nState ) + { + case POBJECTSTATE_UPDATE_CLOSE: + { + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + break; + } + case POBJECTSTATE_UPDATE_FAR: + { + MoveToList(&pFarListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + break; + } + } +} + +void +CParticleObject::UpdateAll(void) +{ + { + CParticleObject *pobj = pCloseListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + pobj->UpdateClose(); + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } + + { + int32 frame = CTimer::GetFrameCounter() & 31; + int32 counter = 0; + + CParticleObject *pobj = pFarListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + + if ( counter == frame ) + { + pobj->UpdateFar(); + frame += 32; + } + + counter++; + + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } +} + +void CParticleObject::UpdateClose(void) +{ + if ( !CGame::playingIntro ) + { + if ( (this->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(100.0f) ) + { + if ( this->m_bRemove ) + { + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + } + else + { + MoveToList(&pCloseListHead, &pFarListHead, this); + this->m_nState = POBJECTSTATE_UPDATE_FAR; + } + + return; + } + } + + if ( ++this->m_nFrameCounter >= this->m_nSkipFrames ) + { + this->m_nFrameCounter = 0; + + int32 randVal; + if ( this->m_nCreationChance != 0 ) + randVal = CGeneral::GetRandomNumber() % this->m_nCreationChance; + + if ( this->m_nCreationChance == 0 + || randVal == 0 && this->m_nCreationChance < 0 + || randVal != 0 && this->m_nCreationChance > 0) + { + switch ( this->m_Type ) + { + case POBJECT_SMALL_FIRE: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector flamevel; + + flamevel.x = vel.x; + flamevel.y = vel.y; + flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*size, 0.1f*size); + + CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, size); + + + CVector possmoke = pos; + + possmoke.x += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); + possmoke.y += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); + possmoke.z += CGeneral::GetRandomNumberInRange(0.625f* size, size*2.5f); + + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, possmoke, vel); + + break; + } + + case POBJECT_BIG_FIRE: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + + float s = 0.7f*size; + + CVector flamevel; + + flamevel.x = vel.x; + flamevel.y = vel.y; + flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*s, 0.1f*s); + + float flamesize = 0.8f*size; + + CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, flamesize); + + + for ( int32 i = 0; i < 4; i++ ) + { + CVector smokepos = pos; + + smokepos.x += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); + smokepos.y += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); + smokepos.z += CGeneral::GetRandomNumberInRange(0.625f* size, 3.5f *size); + + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, smokepos, vel); + } + + break; + } + + case POBJECT_FIREBALL_AND_SMOKE: + { + if ( this->m_pParticle == nil ) + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector expvel = 1.2f*vel; + float expsize = 1.2f*size; + this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, expvel, nil, expsize); + } + else + { + CVector pos = this->GetPosition(); // this->m_pParticle->m_vecPosition ? + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector veloffset = 0.35f*vel; + CVector fireballvel = vel; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + fireballvel.x += CGeneral::GetRandomNumberInRange(-veloffset.x, veloffset.x); + fireballvel.y += CGeneral::GetRandomNumberInRange(-veloffset.y, veloffset.y); + fireballvel.z += CGeneral::GetRandomNumberInRange(-veloffset.z, veloffset.z); + + CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, size); + } + } + + break; + } + + case POBJECT_ROCKET_TRAIL: + { + if ( this->m_pParticle == nil ) + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, vel, nil, size); + } + else + { + CVector pos = this->m_pParticle->m_vecPosition; + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + float fireballsize = size * 1.5f; + CVector fireballvel = vel * -0.8f; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, fireballsize); + } + } + + break; + } + + case POBJECT_FIRE_TRAIL: + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector vel = this->m_vecTarget; + + if ( vel.x != 0.0f ) + vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.y != 0.0f ) + vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.z != 0.0f ) + vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, this->m_fSize, + CGeneral::GetRandomNumberInRange(-6.0f, 6.0f)); + } + + break; + } + + case POBJECT_PED_WATER_SPLASH: + { + CRGBA colorsmoke(255, 255, 255, 196); + + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 3; i++ ) + { + int32 angle = 90 * i; + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(0.75f*fCos, 0.75f*fSin, 0.0f); + splashvel = vel + CVector(0.05f*fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*fCos, 0.75f*-fSin, 0.0f); + splashvel = vel + CVector(0.05f*fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*-fCos, 0.75f*fSin, 0.0f); + splashvel = vel + CVector(0.05f*-fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*-fCos, 0.75f*-fSin, 0.0f); + splashvel = vel + CVector(0.05f*-fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + } + + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * (i + 1); + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(0.5f*fCos, 0.5f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*fCos, 0.5f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*-fCos, 0.5f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*-fCos, 0.5f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + } + + break; + } + + case POBJECT_CAR_WATER_SPLASH: + { + CRGBA colorsmoke(255, 255, 255, 196); + + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + float size = CGeneral::GetRandomNumberInRange(1.0f, 2.5f); + + for ( int32 i = 0; i < 3; i++ ) + { + int32 angle = 90 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(2.0f*fCos, 2.0f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + + splashpos = pos + CVector(2.0f*fCos, 2.0f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(2.0f*-fCos, 2.0f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(2.0f*-fCos, 2.0f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + } + + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * (i + 1); + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + + splashpos = pos + CVector(1.25f*fCos, 1.25f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*fCos, 1.25f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*-fCos, 1.25f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*-fCos, 1.25f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + } + + break; + } + + case POBJECT_SPLASHES_AROUND: + { + CVector pos = this->GetPosition(); + float size = this->m_fSize; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector splashpos = pos; + + splashpos.x += CGeneral::GetRandomNumberInRange(-size, size); + splashpos.y += CGeneral::GetRandomNumberInRange(-size, size); + + if ( CGeneral::GetRandomNumber() & 1 ) + { + CParticle::AddParticle(PARTICLE_RAIN_SPLASH, splashpos, CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, this->m_Color); + } + else + { + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, splashpos, CVector(0.0f, 0.0f, 0.0f), + nil, 0.12f, this->m_Color); + } + } + + break; + } + + case POBJECT_FIRE_HYDRANT: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + if ( (TheCamera.GetPosition() - pos).Magnitude() > 5.0f ) + { + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos, splashvel; + + splashpos = pos + CVector(0.01f*fCos, 0.01f*fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*fCos, 0.01f*-fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*-fCos, 0.01f*fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*-fCos, 0.01f*-fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + } + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(this->m_ParticleType, pos, vel, nil, 0.0f, this->m_Color); + } + } + + break; + } + + case POBJECT_WATER_FOUNTAIN_VERT: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 2; i++ ) + { + int32 angle = 180 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos, splashvel; + + splashpos = pos + CVector(0.015f*fCos, 0.015f*fSin, 0.0f); + splashvel = vel + CVector(0.015f*fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*fCos, 0.015f*-fSin, 0.0f); + splashvel = vel + CVector(0.015f*fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*-fCos, 0.015f*fSin, 0.0f); + splashvel = vel + CVector(0.015f*-fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*-fCos, 0.015f*-fSin, 0.0f); + splashvel = vel + CVector(0.015f*-fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + } + + break; + } + + case POBJECT_WATER_FOUNTAIN_HORIZ: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 3; i++ ) + { + CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, vel, nil, 0.001f, this->m_Color, 0, 0, 1, 1000); + } + + break; + } + + default: + { + if ( this->m_fRandVal != 0.0f ) + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector vel = this->m_vecTarget; + + if ( vel.x != 0.0f ) + vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.y != 0.0f ) + vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.z != 0.0f ) + vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, + this->m_fSize, this->m_Color); + } + } + else + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), this->m_vecTarget, nil, + this->m_fSize, this->m_Color); + } + } + + break; + } + } + } + } + + if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) + { + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + } +} + +void +CParticleObject::UpdateFar(void) +{ + if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) + { + MoveToList(&pFarListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + } + + CVector2D dist = this->GetPosition() - TheCamera.GetPosition(); + if ( dist.MagnitudeSqr() < SQR(100.0f)/*10000.0f*/ ) + { + MoveToList(&pFarListHead, &pCloseListHead, this); + this->m_nState = POBJECTSTATE_UPDATE_CLOSE; + } +} + +bool +CParticleObject::SaveParticle(uint8 *buffer, uint32 *length) +{ + ASSERT( buffer != nil ); + ASSERT( length != nil ); + + int32 numObjects = 0; + + for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) + ++numObjects; + + for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) + ++numObjects; + + *(int32 *)buffer = numObjects; + buffer += sizeof(int32); + + int32 objectsLength = sizeof(CParticleObject) * (numObjects + 1); + int32 dataLength = objectsLength + sizeof(int32); + + for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) + { +#if 0 // todo better + *(CParticleObject*)buffer = *p; +#else + memcpy(buffer, p, sizeof(CParticleObject)); +#endif + buffer += sizeof(CParticleObject); + } + + for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) + { +#if 0 // todo better + *(CParticleObject*)buffer = *p; +#else + memcpy(buffer, p, sizeof(CParticleObject)); +#endif + buffer += sizeof(CParticleObject); + } + + *length = dataLength; + + return true; +} + +bool +CParticleObject::LoadParticle(uint8 *buffer, uint32 length) +{ + ASSERT( buffer != nil ); + + RemoveAllParticleObjects(); + + int32 numObjects = *(int32 *)buffer; + buffer += sizeof(int32); + + if ( length != sizeof(CParticleObject) * (numObjects + 1) + sizeof(int32) ) + return false; + + if ( numObjects == 0 ) + return true; + + + int32 i = 0; + while ( i < numObjects ) + { + CParticleObject *dst = pUnusedListHead; + CParticleObject *src = (CParticleObject *)buffer; + buffer += sizeof(CParticleObject); + + if ( dst == nil ) + return false; + + MoveToList(&pUnusedListHead, &pCloseListHead, dst); + + dst->m_nState = POBJECTSTATE_UPDATE_CLOSE; + dst->m_Type = src->m_Type; + dst->m_ParticleType = src->m_ParticleType; + dst->SetPosition(src->GetPosition()); + dst->m_vecTarget = src->m_vecTarget; + dst->m_nFrameCounter = src->m_nFrameCounter; + dst->m_bRemove = src->m_bRemove; + dst->m_pParticle = nil; + dst->m_nRemoveTimer = src->m_nRemoveTimer; + dst->m_Color = src->m_Color; + dst->m_fSize = src->m_fSize; + dst->m_fRandVal = src->m_fRandVal; + dst->m_nNumEffectCycles = src->m_nNumEffectCycles; + dst->m_nSkipFrames = src->m_nSkipFrames; + dst->m_nCreationChance = src->m_nCreationChance; + + i++; + } + + return true; +} + +void +CParticleObject::RemoveAllExpireableParticleObjects(void) +{ + { + CParticleObject *pobj = pCloseListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + if ( pobj->m_nRemoveTimer != 0 ) + { + MoveToList(&pCloseListHead, &pUnusedListHead, pobj); + pobj->m_nState = POBJECTSTATE_FREE; + } + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } + + { + CParticleObject *pobj = pFarListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + if ( pobj->m_nRemoveTimer != 0 ) + { + MoveToList(&pFarListHead, &pUnusedListHead, pobj); + pobj->m_nState = POBJECTSTATE_FREE; + } + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } +} + +void +CParticleObject::RemoveAllParticleObjects(void) +{ + pUnusedListHead = &gPObjectArray[0]; + + pCloseListHead = nil; + pFarListHead = nil; + + for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) + { + if ( i == 0 ) + gPObjectArray[i].m_pPrev = nil; + else + gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; + + if ( i == MAX_PARTICLEOBJECTS-1 ) + gPObjectArray[i].m_pNext = nil; + else + gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; + + gPObjectArray[i].m_nState = POBJECTSTATE_FREE; + } +} + +void +CParticleObject::MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj) +{ + ASSERT( from != nil ); + ASSERT( to != nil ); + ASSERT( obj != nil ); + + if ( obj->m_pPrev == nil ) + { + *from = obj->m_pNext; + if ( *from ) + (*from)->m_pPrev = nil; + } + else + { + if ( obj->m_pNext == nil ) + obj->m_pPrev->m_pNext = nil; + else + { + obj->m_pNext->m_pPrev = obj->m_pPrev; + obj->m_pPrev->m_pNext = obj->m_pNext; + } + } + + obj->m_pNext = *to; + obj->m_pPrev = nil; + *to = obj; + + if ( obj->m_pNext ) + obj->m_pNext->m_pPrev = obj; +} diff --git a/src/objects/ParticleObject.h b/src/objects/ParticleObject.h new file mode 100644 index 0000000..f199e53 --- /dev/null +++ b/src/objects/ParticleObject.h @@ -0,0 +1,112 @@ +#pragma once + +#include "AudioManager.h" +#include "ParticleType.h" +#include "Placeable.h" + +#define MAX_PARTICLEOBJECTS 70 +#define MAX_AUDIOHYDRANTS 8 + +enum eParticleObjectType +{ + POBJECT_PAVEMENT_STEAM = 0, + POBJECT_PAVEMENT_STEAM_SLOWMOTION, + POBJECT_WALL_STEAM, + POBJECT_WALL_STEAM_SLOWMOTION, + POBJECT_DARK_SMOKE, + POBJECT_FIRE_HYDRANT, + POBJECT_CAR_WATER_SPLASH, + POBJECT_PED_WATER_SPLASH, + POBJECT_SPLASHES_AROUND, + POBJECT_SMALL_FIRE, + POBJECT_BIG_FIRE, + POBJECT_DRY_ICE, + POBJECT_DRY_ICE_SLOWMOTION, + POBJECT_WATER_FOUNTAIN_VERT, + POBJECT_WATER_FOUNTAIN_HORIZ, + POBJECT_FIRE_TRAIL, + POBJECT_SMOKE_TRAIL, + POBJECT_FIREBALL_AND_SMOKE, + POBJECT_ROCKET_TRAIL, + POBJECT_EXPLOSION_ONCE, + POBJECT_CATALINAS_GUNFLASH, + POBJECT_CATALINAS_SHOTGUNFLASH, +}; + +enum eParticleObjectState +{ + POBJECTSTATE_INITIALISED = 0, + POBJECTSTATE_UPDATE_CLOSE, + POBJECTSTATE_UPDATE_FAR, + POBJECTSTATE_FREE, +}; + +class CParticle; + +class CParticleObject : public CPlaceable +{ +public: + CParticleObject *m_pNext; + CParticleObject *m_pPrev; + CParticle *m_pParticle; + uint32 m_nRemoveTimer; + eParticleObjectType m_Type; + tParticleType m_ParticleType; + uint8 m_nNumEffectCycles; + uint8 m_nSkipFrames; + uint16 m_nFrameCounter; + uint16 m_nState; + CVector m_vecTarget; + float m_fRandVal; + float m_fSize; + CRGBA m_Color; + uint8 m_bRemove; + int8 m_nCreationChance; + + static CParticleObject *pCloseListHead; + static CParticleObject *pFarListHead; + static CParticleObject *pUnusedListHead; + + CParticleObject(); + ~CParticleObject(); + + static void Initialise(void); + + static CParticleObject *AddObject(uint16 type, CVector const &pos, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, float size, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove); + static CParticleObject *AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove); + + void RemoveObject(void); + + static void UpdateAll(void); + void UpdateClose(void); + void UpdateFar(void); + + static bool SaveParticle(uint8 *buffer, uint32 *length); + static bool LoadParticle(uint8 *buffer, uint32 length); + + static void RemoveAllExpireableParticleObjects(void); + static void RemoveAllParticleObjects(void); + static void MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj); +}; + +extern CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; + +class CAudioHydrant +{ +public: + int32 AudioEntity; + CParticleObject *pParticleObject; + + CAudioHydrant() : + AudioEntity(AEHANDLE_NONE), + pParticleObject(nil) + { } + + static bool Add (CParticleObject *particleobject); + static void Remove(CParticleObject *particleobject); + + static CAudioHydrant *Get(int n); // for neo screen droplets +}; \ No newline at end of file diff --git a/src/objects/Projectile.cpp b/src/objects/Projectile.cpp new file mode 100644 index 0000000..fe8b0c6 --- /dev/null +++ b/src/objects/Projectile.cpp @@ -0,0 +1,15 @@ +#include "common.h" + +#include "Projectile.h" + +CProjectile::CProjectile(int32 model) : CObject() +{ + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + m_fAirResistance = 0.99999f; + m_fElasticity = 0.75f; + m_fBuoyancy = GRAVITY * m_fMass * 0.1f; + bExplosionProof = true; + SetModelIndex(model); + ObjectCreatedBy = MISSION_OBJECT; +} diff --git a/src/objects/Projectile.h b/src/objects/Projectile.h new file mode 100644 index 0000000..4b3eb4b --- /dev/null +++ b/src/objects/Projectile.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Object.h" + +class CProjectile : public CObject +{ +public: + CProjectile(int32); +}; diff --git a/src/objects/Stinger.cpp b/src/objects/Stinger.cpp new file mode 100644 index 0000000..79730c8 --- /dev/null +++ b/src/objects/Stinger.cpp @@ -0,0 +1,232 @@ +#include "common.h" +#include "Stinger.h" +#include "CopPed.h" +#include "ModelIndices.h" +#include "RpAnimBlend.h" +#include "World.h" +#include "Automobile.h" +#include "Bike.h" +#include "Particle.h" +#include "AnimBlendAssociation.h" +#include "General.h" + +uint32 NumOfStingerSegments; + +/* -- CStingerSegment -- */ + +CStingerSegment::CStingerSegment() +{ + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + m_fAirResistance = 0.99999f; + m_fElasticity = 0.75f; + m_fBuoyancy = GRAVITY * m_fMass * 0.1f; + bExplosionProof = true; + SetModelIndex(MI_PLC_STINGER); + ObjectCreatedBy = CONTROLLED_SUB_OBJECT; + NumOfStingerSegments++; +} + +CStingerSegment::~CStingerSegment() +{ + NumOfStingerSegments--; +} + +/* -- CStinger -- */ + +CStinger::CStinger() +{ + bIsDeployed = false; +} + +void +CStinger::Init(CPed *pPed) +{ + int32 i; + + pOwner = pPed; + for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { + pSpikes[i] = new CStingerSegment; + pSpikes[i]->bUsesCollision = false; + } + bIsDeployed = true; + m_vPos = pPed->GetPosition(); + m_vPos.z -= 1.0f; + m_fMax_Z = Atan2(-pPed->GetForward().x, pPed->GetForward().y) + HALFPI; + + for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { + pSpikes[i]->SetOrientation(0.0f, 0.0f, Atan2(-pPed->GetForward().x, pPed->GetForward().y)); + pSpikes[i]->SetPosition(m_vPos); + } + + CVector2D fwd2d(pPed->GetForward().x, pPed->GetForward().y); + + for (i = 0; i < ARRAY_SIZE(m_vPositions); i++) + m_vPositions[i] = fwd2d * 1.8f * Sin(DEGTORAD(i)); + + m_nSpikeState = STINGERSTATE_NONE; + m_nTimeOfDeploy = CTimer::GetTimeInMilliseconds(); +} + +void +CStinger::Remove() +{ + if (!bIsDeployed) return; + + for (int32 i = 0; i < NUM_STINGER_SEGMENTS; i++) { + CStingerSegment *spikeSegment = pSpikes[i]; + if (spikeSegment->m_entryInfoList.first != nil) + spikeSegment->bRemoveFromWorld = true; + else + delete spikeSegment; + } + bIsDeployed = false; +} + +void +CStinger::Deploy(CPed *pPed) +{ + if (NumOfStingerSegments < NUM_STINGER_SEGMENTS*2 && !pPed->bInVehicle && pPed->IsPedInControl()) { + if (!bIsDeployed && RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_THROW_UNDER) == nil) { + Init(pPed); + pPed->SetPedState(PED_DEPLOY_STINGER); + CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_THROW_UNDER); + } + } +} + +void +CStinger::CheckForBurstTyres() +{ + CVector firstPos = pSpikes[0]->GetPosition(); + firstPos.z += 0.2f; + CVector lastPos = pSpikes[NUM_STINGER_SEGMENTS - 1]->GetPosition(); + lastPos.z += 0.2f; + float dist = (lastPos - firstPos).Magnitude(); + if (dist < 0.1f) return; + + CVehicle *vehsInRange[16]; + int16 numObjects; + CEntity entity; + + CWorld::FindObjectsInRange((lastPos + firstPos) / 2.0f, + dist, true, &numObjects, 15, (CEntity**)vehsInRange, + false, true, false, false, false); + + for (int32 i = 0; i < numObjects; i++) { + CAutomobile *pAutomobile = nil; + CBike *pBike = nil; + + if (vehsInRange[i]->IsCar()) + pAutomobile = (CAutomobile*)vehsInRange[i]; + else if (vehsInRange[i]->IsBike()) + pBike = (CBike*)vehsInRange[i]; + + if (pAutomobile == nil && pBike == nil) continue; + + float maxWheelDistToSpike = sq(((CVehicleModelInfo*)CModelInfo::GetModelInfo(vehsInRange[i]->GetModelIndex()))->m_wheelScale + 0.1f); + + for (int wheelId = 0; wheelId < 4; wheelId++) { + if ((pAutomobile != nil && pAutomobile->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f) || + (pBike != nil && pBike->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f)) { + CVector vecWheelPos; + if (pAutomobile != nil) + vecWheelPos = pAutomobile->m_aWheelColPoints[wheelId].point; + else if (pBike != nil) + vecWheelPos = pBike->m_aWheelColPoints[wheelId].point; + + for (int32 spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { + if ((pSpikes[spike]->GetPosition() - vecWheelPos).Magnitude() < maxWheelDistToSpike) { + if (pBike) { + if (wheelId < 2) + vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); + else + vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); + } + else { + switch (wheelId) { + case 0: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); break; + case 1: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); break; + case 2: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RF, true); break; + case 3: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RR, true); break; + } + } + vecWheelPos.z += 0.15f; + for (int j = 0; j < 4; j++) + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, vecWheelPos, vehsInRange[i]->GetRight() * 0.1f); + } + } + } + } + } +} + +void +CStinger::Process() +{ + switch (m_nSpikeState) + { + case STINGERSTATE_NONE: + if (pOwner != nil + && !pOwner->bInVehicle + && pOwner->GetPedState() == PED_DEPLOY_STINGER + && RpAnimBlendClumpGetAssociation(pOwner->GetClump(), ANIM_STD_THROW_UNDER)->currentTime > 0.39f) + { + m_nSpikeState = STINGERSTATE_DEPLOYING; + for (int i = 0; i < NUM_STINGER_SEGMENTS; i++) + CWorld::Add(pSpikes[i]); + pOwner->SetIdle(); + } + break; + case STINGERSTATE_DEPLOYED: + if (pOwner != nil && pOwner->m_nPedType == PEDTYPE_COP) + ((CCopPed*)pOwner)->m_bThrowsSpikeTrap = false; + break; + case STINGERSTATE_UNDEPLOYING: + if (CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) + m_nSpikeState = STINGERSTATE_REMOVE; + // no break + case STINGERSTATE_DEPLOYING: + if (m_nSpikeState == STINGERSTATE_DEPLOYING && CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) + m_nSpikeState = STINGERSTATE_DEPLOYED; + else { + float progress = (CTimer::GetTimeInMilliseconds() - m_nTimeOfDeploy) / 2500.0f; + if (m_nSpikeState != STINGERSTATE_DEPLOYING) + progress = 1.0f - progress; + + float degangle = progress * ARRAY_SIZE(m_vPositions); + float angle1 = m_fMax_Z + DEGTORAD(degangle); + float angle2 = m_fMax_Z - DEGTORAD(degangle); + int pos = clamp(degangle, 0, ARRAY_SIZE(m_vPositions)-1); + + CVector2D pos2d = m_vPositions[pos]; + CVector pos3d = m_vPos; + CColPoint colPoint; + CEntity *pEntity; + if (CWorld::ProcessVerticalLine(CVector(pos3d.x, pos3d.y, pos3d.z - 10.0f), pos3d.z, colPoint, pEntity, true, false, false, false, true, false, nil)) + pos3d.z = colPoint.point.z + 0.15f; + + angle1 = CGeneral::LimitRadianAngle(angle1); + angle2 = CGeneral::LimitRadianAngle(angle2); + + for (int spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { + if (CWorld::TestSphereAgainstWorld(pos3d + CVector(pos2d.x, pos2d.y, 0.6f), 0.3f, nil, true, false, false, true, false, false)) + pos2d = CVector2D(0.0f, 0.0f); + + if (spike % 2 == 0) { + pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle1); + pos3d.x += pos2d.x; + pos3d.y += pos2d.y; + } else { + pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle2); + } + pSpikes[spike]->SetPosition(pos3d); + } + } + break; + case STINGERSTATE_REMOVE: + Remove(); + break; + } + CheckForBurstTyres(); +} \ No newline at end of file diff --git a/src/objects/Stinger.h b/src/objects/Stinger.h new file mode 100644 index 0000000..250cf62 --- /dev/null +++ b/src/objects/Stinger.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Object.h" + +class CStingerSegment : public CObject +{ +public: + CStingerSegment(); + ~CStingerSegment(); +}; + +#define NUM_STINGER_SEGMENTS (12) + +enum { + STINGERSTATE_NONE = 0, + STINGERSTATE_DEPLOYING, + STINGERSTATE_DEPLOYED, + STINGERSTATE_UNDEPLOYING, + STINGERSTATE_REMOVE, +}; + +class CStinger +{ +public: + bool bIsDeployed; + uint32 m_nTimeOfDeploy; + CVector m_vPos; + float m_fMax_Z; + float m_fMin_Z; + CVector2D m_vPositions[60]; + CStingerSegment *pSpikes[NUM_STINGER_SEGMENTS]; + class CPed *pOwner; + uint8 m_nSpikeState; + CStinger(); + void Init(CPed *pPed); + void Remove(); + void Deploy(CPed *pPed); + void CheckForBurstTyres(); + void Process(); +}; \ No newline at end of file -- cgit v1.2.3