#include "common.h"
#include "General.h"
#include "Timer.h"
#include "TxdStore.h"
#include "Sprite.h"
#include "Camera.h"
#include "Collision.h"
#include "World.h"
#include "Shadows.h"
#include "AudioScriptObject.h"
#include "ParticleObject.h"
#include "Particle.h"
#include "soundlist.h"
#define MAX_PARTICLES_ON_SCREEN (1000)
//(5)
#define MAX_SMOKE_FILES ARRAY_SIZE(SmokeFiles)
//(5)
#define MAX_SMOKE2_FILES ARRAY_SIZE(Smoke2Files)
//(5)
#define MAX_RUBBER_FILES ARRAY_SIZE(RubberFiles)
//(5)
#define MAX_RAINSPLASH_FILES ARRAY_SIZE(RainSplashFiles)
//(3)
#define MAX_WATERSPRAY_FILES ARRAY_SIZE(WatersprayFiles)
//(6)
#define MAX_EXPLOSIONMEDIUM_FILES ARRAY_SIZE(ExplosionMediumFiles)
//(4)
#define MAX_GUNFLASH_FILES ARRAY_SIZE(GunFlashFiles)
//(2)
#define MAX_RAINSPLASHUP_FILES ARRAY_SIZE(RainSplashupFiles)
//(4)
#define MAX_BIRDFRONT_FILES ARRAY_SIZE(BirdfrontFiles)
//(4)
#define MAX_CARDEBRIS_FILES ARRAY_SIZE(CardebrisFiles)
//(4)
#define MAX_CARSPLASH_FILES ARRAY_SIZE(CarsplashFiles)
//(4)
#define MAX_RAINDROP_FILES ARRAY_SIZE(RaindropFiles)
const char SmokeFiles[][6+1] =
{
"smoke1",
"smoke2",
"smoke3",
"smoke4",
"smoke5"
};
const char Smoke2Files[][9+1] =
{
"smokeII_1",
"smokeII_2",
"smokeII_3",
"smokeII_4",
"smokeII_5"
};
const char RubberFiles[][7+1] =
{
"rubber1",
"rubber2",
"rubber3",
"rubber4",
"rubber5"
};
const char RainSplashFiles[][7+1] =
{
"splash1",
"splash2",
"splash3",
"splash4",
"splash5"
};
const char WatersprayFiles[][11+1] =
{
"waterspray1",
"waterspray2",
"waterspray3"
};
const char ExplosionMediumFiles[][7+1] =
{
"explo01",
"explo02",
"explo03",
"explo04",
"explo05",
"explo06"
};
const char GunFlashFiles[][9+1] =
{
"gunflash1",
"gunflash2",
"gunflash3",
"gunflash4"
};
const char RaindropFiles[][9+1] =
{
"raindrop1",
"raindrop2",
"raindrop3",
"raindrop4"
};
const char RainSplashupFiles[][10+1] =
{
"splash_up1",
"splash_up2"
};
const char BirdfrontFiles[][8+1] =
{
"birdf_01",
"birdf_02",
"birdf_03",
"birdf_04"
};
const char CardebrisFiles[][12+1] =
{
"cardebris_01",
"cardebris_02",
"cardebris_03",
"cardebris_04"
};
const char CarsplashFiles[][12+1] =
{
"carsplash_01",
"carsplash_02",
"carsplash_03",
"carsplash_04"
};
CParticle gParticleArray[MAX_PARTICLES_ON_SCREEN];
RwTexture *gpSmokeTex[MAX_SMOKE_FILES];
RwTexture *gpSmoke2Tex[MAX_SMOKE2_FILES];
RwTexture *gpRubberTex[MAX_RUBBER_FILES];
RwTexture *gpRainSplashTex[MAX_RAINSPLASH_FILES];
RwTexture *gpWatersprayTex[MAX_WATERSPRAY_FILES];
RwTexture *gpExplosionMediumTex[MAX_EXPLOSIONMEDIUM_FILES];
RwTexture *gpGunFlashTex[MAX_GUNFLASH_FILES];
RwTexture *gpRainSplashupTex[MAX_RAINSPLASHUP_FILES];
RwTexture *gpBirdfrontTex[MAX_BIRDFRONT_FILES];
RwTexture *gpCarDebrisTex[MAX_CARDEBRIS_FILES];
RwTexture *gpCarSplashTex[MAX_CARSPLASH_FILES];
RwTexture *gpFlame1Tex;
RwTexture *gpFlame5Tex;
RwTexture *gpRainDropSmallTex;
RwTexture *gpBloodTex;
RwTexture *gpLeafTex;
RwTexture *gpCloudTex1; // unused
RwTexture *gpCloudTex4;
RwTexture *gpBloodSmallTex;
RwTexture *gpGungeTex;
RwTexture *gpCollisionSmokeTex;
RwTexture *gpBulletHitTex;
RwTexture *gpGunShellTex;
RwTexture *gpWakeOldTex;
RwTexture *gpPointlightTex;
RwRaster *gpSmokeRaster[MAX_SMOKE_FILES];
RwRaster *gpSmoke2Raster[MAX_SMOKE2_FILES];
RwRaster *gpRubberRaster[MAX_RUBBER_FILES];
RwRaster *gpRainSplashRaster[MAX_RAINSPLASH_FILES];
RwRaster *gpWatersprayRaster[MAX_WATERSPRAY_FILES];
RwRaster *gpExplosionMediumRaster[MAX_EXPLOSIONMEDIUM_FILES];
RwRaster *gpGunFlashRaster[MAX_GUNFLASH_FILES];
RwRaster *gpRainSplashupRaster[MAX_RAINSPLASHUP_FILES];
RwRaster *gpBirdfrontRaster[MAX_BIRDFRONT_FILES];
RwRaster *gpCarDebrisRaster[MAX_CARDEBRIS_FILES];
RwRaster *gpCarSplashRaster[MAX_CARSPLASH_FILES];
RwRaster *gpFlame1Raster;
RwRaster *gpFlame5Raster;
RwRaster *gpRainDropSmallRaster;
RwRaster *gpBloodRaster;
RwRaster *gpLeafRaster;
RwRaster *gpCloudRaster1; // unused
RwRaster *gpCloudRaster4;
RwRaster *gpBloodSmallRaster;
RwRaster *gpGungeRaster;
RwRaster *gpCollisionSmokeRaster;
RwRaster *gpBulletHitRaster;
RwRaster *gpGunShellRaster;
RwRaster *gpWakeOldRaster;
RwRaster *gpPointlightRaster; // CPointLights::RenderFogEffect
RwTexture *gpRainDropTex[MAX_RAINDROP_FILES]; // CWeather::RenderRainStreaks
RwRaster *gpRainDropRaster[MAX_RAINDROP_FILES];
float CParticle::ms_afRandTable[CParticle::RAND_TABLE_SIZE];
CParticle *CParticle::m_pUnusedListHead;
float CParticle::m_SinTable[CParticle::SIN_COS_TABLE_SIZE];
float CParticle::m_CosTable[CParticle::SIN_COS_TABLE_SIZE];
int32 Randomizer;
int32 nParticleCreationInterval = 1;
float fParticleScaleLimit = 0.5f;
#ifdef DEBUGMENU
SETTWEAKPATH("Particle");
TWEAKINT32(nParticleCreationInterval, 0, 5, 1);
TWEAKFLOAT(fParticleScaleLimit, 0.0f, 1.0f, 0.1f);
TWEAKFUNC(CParticle::ReloadConfig);
#endif
void CParticle::ReloadConfig()
{
debug("Initialising CParticleMgr...");
mod_ParticleSystemManager.Initialise();
debug("Initialising CParticle...");
m_pUnusedListHead = gParticleArray;
for ( int32 i = 0; i < MAX_PARTICLES_ON_SCREEN; i++ )
{
if ( i == MAX_PARTICLES_ON_SCREEN - 1 )
gParticleArray[i].m_pNext = nil;
else
gParticleArray[i].m_pNext = &gParticleArray[i + 1];
gParticleArray[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f);
gParticleArray[i].m_vecVelocity = CVector(0.0f, 0.0f, 0.0f);
gParticleArray[i].m_nTimeWhenWillBeDestroyed = 0;
gParticleArray[i].m_nTimeWhenColorWillBeChanged = 0;
gParticleArray[i].m_fSize = 0.2f;
gParticleArray[i].m_fExpansionRate = 0.0f;
gParticleArray[i].m_nColorIntensity = 255;
gParticleArray[i].m_nFadeToBlackTimer = 0;
gParticleArray[i].m_nAlpha = 255;
gParticleArray[i].m_nFadeAlphaTimer = 0;
gParticleArray[i].m_nCurrentZRotation = 0;
gParticleArray[i].m_nZRotationTimer = 0;
gParticleArray[i].m_fCurrentZRadius = 0.0f;
gParticleArray[i].m_nZRadiusTimer = 0;
gParticleArray[i].m_nCurrentFrame = 0;
gParticleArray[i].m_nAnimationSpeedTimer = 0;
gParticleArray[i].m_nRotation = 0;
gParticleArray[i].m_nRotationStep = 0;
}
}
void CParticle::Initialise()
{
ReloadConfig();
CParticleObject::Initialise();
float randVal = -1.0f;
for ( int32 i = 0; i < RAND_TABLE_SIZE; i++ )
{
ms_afRandTable[i] = randVal;
randVal += 0.1f;
}
for ( int32 i = 0; i < SIN_COS_TABLE_SIZE; i++ )
{
float angle = DEGTORAD(float(i) * float(360.0f / SIN_COS_TABLE_SIZE));
m_SinTable[i] = Sin(angle);
m_CosTable[i] = Cos(angle);
}
int32 slot = CTxdStore::FindTxdSlot("particle");
CTxdStore::PushCurrentTxd();
CTxdStore::SetCurrentTxd(slot);
for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ )
{
gpSmokeTex[i] = RwTextureRead(SmokeFiles[i], nil);
gpSmokeRaster[i] = RwTextureGetRaster(gpSmokeTex[i]);
}
for ( int32 i = 0; i < MAX_SMOKE2_FILES; i++ )
{
gpSmoke2Tex[i] = RwTextureRead(Smoke2Files[i], nil);
gpSmoke2Raster[i] = RwTextureGetRaster(gpSmoke2Tex[i]);
}
for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ )
{
gpRubberTex[i] = RwTextureRead(RubberFiles[i], nil);
gpRubberRaster[i] = RwTextureGetRaster(gpRubberTex[i]);
}
for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
{
gpRainSplashTex[i] = RwTextureRead(RainSplashFiles[i], nil);
gpRainSplashRaster[i] = RwTextureGetRaster(gpRainSplashTex[i]);
}
for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
{
gpWatersprayTex[i] = RwTextureRead(WatersprayFiles[i], nil);
gpWatersprayRaster[i] = RwTextureGetRaster(gpWatersprayTex[i]);
}
for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
{
gpExplosionMediumTex[i] = RwTextureRead(ExplosionMediumFiles[i], nil);
gpExplosionMediumRaster[i] = RwTextureGetRaster(gpExplosionMediumTex[i]);
}
for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
{
gpGunFlashTex[i] = RwTextureRead(GunFlashFiles[i], NULL);
gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]);
}
for ( int32 i = 0; i < MAX_RAINDROP_FILES; i++ )
{
gpRainDropTex[i] = RwTextureRead(RaindropFiles[i], nil);
gpRainDropRaster[i] = RwTextureGetRaster(gpRainDropTex[i]);
}
for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
{
gpRainSplashupTex[i] = RwTextureRead(RainSplashupFiles[i], nil);
gpRainSplashupRaster[i] = RwTextureGetRaster(gpRainSplashupTex[i]);
}
for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
{
gpBirdfrontTex[i] = RwTextureRead(BirdfrontFiles[i], NULL);
gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]);
}
for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
{
gpCarDebrisTex[i] = RwTextureRead(CardebrisFiles[i], nil);
gpCarDebrisRaster[i] = RwTextureGetRaster(gpCarDebrisTex[i]);
}
for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
{
gpCarSplashTex[i] = RwTextureRead(CarsplashFiles[i], nil);
gpCarSplashRaster[i] = RwTextureGetRaster(gpCarSplashTex[i]);
}
gpFlame1Tex = RwTextureRead("flame1", NULL);
gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex);
gpFlame5Tex = RwTextureRead("flame5", nil);
#ifdef FIX_BUGS
gpFlame5Raster = RwTextureGetRaster(gpFlame5Tex);
#else
gpFlame5Raster = RwTextureGetRaster(gpFlame1Tex); // copy-paste bug ?
#endif
gpRainDropSmallTex = RwTextureRead("rainsmall", nil);
gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex);
gpBloodTex = RwTextureRead("blood", nil);
gpBloodRaster = RwTextureGetRaster(gpBloodTex);
gpLeafTex = RwTextureRead("gameleaf01_64", nil);
gpLeafRaster = RwTextureGetRaster(gpLeafTex);
gpCloudTex1 = RwTextureRead("cloud3", nil);
gpCloudRaster1 = RwTextureGetRaster(gpCloudTex1);
gpCloudTex4 = RwTextureRead("cloudmasked", nil);
gpCloudRaster4 = RwTextureGetRaster(gpCloudTex4);
gpBloodSmallTex = RwTextureRead("bloodsplat2", nil);
gpBloodSmallRaster = RwTextureGetRaster(gpBloodSmallTex);
gpGungeTex = RwTextureRead("gunge", nil);
gpGungeRaster = RwTextureGetRaster(gpGungeTex);
gpCollisionSmokeTex = RwTextureRead("collisionsmoke", nil);
gpCollisionSmokeRaster = RwTextureGetRaster(gpCollisionSmokeTex);
gpBulletHitTex = RwTextureRead("bullethitsmoke", nil);
gpBulletHitRaster = RwTextureGetRaster(gpBulletHitTex);
gpGunShellTex = RwTextureRead("gunshell", nil);
gpGunShellRaster = RwTextureGetRaster(gpGunShellTex);
gpWakeOldTex = RwTextureRead("wake_old", nil);
gpWakeOldRaster = RwTextureGetRaster(gpWakeOldTex);
gpPointlightTex = RwTextureRead("pointlight", nil);
gpPointlightRaster = RwTextureGetRaster(gpPointlightTex);
CTxdStore::PopCurrentTxd();
for ( int32 i = 0; i < MAX_PARTICLES; i++ )
{
tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i];
switch ( i )
{
case PARTICLE_BLOOD:
entry->m_ppRaster = &gpBloodRaster;
break;
case PARTICLE_BLOOD_SMALL:
case PARTICLE_BLOOD_SPURT:
entry->m_ppRaster = &gpBloodSmallRaster;
break;
case PARTICLE_DEBRIS2:
entry->m_ppRaster = &gpGungeRaster;
break;
case PARTICLE_GUNFLASH:
case PARTICLE_GUNFLASH_NOANIM:
entry->m_ppRaster = gpGunFlashRaster;
break;
case PARTICLE_GUNSMOKE:
case PARTICLE_SPLASH:
entry->m_ppRaster = nil;
break;
case PARTICLE_FLAME:
case PARTICLE_CARFLAME:
entry->m_ppRaster = &gpFlame1Raster;
break;
case PARTICLE_FIREBALL:
entry->m_ppRaster = &gpFlame5Raster;
break;
case PARTICLE_RAIN_SPLASH:
case PARTICLE_RAIN_SPLASH_BIGGROW:
entry->m_ppRaster = gpRainSplashRaster;
break;
case PARTICLE_RAIN_SPLASHUP:
entry->m_ppRaster = gpRainSplashupRaster;
break;
case PARTICLE_WATERSPRAY:
entry->m_ppRaster = gpWatersprayRaster;
break;
case PARTICLE_SHARD:
case PARTICLE_RAINDROP:
case PARTICLE_RAINDROP_2D:
entry->m_ppRaster = gpRainDropRaster;
break;
case PARTICLE_EXPLOSION_MEDIUM:
case PARTICLE_EXPLOSION_LARGE:
case PARTICLE_EXPLOSION_MFAST:
case PARTICLE_EXPLOSION_LFAST:
entry->m_ppRaster = gpExplosionMediumRaster;
break;
case PARTICLE_BOAT_WAKE:
entry->m_ppRaster = &gpWakeOldRaster;
break;
case PARTICLE_CAR_SPLASH:
case PARTICLE_WATER_HYDRANT:
case PARTICLE_PED_SPLASH:
entry->m_ppRaster = gpCarSplashRaster;
break;
case PARTICLE_SPARK:
case PARTICLE_SPARK_SMALL:
case PARTICLE_RAINDROP_SMALL:
case PARTICLE_HELI_ATTACK:
entry->m_ppRaster = &gpRainDropSmallRaster;
break;
case PARTICLE_DEBRIS:
case PARTICLE_TREE_LEAVES:
entry->m_ppRaster = &gpLeafRaster;
break;
case PARTICLE_CAR_DEBRIS:
case PARTICLE_HELI_DEBRIS:
entry->m_ppRaster = gpCarDebrisRaster;
break;
case PARTICLE_WHEEL_DIRT:
case PARTICLE_STEAM2:
case PARTICLE_STEAM_NY:
case PARTICLE_STEAM_NY_SLOWMOTION:
case PARTICLE_ENGINE_STEAM:
case PARTICLE_BOAT_THRUSTJET:
case PARTICLE_PEDFOOT_DUST:
case PARTICLE_EXHAUST_FUMES:
entry->m_ppRaster = gpSmoke2Raster;
break;
case PARTICLE_GUNSMOKE2:
case PARTICLE_RUBBER_SMOKE:
entry->m_ppRaster = gpRubberRaster;
break;
case PARTICLE_CARCOLLISION_DUST:
case PARTICLE_BURNINGRUBBER_SMOKE:
entry->m_ppRaster = &gpCollisionSmokeRaster;
break;
case PARTICLE_WHEEL_WATER:
case PARTICLE_WATER:
case PARTICLE_SMOKE:
case PARTICLE_SMOKE_SLOWMOTION:
case PARTICLE_GARAGEPAINT_SPRAY:
case PARTICLE_STEAM:
case PARTICLE_BOAT_SPLASH:
case PARTICLE_WATER_CANNON:
case PARTICLE_EXTINGUISH_STEAM:
case PARTICLE_HELI_DUST:
case PARTICLE_PAINT_SMOKE:
case PARTICLE_BULLETHIT_SMOKE:
entry->m_ppRaster = gpSmokeRaster;
break;
case PARTICLE_GUNSHELL_FIRST:
case PARTICLE_GUNSHELL:
case PARTICLE_GUNSHELL_BUMP1:
case PARTICLE_GUNSHELL_BUMP2:
entry->m_ppRaster = &gpGunShellRaster;
break;
case PARTICLE_ENGINE_SMOKE:
case PARTICLE_ENGINE_SMOKE2:
case PARTICLE_CARFLAME_SMOKE:
case PARTICLE_FIREBALL_SMOKE:
case PARTICLE_TEST:
entry->m_ppRaster = &gpCloudRaster4;
break;
case PARTICLE_BIRD_FRONT:
entry->m_ppRaster = gpBirdfrontRaster;
break;
}
}
debug("CParticle ready");
}
void CParticle::Shutdown()
{
debug("Shutting down CParticle...");
for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ )
{
RwTextureDestroy(gpSmokeTex[i]);
#ifdef GTA3_1_1_PATCH
gpSmokeTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_SMOKE2_FILES; i++ )
{
RwTextureDestroy(gpSmoke2Tex[i]);
#ifdef GTA3_1_1_PATCH
gpSmoke2Tex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ )
{
RwTextureDestroy(gpRubberTex[i]);
#ifdef GTA3_1_1_PATCH
gpRubberTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
{
RwTextureDestroy(gpRainSplashTex[i]);
#ifdef GTA3_1_1_PATCH
gpRainSplashTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
{
RwTextureDestroy(gpWatersprayTex[i]);
#ifdef GTA3_1_1_PATCH
gpWatersprayTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
{
RwTextureDestroy(gpExplosionMediumTex[i]);
#ifdef GTA3_1_1_PATCH
gpExplosionMediumTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
{
RwTextureDestroy(gpGunFlashTex[i]);
#ifdef GTA3_1_1_PATCH
gpGunFlashTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_RAINDROP_FILES; i++ )
{
RwTextureDestroy(gpRainDropTex[i]);
#ifdef GTA3_1_1_PATCH
gpRainDropTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
{
RwTextureDestroy(gpRainSplashupTex[i]);
#ifdef GTA3_1_1_PATCH
gpRainSplashupTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
{
RwTextureDestroy(gpBirdfrontTex[i]);
#ifdef GTA3_1_1_PATCH
gpBirdfrontTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
{
RwTextureDestroy(gpCarDebrisTex[i]);
#ifdef GTA3_1_1_PATCH
gpCarDebrisTex[i] = nil;
#endif
}
for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
{
RwTextureDestroy(gpCarSplashTex[i]);
#ifdef GTA3_1_1_PATCH
gpCarSplashTex[i] = nil;
#endif
}
RwTextureDestroy(gpFlame1Tex);
#ifdef GTA3_1_1_PATCH
gpFlame1Tex = nil;
#endif
RwTextureDestroy(gpFlame5Tex);
#ifdef GTA3_1_1_PATCH
gpFlame5Tex = nil;
#endif
RwTextureDestroy(gpRainDropSmallTex);
#ifdef GTA3_1_1_PATCH
gpRainDropSmallTex = nil;
#endif
RwTextureDestroy(gpBloodTex);
#ifdef GTA3_1_1_PATCH
gpBloodTex = nil;
#endif
RwTextureDestroy(gpLeafTex);
#ifdef GTA3_1_1_PATCH
gpLeafTex = nil;
#endif
RwTextureDestroy(gpCloudTex1);
#ifdef GTA3_1_1_PATCH
gpCloudTex1 = nil;
#endif
RwTextureDestroy(gpCloudTex4);
#ifdef GTA3_1_1_PATCH
gpCloudTex4 = nil;
#endif
RwTextureDestroy(gpBloodSmallTex);
#ifdef GTA3_1_1_PATCH
gpBloodSmallTex = nil;
#endif
RwTextureDestroy(gpGungeTex);
#ifdef GTA3_1_1_PATCH
gpGungeTex = nil;
#endif
RwTextureDestroy(gpCollisionSmokeTex);
#ifdef GTA3_1_1_PATCH
gpCollisionSmokeTex = nil;
#endif
RwTextureDestroy(gpBulletHitTex);
#ifdef GTA3_1_1_PATCH
gpBulletHitTex = nil;
#endif
RwTextureDestroy(gpGunShellTex);
#ifdef GTA3_1_1_PATCH
gpGunShellTex = nil;
#endif
RwTextureDestroy(gpWakeOldTex);
#ifdef GTA3_1_1_PATCH
gpWakeOldTex = nil;
#endif
RwTextureDestroy(gpPointlightTex);
#ifdef GTA3_1_1_PATCH
gpPointlightTex = nil;
#endif
int32 slot;
slot = CTxdStore::FindTxdSlot("particle");
CTxdStore::RemoveTxdSlot(slot);
debug("CParticle shut down");
}
CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan)
{
CRGBA color(0, 0, 0, 0);
return AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan);
}
CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan)
{
if ( CTimer::GetIsPaused() )
return NULL;
#ifdef PC_PARTICLE
if ( ( type == PARTICLE_ENGINE_SMOKE
|| type == PARTICLE_ENGINE_SMOKE2
|| type == PARTICLE_ENGINE_STEAM
|| type == PARTICLE_CARFLAME_SMOKE
|| type == PARTICLE_RUBBER_SMOKE
|| type == PARTICLE_BURNINGRUBBER_SMOKE
|| type == PARTICLE_EXHAUST_FUMES
|| type == PARTICLE_CARCOLLISION_DUST )
&& nParticleCreationInterval & CTimer::GetFrameCounter() )
{
return nil;
}
#endif
CParticle *pParticle = m_pUnusedListHead;
if ( pParticle == nil )
return nil;
tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[type];
if ( psystem->m_fCreateRange != 0.0f && psystem->m_fCreateRange < ( TheCamera.GetPosition() - vecPos ).MagnitudeSqr() )
return nil;
pParticle->m_fSize = psystem->m_fDefaultInitialRadius;
pParticle->m_fExpansionRate = psystem->m_fExpansionRate;
if ( nLifeSpan != 0 )
pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + nLifeSpan;
else
pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + psystem->m_nLifeSpan;
pParticle->m_nColorIntensity = psystem->m_nFadeToBlackInitialIntensity;
pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity;
pParticle->m_nCurrentZRotation = psystem->m_nZRotationInitialAngle;
pParticle->m_fCurrentZRadius = psystem->m_fInitialZRadius;
if ( nCurFrame != 0 )
pParticle->m_nCurrentFrame = nCurFrame;
else
pParticle->m_nCurrentFrame = psystem->m_nStartAnimationFrame;
pParticle->m_nFadeToBlackTimer = 0;
pParticle->m_nFadeAlphaTimer = 0;
pParticle->m_nZRotationTimer = 0;
pParticle->m_nZRadiusTimer = 0;
pParticle->m_nAnimationSpeedTimer = 0;
pParticle->m_fZGround = 0.0f;
pParticle->m_vecPosition = vecPos;
pParticle->m_vecVelocity = vecDir;
pParticle->m_vecParticleMovementOffset = CVector(0.0f, 0.0f, 0.0f);
pParticle->m_nTimeWhenColorWillBeChanged = 0;
if ( color.alpha != 0 )
RwRGBAAssign(&pParticle->m_Color, &color);
else
{
RwRGBAAssign(&pParticle->m_Color, &psystem->m_RenderColouring);
if ( psystem->m_ColorFadeTime != 0 )
pParticle->m_nTimeWhenColorWillBeChanged = CTimer::GetTimeInMilliseconds() + psystem->m_ColorFadeTime;
if ( psystem->m_InitialColorVariation != 0 )
{
int32 ColorVariation = CGeneral::GetRandomNumberInRange(-psystem->m_InitialColorVariation, psystem->m_InitialColorVariation);
//Float ColorVariation = CGeneral::GetRandomNumberInRange((float)-psystem->m_InitialColorVariation, (float)psystem->m_InitialColorVariation);
pParticle->m_Color.red = clamp(pParticle->m_Color.red +
PERCENT(pParticle->m_Color.red, ColorVariation),
0, 255);
pParticle->m_Color.green = clamp(pParticle->m_Color.green +
PERCENT(pParticle->m_Color.green, ColorVariation),
0, 255);
pParticle->m_Color.blue = clamp(pParticle->m_Color.blue +
PERCENT(pParticle->m_Color.blue, ColorVariation),
0, 255);
}
}
pParticle->m_nRotation = nRotation;
// PC only
if ( pParticle->m_nRotation >= 360 )
pParticle->m_nRotation -= 360;
else if ( pParticle->m_nRotation < 0 )
pParticle->m_nRotation += 360;
if ( nRotationSpeed != 0 )
pParticle->m_nRotationStep = nRotationSpeed;
else
pParticle->m_nRotationStep = psystem->m_nRotationSpeed;
if ( CGeneral::GetRandomNumber() & 1 )
pParticle->m_nRotationStep = -pParticle->m_nRotationStep;
pParticle->m_vecScreenPosition.x = 0.0f; // bug ?
if ( psystem->m_fPositionRandomError != 0.0f )
{
pParticle->m_vecPosition.x += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
pParticle->m_vecPosition.y += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
if ( psystem->Flags & RAND_VERT_V )
pParticle->m_vecPosition.z += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
}
if ( psystem->m_fVelocityRandomError != 0.0f )
{
pParticle->m_vecVelocity.x += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
pParticle->m_vecVelocity.y += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
if ( psystem->Flags & RAND_VERT_V )
pParticle->m_vecVelocity.z += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
}
if ( psystem->m_fExpansionRateError != 0.0f )
pParticle->m_fExpansionRate += psystem->m_fExpansionRateError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE] + psystem->m_fExpansionRateError;
if ( psystem->m_nRotationRateError != 0 )
pParticle->m_nRotationStep += CGeneral::GetRandomNumberInRange(-psystem->m_nRotationRateError, psystem->m_nRotationRateError);
if ( psystem->m_nLifeSpanErrorShape != 0 )
{
float randVal = ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
if ( randVal > 0.0f )
pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal * float(psystem->m_nLifeSpanErrorShape));
else
pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal / float(psystem->m_nLifeSpanErrorShape));
}
if ( psystem->Flags & ZCHECK_FIRST )
{
static bool bValidGroundFound = false;
static CVector LastTestCoors;
static float LastTestGroundZ;
if ( bValidGroundFound
&& vecPos.x == LastTestCoors.x
&& vecPos.y == LastTestCoors.y
&& vecPos.z == LastTestCoors.z )
{
pParticle->m_fZGround = LastTestGroundZ;
}
else
{
bValidGroundFound = false;
CColPoint point;
CEntity *entity;
if ( !CWorld::ProcessVerticalLine(
pParticle->m_vecPosition + CVector(0.0f, 0.0f, 0.5f),
-100.0f, point, entity, true, true, false, false, true, false, nil) )
{
return nil;
}
if ( point.point.z >= pParticle->m_vecPosition.z )
return nil;
pParticle->m_fZGround = point.point.z;
bValidGroundFound = true;
LastTestCoors = vecPos;
LastTestGroundZ = point.point.z;
}
}
if ( psystem->Flags & ZCHECK_BUMP )
{
static float Z_Ground = 0.0f;
if ( psystem->Flags & ZCHECK_BUMP_FIRST )
{
bool bZFound = false;
Z_Ground = CWorld::FindGroundZFor3DCoord(vecPos.x, vecPos.y, vecPos.z, (bool *)&bZFound);
if ( bZFound == false )
return nil;
pParticle->m_fZGround = Z_Ground;
}
pParticle->m_fZGround = Z_Ground;
}
switch ( type )
{
case PARTICLE_DEBRIS:
pParticle->m_vecVelocity.z *= CGeneral::GetRandomNumberInRange(0.5f, 3.0f);
break;
case PARTICLE_EXPLOSION_MEDIUM:
pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ?
pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7;
pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.3f, 0.8f);
pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
break;
case PARTICLE_EXPLOSION_LARGE:
pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ?
pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7;
pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.8f, 1.4f);
pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.3f, 0.3f);
break;
case PARTICLE_WATER_HYDRANT:
pParticle->m_vecPosition.z += 20.0f * psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
break;
default: break;
}
if ( fSize != 0.0f )
pParticle->m_fSize = fSize;
m_pUnusedListHead = pParticle->m_pNext;
pParticle->m_pNext = psystem->m_pParticles;
psystem->m_pParticles = pParticle;
return pParticle;
}
void CParticle::Update()
{
if ( CTimer::GetIsPaused() )
return;
CRGBA color(0, 0, 0, 0);
float fFricDeccel50 = pow(0.50f, CTimer::GetTimeStep());
float fFricDeccel80 = pow(0.80f, CTimer::GetTimeStep());
float fFricDeccel90 = pow(0.90f, CTimer::GetTimeStep());
float fFricDeccel95 = pow(0.95f, CTimer::GetTimeStep());
float fFricDeccel96 = pow(0.96f, CTimer::GetTimeStep());
float fFricDeccel99 = pow(0.99f, CTimer::GetTimeStep());
CParticleObject::UpdateAll();
for ( int32 i = 0; i < MAX_PARTICLES; i++ )
{
tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i];
CParticle *particle = psystem->m_pParticles;
CParticle *prevParticle = nil;
bool bRemoveParticle;
if ( particle == nil )
continue;
for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) )
{
bRemoveParticle = false;
CVector moveStep = particle->m_vecPosition + ( particle->m_vecVelocity * CTimer::GetTimeStep() );
if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 )
{
bRemoveParticle = true;
continue;
}
if ( particle->m_nTimeWhenColorWillBeChanged != 0 )
{
if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::GetTimeInMilliseconds() )
{
float colorMul = 1.0f - float(particle->m_nTimeWhenColorWillBeChanged - CTimer::GetTimeInMilliseconds()) / float(psystem->m_ColorFadeTime);
particle->m_Color.red = clamp(
psystem->m_RenderColouring.red + int32(float(psystem->m_FadeDestinationColor.red - psystem->m_RenderColouring.red) * colorMul),
0, 255);
particle->m_Color.green = clamp(
psystem->m_RenderColouring.green + int32(float(psystem->m_FadeDestinationColor.green - psystem->m_RenderColouring.green) * colorMul),
0, 255);
particle->m_Color.blue = clamp(
psystem->m_RenderColouring.blue + int32(float(psystem->m_FadeDestinationColor.blue - psystem->m_RenderColouring.blue) * colorMul),
0, 255);
}
else
RwRGBAAssign(&particle->m_Color, &psystem->m_FadeDestinationColor);
}
if ( psystem->Flags & CLIPOUT2D )
{
if ( particle->m_vecPosition.x < -10.0f || particle->m_vecPosition.x > SCREEN_WIDTH + 10.0f
|| particle->m_vecPosition.y < -10.0f || particle->m_vecPosition.y > SCREEN_HEIGHT + 10.0f )
{
bRemoveParticle = true;
continue;
}
}
float size = particle->m_fSize + particle->m_fExpansionRate;
if ( size < 0.0f )
{
bRemoveParticle = true;
continue;
}
particle->m_fSize = size;
switch ( psystem->m_nFrictionDecceleration )
{
case 50:
particle->m_vecVelocity *= fFricDeccel50;
break;
case 80:
particle->m_vecVelocity *= fFricDeccel80;
break;
case 90:
particle->m_vecVelocity *= fFricDeccel90;
break;
case 95:
particle->m_vecVelocity *= fFricDeccel95;
break;
case 96:
particle->m_vecVelocity *= fFricDeccel96;
break;
case 99:
particle->m_vecVelocity *= fFricDeccel99;
break;
}
if ( psystem->m_fGravitationalAcceleration > 0.0f )
{
if ( -50.0f * psystem->m_fGravitationalAcceleration < particle->m_vecVelocity.z )
particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep();
if ( psystem->Flags & ZCHECK_FIRST )
{
if ( particle->m_vecPosition.z < particle->m_fZGround )
{
switch ( psystem->m_Type )
{
case PARTICLE_RAINDROP:
case PARTICLE_RAINDROP_SMALL:
{
bRemoveParticle = true;
if ( CGeneral::GetRandomNumber() & 1 )
{
AddParticle(PARTICLE_RAIN_SPLASH,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0);
}
else
{
AddParticle(PARTICLE_RAIN_SPLASHUP,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0);
}
continue;
}
break;
case PARTICLE_WHEEL_WATER:
{
bRemoveParticle = true;
int32 randVal = CGeneral::GetRandomNumber();
if ( randVal & 1 )
{
if ( (randVal % 5) == 0 )
{
AddParticle(PARTICLE_RAIN_SPLASH,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0);
}
else
{
AddParticle(PARTICLE_RAIN_SPLASHUP,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0);
}
}
continue;
}
break;
case PARTICLE_BLOOD:
case PARTICLE_BLOOD_SMALL:
{
bRemoveParticle = true;
CVector vecPosn = particle->m_vecPosition;
vecPosn.z += 1.0f;
Randomizer++;
int32 randVal = int32(Randomizer & 7);
if ( randVal == 5 )
{
CShadows::AddPermanentShadow(1, gpBloodPoolTex, &vecPosn,
0.1f, 0.0f, 0.0f, -0.1f,
255,
255, 0, 0,
4.0f, (CGeneral::GetRandomNumber() & 4095) + 2000, 1.0f);
}
else if ( randVal == 2 )
{
CShadows::AddPermanentShadow(1, gpBloodPoolTex, &vecPosn,
0.2f, 0.0f, 0.0f, -0.2f,
255,
255, 0, 0,
4.0f, (CGeneral::GetRandomNumber() & 4095) + 8000, 1.0f);
}
continue;
}
break;
default: break;
}
}
}
else if ( psystem->Flags & ZCHECK_STEP )
{
CColPoint point;
CEntity *entity;
if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, moveStep.z, point, entity,
true, true, false, false, true, false, nil) )
{
if ( moveStep.z <= point.point.z )
{
moveStep.z = point.point.z;
if ( psystem->m_Type == PARTICLE_DEBRIS2 )
{
particle->m_vecVelocity.x *= 0.8f;
particle->m_vecVelocity.y *= 0.8f;
particle->m_vecVelocity.z *= -0.4f;
if ( particle->m_vecVelocity.z < 0.005f )
particle->m_vecVelocity.z = 0.0f;
}
}
}
}
else if ( psystem->Flags & ZCHECK_BUMP )
{
if ( particle->m_vecPosition.z < particle->m_fZGround )
{
switch ( psystem->m_Type )
{
case PARTICLE_GUNSHELL_FIRST:
case PARTICLE_GUNSHELL:
{
bRemoveParticle = true;
AddParticle(PARTICLE_GUNSHELL_BUMP1,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector
(
CGeneral::GetRandomNumberInRange(-0.02f, 0.02f),
CGeneral::GetRandomNumberInRange(-0.02f, 0.02f),
CGeneral::GetRandomNumberInRange(0.05f, 0.1f)
),
nil,
particle->m_fSize, color, particle->m_nRotationStep, 0, 0, 0);
PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP, particle->m_vecPosition);
}
break;
case PARTICLE_GUNSHELL_BUMP1:
{
bRemoveParticle = true;
AddParticle(PARTICLE_GUNSHELL_BUMP2,
CVector
(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
0.05f + particle->m_fZGround
),
CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.03f, 0.06f)),
nil,
particle->m_fSize, color, 0, 0, 0, 0);
PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP_SOFT, particle->m_vecPosition);
}
break;
case PARTICLE_GUNSHELL_BUMP2:
{
bRemoveParticle = true;
continue;
}
break;
default: break;
}
}
}
}
else
{
if ( psystem->m_fGravitationalAcceleration < 0.0f )
{
if ( -5.0f * psystem->m_fGravitationalAcceleration > particle->m_vecVelocity.z )
particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep();
}
else
{
if ( psystem->Flags & ZCHECK_STEP )
{
CColPoint point;
CEntity *entity;
if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, moveStep.z, point, entity,
true, false, false, false, true, false, nil) )
{
if ( moveStep.z <= point.point.z )
{
moveStep.z = point.point.z;
if ( psystem->m_Type == PARTICLE_HELI_ATTACK )
{
bRemoveParticle = true;
AddParticle(PARTICLE_STEAM, moveStep, CVector(0.0f, 0.0f, 0.05f), nil, 0.2f, 0, 0, 0, 0);
continue;
}
}
}
}
}
}
if ( psystem->m_nFadeToBlackAmount != 0 )
{
if ( particle->m_nFadeToBlackTimer >= psystem->m_nFadeToBlackTime )
{
particle->m_nFadeToBlackTimer = 0;
particle->m_nColorIntensity = clamp(particle->m_nColorIntensity - psystem->m_nFadeToBlackAmount,
0, 255);
}
else
++particle->m_nFadeToBlackTimer;
}
if ( psystem->m_nFadeAlphaAmount != 0 )
{
if ( particle->m_nFadeAlphaTimer >= psystem->m_nFadeAlphaTime )
{
particle->m_nFadeAlphaTimer = 0;
particle->m_nAlpha = clamp(particle->m_nAlpha - psystem->m_nFadeAlphaAmount,
0, 255);
#ifdef PC_PARTICLE
if ( particle->m_nAlpha == 0 )
{
bRemoveParticle = true;
continue;
}
#endif
}
else
++particle->m_nFadeAlphaTimer;
}
if ( psystem->m_nZRotationAngleChangeAmount != 0 )
{
if ( particle->m_nZRotationTimer >= psystem->m_nZRotationChangeTime )
{
particle->m_nZRotationTimer = 0;
particle->m_nCurrentZRotation += psystem->m_nZRotationAngleChangeAmount;
}
else
++particle->m_nZRotationTimer;
}
if ( psystem->m_fZRadiusChangeAmount != 0.0f )
{
if ( particle->m_nZRadiusTimer >= psystem->m_nZRadiusChangeTime )
{
particle->m_nZRadiusTimer = 0;
particle->m_fCurrentZRadius += psystem->m_fZRadiusChangeAmount;
}
else
++particle->m_nZRadiusTimer;
}
if ( psystem->m_nAnimationSpeed != 0 )
{
if ( particle->m_nAnimationSpeedTimer > psystem->m_nAnimationSpeed )
{
particle->m_nAnimationSpeedTimer = 0;
if ( ++particle->m_nCurrentFrame > psystem->m_nFinalAnimationFrame )
{
if ( psystem->Flags & CYCLE_ANIM )
particle->m_nCurrentFrame = psystem->m_nStartAnimationFrame;
else
--particle->m_nCurrentFrame;
}
}
else
++particle->m_nAnimationSpeedTimer;
}
if ( particle->m_nRotationStep != 0 )
{
particle->m_nRotation += particle->m_nRotationStep;
if ( particle->m_nRotation >= 360 )
particle->m_nRotation -= 360;
else if ( particle->m_nRotation < 0 )
particle->m_nRotation += 360;
}
if ( particle->m_fCurrentZRadius != 0.0f )
{
int32 nRot = particle->m_nCurrentZRotation % (SIN_COS_TABLE_SIZE - 1);
float fX = (Cos(nRot) - Sin(nRot)) * particle->m_fCurrentZRadius;
float fY = (Sin(nRot) + Cos(nRot)) * particle->m_fCurrentZRadius;
moveStep -= particle->m_vecParticleMovementOffset;
moveStep += CVector(fX, fY, 0.0f);
particle->m_vecParticleMovementOffset = CVector(fX, fY, 0.0f);
}
particle->m_vecPosition = moveStep;
}
}
}
void CParticle::Render()
{
RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP);
RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void *)TRUE);
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE);
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE);
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE);
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
CSprite::InitSpriteBuffer2D();
uint32 flags = DRAW_OPAQUE;
RwRaster *prevFrame = nil;
for ( int32 i = 0; i < MAX_PARTICLES; i++ )
{
tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i];
#ifdef PC_PARTICLE
bool particleBanned = false;
#endif
CParticle *particle = psystem->m_pParticles;
RwRaster **frames = psystem->m_ppRaster;
#ifdef PC_PARTICLE
tParticleType type = psystem->m_Type;
if ( type == PARTICLE_ENGINE_SMOKE
|| type == PARTICLE_ENGINE_SMOKE2
|| type == PARTICLE_ENGINE_STEAM
|| type == PARTICLE_CARFLAME_SMOKE
|| type == PARTICLE_RUBBER_SMOKE
|| type == PARTICLE_BURNINGRUBBER_SMOKE
|| type == PARTICLE_EXHAUST_FUMES
|| type == PARTICLE_CARCOLLISION_DUST )
{
particleBanned = true;
}
#endif
if ( particle )
{
if ( (flags & DRAW_OPAQUE) != (psystem->Flags & DRAW_OPAQUE)
|| (flags & DRAW_DARK) != (psystem->Flags & DRAW_DARK) )
{
CSprite::FlushSpriteBuffer();
if ( psystem->Flags & DRAW_OPAQUE )
{
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
}
else
{
if ( psystem->Flags & DRAW_DARK )
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
else
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE);
}
flags = psystem->Flags;
}
if ( frames != nil )
{
RwRaster *curFrame = *frames;
if ( curFrame != prevFrame )
{
CSprite::FlushSpriteBuffer();
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame);
prevFrame = curFrame;
}
}
}
while ( particle != nil )
{
bool canDraw = true;
#ifdef PC_PARTICLE
if ( particle->m_nAlpha == 0 )
canDraw = false;
#endif
if ( canDraw && psystem->m_nFinalAnimationFrame != 0 && frames != nil )
{
RwRaster *curFrame = frames[particle->m_nCurrentFrame];
if ( prevFrame != curFrame )
{
CSprite::FlushSpriteBuffer();
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame);
prevFrame = curFrame;
}
}
if ( canDraw && psystem->Flags & DRAWTOP2D )
{
if ( particle->m_nRotation != 0 )
{
CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
particle->m_fSize * 63.0f,
particle->m_fSize * 63.0f,
particle->m_Color,
particle->m_nColorIntensity,
(float)particle->m_nRotation, //DEGTORAD((float)particle->m_nRotation) ps2
particle->m_nAlpha);
}
else
{
CSprite::RenderBufferedOneXLUSprite2D(
particle->m_vecPosition.x,
particle->m_vecPosition.y,
particle->m_fSize * 63.0f,
particle->m_fSize * 63.0f,
particle->m_Color,
particle->m_nColorIntensity,
particle->m_nAlpha);
}
canDraw = false;
}
if ( canDraw )
{
CVector coors;
float w;
float h;
if ( CSprite::CalcScreenCoors(particle->m_vecPosition, coors, &w, &h, true) )
{
#ifdef PC_PARTICLE
if ( (!particleBanned || SCREEN_WIDTH * fParticleScaleLimit >= w)
&& SCREEN_HEIGHT * fParticleScaleLimit >= h )
#endif
{
if ( particle->m_nRotation != 0 )
{
CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
particle->m_fSize * w, particle->m_fSize * h,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
float(particle->m_nRotation), // DEGTORAD((float)particle->m_nRotation) ps2
particle->m_nAlpha);
}
else if ( psystem->Flags & SCREEN_TRAIL )
{
float fRotation;
float fTrailLength;
if ( particle->m_vecScreenPosition.x == 0.0f )
{
fTrailLength = 0.0f;
fRotation = 0.0f;
}
else
{
CVector2D vecDist
(
coors.x - particle->m_vecScreenPosition.x,
coors.y - particle->m_vecScreenPosition.y
);
float fDist = vecDist.Magnitude();
fTrailLength = fDist;
float fRot = Asin(vecDist.x / fDist);
fRotation = fRot;
if ( vecDist.y < 0.0f )
fRotation = -1.0f * fRot + DEGTORAD(180.0f);
fRotation = RADTODEG(fRotation);
if ( fRotation < 0.0f )
fRotation += 360.0f;
float fSpeed = particle->m_vecVelocity.Magnitude();
float fNewTrailLength = fSpeed * CTimer::GetTimeStep() * w * 2.0f;
if ( fDist > fNewTrailLength )
fTrailLength = fNewTrailLength;
}
CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
particle->m_fSize * w,
particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
fRotation,
particle->m_nAlpha);
particle->m_vecScreenPosition = coors;
}
else if ( psystem->Flags & SPEED_TRAIL )
{
CVector vecPrevPos = particle->m_vecPosition - particle->m_vecVelocity;
float fRotation;
float fTrailLength;
if ( CSprite::CalcScreenCoors(vecPrevPos, particle->m_vecScreenPosition, &fTrailLength, &fRotation, true) )
{
CVector2D vecDist
(
coors.x - particle->m_vecScreenPosition.x,
coors.y - particle->m_vecScreenPosition.y
);
float fDist = vecDist.Magnitude();
fTrailLength = fDist;
float fRot = Asin(vecDist.x / fDist);
fRotation = fRot;
if ( vecDist.y < 0.0f )
fRotation = -1.0f * fRot + DEGTORAD(180.0f);
fRotation = RADTODEG(fRotation);
if ( fRotation < 0.0f )
fRotation += 360.0f;
}
else
{
fRotation = 0.0f;
fTrailLength = 0.0f;
}
CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
particle->m_fSize * w,
particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
fRotation,
particle->m_nAlpha);
}
else if ( psystem->Flags & VERT_TRAIL )
{
float fTrailLength = fabsf(particle->m_vecVelocity.z * 10.0f);
CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
particle->m_fSize * w,
(particle->m_fSize + fTrailLength * psystem->m_fTrailLengthMultiplier) * h,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
particle->m_nAlpha);
}
else if ( i == PARTICLE_RAINDROP_SMALL )
{
CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
particle->m_fSize * w * 0.05f,
particle->m_fSize * h,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
particle->m_nAlpha);
}
else if ( i == PARTICLE_BOAT_WAKE )
{
CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
particle->m_fSize * w,
psystem->m_fDefaultInitialRadius * h,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
particle->m_nAlpha);
}
else
{
CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
particle->m_fSize * w,
particle->m_fSize * h,
particle->m_Color.red,
particle->m_Color.green,
particle->m_Color.blue,
particle->m_nColorIntensity,
1.0f / coors.z,
particle->m_nAlpha);
}
}
}
}
particle = particle->m_pNext;
}
CSprite::FlushSpriteBuffer();
}
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE);
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
}
void CParticle::RemovePSystem(tParticleType type)
{
tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[type];
for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = psystemdata->m_pParticles )
RemoveParticle(particle, nil, psystemdata);
}
void CParticle::RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData)
{
if ( pPrevParticle )
pPrevParticle->m_pNext = pParticle->m_pNext;
else
pPSystemData->m_pParticles = pParticle->m_pNext;
pParticle->m_pNext = m_pUnusedListHead;
m_pUnusedListHead = pParticle;
}
void CParticle::AddJetExplosion(CVector const &vecPos, float fPower, float fSize)
{
CRGBA color(240, 240, 240, 255);
if ( fPower < 1.0f )
fPower = 1.0f;
CVector vecRandOffset
(
CGeneral::GetRandomNumberInRange(-0.4f, 0.4f),
CGeneral::GetRandomNumberInRange(-0.4f, 0.4f),
CGeneral::GetRandomNumberInRange(0.1f, 0.3f)
);
vecRandOffset *= 2.0f;
CVector vecStepPos = vecPos;
for ( int32 i = 0; i < int32(fPower * 4.0f); i++ )
{
AddParticle(PARTICLE_EXPLOSION_MFAST,
vecStepPos,
CVector
(
CGeneral::GetRandomNumberInRange(-0.2f, 0.2f),
CGeneral::GetRandomNumberInRange(-0.2f, 0.2f),
CGeneral::GetRandomNumberInRange(-0.2f, 0.0f)
),
nil,
fSize, color, 0, 0, 0, 0);
AddParticle(PARTICLE_EXPLOSION_MFAST,
vecStepPos,
CVector
(
CGeneral::GetRandomNumberInRange(-0.04f, 0.04f),
CGeneral::GetRandomNumberInRange(-0.04f, 0.04f),
CGeneral::GetRandomNumberInRange(0.0f, 0.07f)
),
nil,
fSize, color, 0, 0, 0, 0);
AddParticle(PARTICLE_EXPLOSION_MFAST,
vecStepPos,
CVector
(
CGeneral::GetRandomNumberInRange(-0.04f, 0.04f),
CGeneral::GetRandomNumberInRange(-0.04f, 0.04f),
CGeneral::GetRandomNumberInRange(0.0f, 0.07f)
),
nil,
fSize, color, 0, 0, 0, 0);
vecStepPos += vecRandOffset;
}
}
void CParticle::AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix)
{
CRGBA color(0, 0, 0, 0);
CMatrix invMat(Invert(matMatrix));
CVector vecBasePos = matMatrix * (invMat * vecPos + CVector(0.0f, -1.0f, 0.5f));
for ( int32 i = 0; i < 5; i++ )
{
CVector pos = vecBasePos;
pos.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f);
pos.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f);
AddParticle(PARTICLE_CARCOLLISION_DUST,
pos,
CVector(0.0f, 0.0f, 0.0f),
nil,
0.3f, color, 0, 0, 0, 0);
}
}