summaryrefslogtreecommitdiffstats
path: root/src/renderer/Particle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer/Particle.cpp')
-rw-r--r--src/renderer/Particle.cpp2328
1 files changed, 2328 insertions, 0 deletions
diff --git a/src/renderer/Particle.cpp b/src/renderer/Particle.cpp
new file mode 100644
index 00000000..54b6ca92
--- /dev/null
+++ b/src/renderer/Particle.cpp
@@ -0,0 +1,2328 @@
+#include "common.h"
+
+#include "main.h"
+#include "General.h"
+#include "Timer.h"
+#include "TxdStore.h"
+#include "Sprite.h"
+#include "Camera.h"
+#include "Clock.h"
+#include "Collision.h"
+#include "World.h"
+#include "Shadows.h"
+#include "Replay.h"
+#include "Stats.h"
+#include "Weather.h"
+#include "WaterLevel.h"
+#include "MBlur.h"
+#include "main.h"
+#include "AudioScriptObject.h"
+#include "ParticleObject.h"
+#include "Particle.h"
+#include "soundlist.h"
+#include "SaveBuf.h"
+#include "debugmenu.h"
+
+#define MAX_PARTICLES_ON_SCREEN (750)
+
+
+//(5)
+#define MAX_SMOKE_FILES ARRAY_SIZE(SmokeFiles)
+
+//(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)
+//(8)
+#define MAX_BOAT_FILES ARRAY_SIZE(BoatFiles)
+//(4)
+#define MAX_CARDEBRIS_FILES ARRAY_SIZE(CardebrisFiles)
+//(4)
+#define MAX_CARSPLASH_FILES ARRAY_SIZE(CarsplashFiles)
+
+#define MAX_RAINDRIP_FILES (2)
+
+#define MAX_LEAF_FILES (2)
+
+
+const char SmokeFiles[][6+1] =
+{
+ "smoke1",
+ "smoke2",
+ "smoke3",
+ "smoke4",
+ "smoke5"
+};
+
+
+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 RainSplashupFiles[][10+1] =
+{
+ "splash_up1",
+ "splash_up2"
+};
+
+const char BirdfrontFiles[][8+1] =
+{
+ "birdf_01",
+ "birdf_02",
+ "birdf_03",
+ "birdf_04"
+};
+
+const char BoatFiles[][8+1] =
+{
+ "boats_01",
+ "boats_02",
+ "boats_03",
+ "boats_04",
+ "boats_05",
+ "boats_06",
+ "boats_07",
+ "boats_08"
+};
+
+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;
+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 *gpBoatTex[MAX_BOAT_FILES];
+RwTexture *gpCarDebrisTex[MAX_CARDEBRIS_FILES];
+RwTexture *gpCarSplashTex[MAX_CARSPLASH_FILES];
+
+RwTexture *gpBoatWakeTex;
+RwTexture *gpFlame1Tex;
+RwTexture *gpFlame5Tex;
+RwTexture *gpRainDropSmallTex;
+RwTexture *gpBloodTex;
+RwTexture *gpLeafTex[MAX_LEAF_FILES];
+RwTexture *gpCloudTex1;
+RwTexture *gpCloudTex4;
+RwTexture *gpBloodSmallTex;
+RwTexture *gpGungeTex;
+RwTexture *gpCollisionSmokeTex;
+RwTexture *gpBulletHitTex;
+RwTexture *gpGunShellTex;
+RwTexture *gpPointlightTex;
+
+RwRaster *gpSmokeRaster[MAX_SMOKE_FILES];
+RwRaster *gpSmoke2Raster;
+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 *gpBoatRaster[MAX_BOAT_FILES];
+RwRaster *gpCarDebrisRaster[MAX_CARDEBRIS_FILES];
+RwRaster *gpCarSplashRaster[MAX_CARSPLASH_FILES];
+
+RwRaster *gpBoatWakeRaster;
+RwRaster *gpFlame1Raster;
+RwRaster *gpFlame5Raster;
+RwRaster *gpRainDropSmallRaster;
+RwRaster *gpBloodRaster;
+RwRaster *gpLeafRaster[MAX_LEAF_FILES];
+RwRaster *gpCloudRaster1;
+RwRaster *gpCloudRaster4;
+RwRaster *gpBloodSmallRaster;
+RwRaster *gpGungeRaster;
+RwRaster *gpCollisionSmokeRaster;
+RwRaster *gpBulletHitRaster;
+RwRaster *gpGunShellRaster;
+RwRaster *gpPointlightRaster;
+
+RwTexture *gpRainDropTex;
+RwRaster *gpRainDropRaster;
+
+RwTexture *gpSparkTex;
+RwTexture *gpNewspaperTex;
+RwTexture *gpGunSmokeTex;
+RwTexture *gpHeatHazeTex;
+RwTexture *gpBeastieTex;
+RwTexture *gpRainDripTex[MAX_RAINDRIP_FILES];
+RwTexture *gpRainDripDarkTex[MAX_RAINDRIP_FILES];
+
+RwRaster *gpSparkRaster;
+RwRaster *gpNewspaperRaster;
+RwRaster *gpGunSmokeRaster;
+RwRaster *gpHeatHazeRaster;
+RwRaster *gpBeastieRaster;
+RwRaster *gpRainDripRaster[MAX_RAINDRIP_FILES];
+RwRaster *gpRainDripDarkRaster[MAX_RAINDRIP_FILES];
+
+RwTexture *gpFireHoseTex;
+RwRaster *gpFireHoseRaster;
+
+RwTexture *gpMultiPlayerHitTex;
+RwRaster *gpMultiPlayerHitRaster;
+
+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;
+float PARTICLE_WIND_TEST_SCALE = 0.002f;
+
+#ifdef DEBUGMENU
+SETTWEAKPATH("Particle");
+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_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);
+ ASSERT(gpSmokeTex[i] != nil);
+ gpSmokeRaster[i] = RwTextureGetRaster(gpSmokeTex[i]);
+ }
+
+ gpSmoke2Tex = RwTextureRead("smokeII_3", nil);
+ ASSERT(gpSmoke2Tex != nil);
+ gpSmoke2Raster = RwTextureGetRaster(gpSmoke2Tex);
+
+ for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ )
+ {
+ gpRubberTex[i] = RwTextureRead(RubberFiles[i], nil);
+ ASSERT(gpRubberTex[i] != nil);
+ gpRubberRaster[i] = RwTextureGetRaster(gpRubberTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
+ {
+ gpRainSplashTex[i] = RwTextureRead(RainSplashFiles[i], nil);
+ ASSERT(gpRainSplashTex[i] != nil);
+ gpRainSplashRaster[i] = RwTextureGetRaster(gpRainSplashTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
+ {
+ gpWatersprayTex[i] = RwTextureRead(WatersprayFiles[i], nil);
+ ASSERT(gpWatersprayTex[i] != nil);
+ gpWatersprayRaster[i] = RwTextureGetRaster(gpWatersprayTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
+ {
+ gpExplosionMediumTex[i] = RwTextureRead(ExplosionMediumFiles[i], nil);
+ ASSERT(gpExplosionMediumTex[i] != nil);
+ gpExplosionMediumRaster[i] = RwTextureGetRaster(gpExplosionMediumTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
+ {
+ gpGunFlashTex[i] = RwTextureRead(GunFlashFiles[i], nil);
+ ASSERT(gpGunFlashTex[i] != nil);
+ gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]);
+ }
+
+ gpRainDropTex = RwTextureRead("raindrop4", nil);
+ ASSERT(gpRainDropTex != nil);
+ gpRainDropRaster = RwTextureGetRaster(gpRainDropTex);
+
+ for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
+ {
+ gpRainSplashupTex[i] = RwTextureRead(RainSplashupFiles[i], nil);
+ ASSERT(gpRainSplashupTex[i] != nil);
+ gpRainSplashupRaster[i] = RwTextureGetRaster(gpRainSplashupTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
+ {
+ gpBirdfrontTex[i] = RwTextureRead(BirdfrontFiles[i], nil);
+ ASSERT(gpBirdfrontTex[i] != nil);
+ gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_BOAT_FILES; i++ )
+ {
+ gpBoatTex[i] = RwTextureRead(BoatFiles[i], nil);
+ ASSERT(gpBoatTex[i] != nil);
+ gpBoatRaster[i] = RwTextureGetRaster(gpBoatTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
+ {
+ gpCarDebrisTex[i] = RwTextureRead(CardebrisFiles[i], nil);
+ ASSERT(gpCarDebrisTex[i] != nil);
+ gpCarDebrisRaster[i] = RwTextureGetRaster(gpCarDebrisTex[i]);
+ }
+
+ for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
+ {
+ gpCarSplashTex[i] = RwTextureRead(CarsplashFiles[i], nil);
+ ASSERT(gpCarSplashTex[i] != nil);
+ gpCarSplashRaster[i] = RwTextureGetRaster(gpCarSplashTex[i]);
+ }
+
+ gpBoatWakeTex = RwTextureRead("boatwake2", nil);
+ ASSERT(gpBoatWakeTex != nil);
+ gpBoatWakeRaster = RwTextureGetRaster(gpBoatWakeTex);
+
+ gpFlame1Tex = RwTextureRead("flame1", nil);
+ ASSERT(gpFlame1Tex != nil);
+ gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex);
+
+ gpFlame5Tex = RwTextureRead("flame5", nil);
+ ASSERT(gpFlame5Tex != nil);
+
+//#ifdef FIX_BUGS
+#if 0
+ gpFlame5Raster = RwTextureGetRaster(gpFlame5Tex);
+#else
+ // this seems to have become more of a design choice
+ gpFlame5Raster = RwTextureGetRaster(gpFlame1Tex); // copy-paste bug ?
+#endif
+
+ gpRainDropSmallTex = RwTextureRead("rainsmall", nil);
+ ASSERT(gpRainDropSmallTex != nil);
+ gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex);
+
+ gpBloodTex = RwTextureRead("blood", nil);
+ ASSERT(gpBloodTex != nil);
+ gpBloodRaster = RwTextureGetRaster(gpBloodTex);
+
+ gpLeafTex[0] = RwTextureRead("gameleaf01_64", nil);
+ ASSERT(gpLeafTex[0] != nil);
+ gpLeafRaster[0] = RwTextureGetRaster(gpLeafTex[0]);
+
+ gpLeafTex[1] = RwTextureRead("letter", nil);
+ ASSERT(gpLeafTex[1] != nil);
+ gpLeafRaster[1] = RwTextureGetRaster(gpLeafTex[1]);
+
+ gpCloudTex1 = RwTextureRead("cloud3", nil);
+ ASSERT(gpCloudTex1 != nil);
+ gpCloudRaster1 = RwTextureGetRaster(gpCloudTex1);
+
+ gpCloudTex4 = RwTextureRead("cloudmasked", nil);
+ ASSERT(gpCloudTex4 != nil);
+ gpCloudRaster4 = RwTextureGetRaster(gpCloudTex4);
+
+ gpBloodSmallTex = RwTextureRead("bloodsplat2", nil);
+ ASSERT(gpBloodSmallTex != nil);
+ gpBloodSmallRaster = RwTextureGetRaster(gpBloodSmallTex);
+
+ gpGungeTex = RwTextureRead("gunge", nil);
+ ASSERT(gpGungeTex != nil);
+ gpGungeRaster = RwTextureGetRaster(gpGungeTex);
+
+ gpCollisionSmokeTex = RwTextureRead("collisionsmoke", nil);
+ ASSERT(gpCollisionSmokeTex != nil);
+ gpCollisionSmokeRaster = RwTextureGetRaster(gpCollisionSmokeTex);
+
+ gpBulletHitTex = RwTextureRead("bullethitsmoke", nil);
+ ASSERT(gpBulletHitTex != nil);
+ gpBulletHitRaster = RwTextureGetRaster(gpBulletHitTex);
+
+ gpGunShellTex = RwTextureRead("gunshell", nil);
+ ASSERT(gpGunShellTex != nil);
+ gpGunShellRaster = RwTextureGetRaster(gpGunShellTex);
+
+ gpPointlightTex = RwTextureRead("smoke5", nil);
+ ASSERT(gpPointlightTex != nil);
+ gpPointlightRaster = RwTextureGetRaster(gpPointlightTex);
+
+ gpSparkTex = RwTextureRead("spark", nil);
+ ASSERT(gpSparkTex != nil);
+ gpSparkRaster = RwTextureGetRaster(gpSparkTex);
+
+ gpNewspaperTex = RwTextureRead("ballot_paper", nil);
+ ASSERT(gpNewspaperTex != nil);
+ gpNewspaperRaster = RwTextureGetRaster(gpNewspaperTex);
+
+ gpGunSmokeTex = RwTextureRead("gunsmoke3", nil);
+ ASSERT(gpGunSmokeTex != nil);
+ gpGunSmokeRaster = RwTextureGetRaster(gpGunSmokeTex);
+
+ gpHeatHazeTex = RwTextureRead("heathaze", nil);
+ ASSERT(gpHeatHazeTex != nil);
+ gpHeatHazeRaster = RwTextureGetRaster(gpHeatHazeTex);
+
+ gpBeastieTex = RwTextureRead("beastie", nil);
+ ASSERT(gpBeastieTex != nil);
+ gpBeastieRaster = RwTextureGetRaster(gpBeastieTex);
+
+ gpRainDripTex[0] = RwTextureRead("raindrip64", nil);
+ ASSERT(gpRainDripTex[0] != nil);
+ gpRainDripRaster[0] = RwTextureGetRaster(gpRainDripTex[0]);
+
+ gpRainDripTex[1] = RwTextureRead("raindripb64", nil);
+ ASSERT(gpRainDripTex[1] != nil);
+ gpRainDripRaster[1] = RwTextureGetRaster(gpRainDripTex[1]);
+
+ gpRainDripDarkTex[0] = RwTextureRead("raindrip64_d", nil);
+ ASSERT(gpRainDripDarkTex[0] != nil);
+ gpRainDripDarkRaster[0] = RwTextureGetRaster(gpRainDripDarkTex[0]);
+
+ gpMultiPlayerHitTex = RwTextureRead("mphit", nil);
+ ASSERT(gpMultiPlayerHitTex != nil);
+ gpMultiPlayerHitRaster = RwTextureGetRaster(gpMultiPlayerHitTex);
+
+ gpFireHoseTex = RwTextureRead("firehose", nil);
+ ASSERT(gpFireHoseTex != nil);
+ gpFireHoseRaster = RwTextureGetRaster(gpFireHoseTex);
+
+ CTxdStore::PopCurrentTxd();
+
+ for ( int32 i = 0; i < MAX_PARTICLES; i++ )
+ {
+ tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i];
+
+ switch( i )
+ {
+ case PARTICLE_SPARK:
+ entry->m_ppRaster = &gpRainDropSmallRaster;
+ break;
+
+ case PARTICLE_SPARK_SMALL:
+ entry->m_ppRaster = &gpRainDropSmallRaster;
+ break;
+
+ case PARTICLE_WATER_SPARK:
+ entry->m_ppRaster = &gpSparkRaster;
+ break;
+
+ case PARTICLE_WHEEL_DIRT:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_SAND:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_WHEEL_WATER:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_BLOOD:
+ entry->m_ppRaster = &gpBloodRaster;
+ break;
+
+ case PARTICLE_BLOOD_SMALL:
+ entry->m_ppRaster = &gpBloodSmallRaster;
+ break;
+
+ case PARTICLE_BLOOD_SPURT:
+ entry->m_ppRaster = &gpBloodSmallRaster;
+ break;
+
+ case PARTICLE_DEBRIS:
+ entry->m_ppRaster = gpLeafRaster;
+ break;
+
+ case PARTICLE_DEBRIS2:
+ entry->m_ppRaster = &gpGungeRaster;
+ break;
+
+ case PARTICLE_FLYERS:
+ entry->m_ppRaster = &gpNewspaperRaster;
+ break;
+
+ case PARTICLE_WATER:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_FLAME:
+ entry->m_ppRaster = &gpFlame1Raster;
+ break;
+
+ case PARTICLE_FIREBALL:
+ entry->m_ppRaster = &gpFlame5Raster;
+ break;
+
+ case PARTICLE_GUNFLASH:
+ entry->m_ppRaster = gpGunFlashRaster;
+ break;
+
+ case PARTICLE_GUNFLASH_NOANIM:
+ entry->m_ppRaster = gpGunFlashRaster;
+ break;
+
+ case PARTICLE_GUNSMOKE:
+ entry->m_ppRaster = nil;
+ break;
+
+ case PARTICLE_GUNSMOKE2:
+ entry->m_ppRaster = gpRubberRaster;
+ break;
+
+ case PARTICLE_CIGARETTE_SMOKE:
+ entry->m_ppRaster = &gpGunSmokeRaster;
+ break;
+
+ case PARTICLE_TEARGAS:
+ entry->m_ppRaster = &gpHeatHazeRaster;
+ break;
+
+ case PARTICLE_SMOKE:
+ case PARTICLE_SMOKE_SLOWMOTION:
+ case PARTICLE_DRY_ICE:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_GARAGEPAINT_SPRAY:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_SHARD:
+ entry->m_ppRaster = &gpRainDropRaster;
+ break;
+
+ case PARTICLE_SPLASH:
+ entry->m_ppRaster = gpCarSplashRaster;
+ break;
+
+ case PARTICLE_CARFLAME:
+ entry->m_ppRaster = &gpFlame1Raster;
+ break;
+
+ case PARTICLE_STEAM:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_STEAM2:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_STEAM_NY:
+ case PARTICLE_STEAM_NY_SLOWMOTION:
+ case PARTICLE_GROUND_STEAM:
+ case PARTICLE_HYDRANT_STEAM:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_ENGINE_STEAM:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_RAINDROP:
+ entry->m_ppRaster = &gpRainDropRaster;
+ break;
+
+ case PARTICLE_RAINDROP_SMALL:
+ entry->m_ppRaster = &gpRainDropSmallRaster;
+ 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_RAINDROP_2D:
+ entry->m_ppRaster = &gpRainDropRaster;
+ break;
+
+ case PARTICLE_EXPLOSION_MEDIUM:
+ entry->m_ppRaster = gpExplosionMediumRaster;
+ break;
+
+ case PARTICLE_EXPLOSION_LARGE:
+ entry->m_ppRaster = gpExplosionMediumRaster;
+ break;
+
+ case PARTICLE_EXPLOSION_MFAST:
+ entry->m_ppRaster = gpExplosionMediumRaster;
+ break;
+
+ case PARTICLE_EXPLOSION_LFAST:
+ entry->m_ppRaster = gpExplosionMediumRaster;
+ break;
+
+ case PARTICLE_CAR_SPLASH:
+ entry->m_ppRaster = gpCarSplashRaster;
+ break;
+
+ case PARTICLE_BOAT_SPLASH:
+ entry->m_ppRaster = &gpBoatWakeRaster;
+ break;
+
+ case PARTICLE_BOAT_THRUSTJET:
+ entry->m_ppRaster = gpRubberRaster;
+ break;
+
+ case PARTICLE_WATER_HYDRANT:
+ entry->m_ppRaster = gpCarSplashRaster;
+ break;
+
+ case PARTICLE_WATER_CANNON:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_EXTINGUISH_STEAM:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_PED_SPLASH:
+ entry->m_ppRaster = gpCarSplashRaster;
+ break;
+
+ case PARTICLE_PEDFOOT_DUST:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_CAR_DUST:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_HELI_DUST:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_HELI_ATTACK:
+ entry->m_ppRaster = &gpRainDropSmallRaster;
+ break;
+
+ case PARTICLE_ENGINE_SMOKE:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_ENGINE_SMOKE2:
+ case PARTICLE_ROCKET_SMOKE:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_CARFLAME_SMOKE:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_FIREBALL_SMOKE:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_PAINT_SMOKE:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_TREE_LEAVES:
+ entry->m_ppRaster = gpLeafRaster;
+ break;
+
+ case PARTICLE_CARCOLLISION_DUST:
+ entry->m_ppRaster = &gpCollisionSmokeRaster;
+ break;
+
+ case PARTICLE_CAR_DEBRIS:
+ case PARTICLE_BIRD_DEBRIS:
+ case PARTICLE_HELI_DEBRIS:
+ entry->m_ppRaster = gpCarDebrisRaster;
+ break;
+
+ case PARTICLE_EXHAUST_FUMES:
+ entry->m_ppRaster = &gpSmoke2Raster;
+ break;
+
+ case PARTICLE_RUBBER_SMOKE:
+ entry->m_ppRaster = gpRubberRaster;
+ break;
+
+ case PARTICLE_BURNINGRUBBER_SMOKE:
+ entry->m_ppRaster = &gpCollisionSmokeRaster;
+ break;
+
+ case PARTICLE_BULLETHIT_SMOKE:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_GUNSHELL_FIRST:
+ entry->m_ppRaster = &gpGunShellRaster;
+ break;
+
+ case PARTICLE_GUNSHELL:
+ entry->m_ppRaster = &gpGunShellRaster;
+ break;
+
+ case PARTICLE_GUNSHELL_BUMP1:
+ entry->m_ppRaster = &gpGunShellRaster;
+ break;
+
+ case PARTICLE_GUNSHELL_BUMP2:
+ entry->m_ppRaster = &gpGunShellRaster;
+ break;
+
+ case PARTICLE_TEST:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_BIRD_FRONT:
+ entry->m_ppRaster = gpBirdfrontRaster;
+ break;
+
+ case PARTICLE_SHIP_SIDE:
+ entry->m_ppRaster = gpBoatRaster;
+ break;
+
+ case PARTICLE_BEASTIE:
+ entry->m_ppRaster = &gpBeastieRaster;
+ break;
+
+ case PARTICLE_FERRY_CHIM_SMOKE:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_MULTIPLAYER_HIT:
+ entry->m_ppRaster = &gpMultiPlayerHitRaster;
+ break;
+ }
+ }
+
+ debug("CParticle ready");
+}
+
+void CParticle::Shutdown()
+{
+ debug("Shutting down CParticle...");
+
+ for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ )
+ {
+ RwTextureDestroy(gpSmokeTex[i]);
+ gpSmokeTex[i] = nil;
+ }
+
+ RwTextureDestroy(gpSmoke2Tex);
+ gpSmoke2Tex = nil;
+
+ for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ )
+ {
+ RwTextureDestroy(gpRubberTex[i]);
+ gpRubberTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainSplashTex[i]);
+ gpRainSplashTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
+ {
+ RwTextureDestroy(gpWatersprayTex[i]);
+ gpWatersprayTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
+ {
+ RwTextureDestroy(gpExplosionMediumTex[i]);
+ gpExplosionMediumTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpGunFlashTex[i]);
+ gpGunFlashTex[i] = nil;
+ }
+
+ RwTextureDestroy(gpRainDropTex);
+ gpRainDropTex = nil;
+
+ for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainSplashupTex[i]);
+ gpRainSplashupTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
+ {
+ RwTextureDestroy(gpBirdfrontTex[i]);
+ gpBirdfrontTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_BOAT_FILES; i++ )
+ {
+ RwTextureDestroy(gpBoatTex[i]);
+ gpBoatTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
+ {
+ RwTextureDestroy(gpCarDebrisTex[i]);
+ gpCarDebrisTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpCarSplashTex[i]);
+ gpCarSplashTex[i] = nil;
+ }
+
+ for ( int32 i = 0; i < MAX_RAINDRIP_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainDripTex[i]);
+ gpRainDripTex[i] = nil;
+
+#ifdef FIX_BUGS
+ if (gpRainDripDarkTex[i])
+#endif
+ {
+ RwTextureDestroy(gpRainDripDarkTex[i]);
+ gpRainDripDarkTex[i] = nil;
+ }
+ }
+
+ RwTextureDestroy(gpBoatWakeTex);
+ gpBoatWakeTex = nil;
+
+ RwTextureDestroy(gpFlame1Tex);
+ gpFlame1Tex = nil;
+
+ RwTextureDestroy(gpFlame5Tex);
+ gpFlame5Tex = nil;
+
+ RwTextureDestroy(gpRainDropSmallTex);
+ gpRainDropSmallTex = nil;
+
+ RwTextureDestroy(gpBloodTex);
+ gpBloodTex = nil;
+
+ RwTextureDestroy(gpLeafTex[0]);
+ gpLeafTex[0] = nil;
+
+ RwTextureDestroy(gpLeafTex[1]);
+ gpLeafTex[1] = nil;
+
+ RwTextureDestroy(gpCloudTex1);
+ gpCloudTex1 = nil;
+
+ RwTextureDestroy(gpCloudTex4);
+ gpCloudTex4 = nil;
+
+ RwTextureDestroy(gpBloodSmallTex);
+ gpBloodSmallTex = nil;
+
+ RwTextureDestroy(gpGungeTex);
+ gpGungeTex = nil;
+
+ RwTextureDestroy(gpCollisionSmokeTex);
+ gpCollisionSmokeTex = nil;
+
+ RwTextureDestroy(gpBulletHitTex);
+ gpBulletHitTex = nil;
+
+ RwTextureDestroy(gpGunShellTex);
+ gpGunShellTex = nil;
+
+ RwTextureDestroy(gpPointlightTex);
+ gpPointlightTex = nil;
+
+ RwTextureDestroy(gpSparkTex);
+ gpSparkTex = nil;
+
+ RwTextureDestroy(gpNewspaperTex);
+ gpNewspaperTex = nil;
+
+ RwTextureDestroy(gpGunSmokeTex);
+ gpGunSmokeTex = nil;
+
+ RwTextureDestroy(gpHeatHazeTex);
+ gpHeatHazeTex = nil;
+
+ RwTextureDestroy(gpBeastieTex);
+ gpBeastieTex = nil;
+
+ RwTextureDestroy(gpMultiPlayerHitTex);
+ gpBeastieTex = nil;
+
+ RwTextureDestroy(gpFireHoseTex);
+ gpFireHoseTex = nil;
+
+ int32 slot;
+
+ slot = CTxdStore::FindTxdSlot("particle");
+ CTxdStore::RemoveTxdSlot(slot);
+
+ debug("CParticle shut down");
+}
+
+
+void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan)
+{
+ CVector vecDist = vecEnd - vecStart;
+ float fDist = vecDist.Magnitude();
+ float fSteps = Max(fDist / fPower, 1.0f);
+ int32 nSteps = (int32)fSteps;
+
+ CVector vecStep = vecDist * (1.0f / (float)nSteps);
+
+ for ( int32 i = 0; i < nSteps; i++ )
+ {
+ CVector vecPos = float(i) * vecStep + vecStart;
+ AddParticle(type, vecPos, vecDir, pEntity, fSize, nRotationSpeed, nRotation, nCurFrame, nLifeSpan);
+ }
+}
+
+void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan)
+{
+ CVector vecDist = vecEnd - vecStart;
+ float fDist = vecDist.Magnitude();
+ float fSteps = Max(fDist / fPower, 1.0f);
+ int32 nSteps = (int32)fSteps;
+
+ CVector vecStep = vecDist * (1.0f / (float)nSteps);
+
+ for ( int32 i = 0; i < nSteps; i++ )
+ {
+ CVector vecPos = float(i) * vecStep + vecStart;
+
+ 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, 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() /*TODO: || byte_355C47*/ )
+ return nil;
+/*
+ if ( !CReplay::IsPlayingBack() )
+ CReplay::RecordParticle(type, vecPos, vecDir, fSize, color);
+*/
+
+ 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_nFadeToBlackTimer = psystem->m_nFadeToBlackAmount;
+
+ if ( psystem->m_nFadeToBlackTime )
+ pParticle->m_nFadeToBlackTimer /= psystem->m_nFadeToBlackTime;
+
+ pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity;
+
+ pParticle->m_nFadeAlphaTimer = psystem->m_nFadeAlphaAmount;
+
+ if ( psystem->m_nFadeAlphaTime )
+ pParticle->m_nFadeAlphaTimer /= psystem->m_nFadeAlphaTime;
+
+ if ( nCurFrame != 0 )
+ pParticle->m_nCurrentFrame = nCurFrame;
+ else
+ pParticle->m_nCurrentFrame = psystem->m_nStartAnimationFrame;
+
+ pParticle->m_nAnimationSpeedTimer = 0;
+ pParticle->m_fZGround = 0.0f;
+ pParticle->m_vecPosition = vecPos;
+ pParticle->m_vecVelocity = vecDir;
+ 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;
+
+ if ( nRotationSpeed != 0 )
+ pParticle->m_nRotationStep = nRotationSpeed;
+ else
+ pParticle->m_nRotationStep = psystem->m_nRotationSpeed;
+
+ if ( CGeneral::GetRandomNumber() & 1 )
+ pParticle->m_nRotationStep = -pParticle->m_nRotationStep;
+
+ 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 && !(psystem->Flags & SCREEN_TRAIL) )
+ 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 -= 2 * 15 * (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 -= 2 * 15 * (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() /*TODO: || byte_355C47*/ )
+ 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();
+
+ // no more erectedcocks
+#ifndef FIX_BUGS
+ // ejaculation at 23:00, 23:15, 23:30, 23:45
+ if ( CClock::ms_nGameClockHours == 23 &&
+ ( CClock::ms_nGameClockMinutes == 0
+ || CClock::ms_nGameClockMinutes == 15
+ || CClock::ms_nGameClockMinutes == 30
+ || CClock::ms_nGameClockMinutes == 45 ) )
+ {
+ AddParticle(PARTICLE_CAR_SPLASH,
+ CVector(557.03f, -4.0f, 151.46f),
+ CVector(0.0f, 0.0f, 2.5f),
+ NULL,
+ 2.0f,
+ CRGBA(255, 255, 255, 255),
+ 0,
+ 0,
+ 1,
+ 1000);
+ }
+#endif
+
+ 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) )
+ {
+ CVector vecWind(0.0f, 0.0f, 0.0f);
+
+ bRemoveParticle = false;
+
+ CVector vecMoveStep = particle->m_vecVelocity * CTimer::GetTimeStep();
+ CVector vecPos = particle->m_vecPosition;
+
+ if ( psystem->m_Type == PARTICLE_BEASTIE )
+ {
+#ifdef FIX_BUGS
+ int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE;
+#else
+ int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE;
+#endif
+ particle->m_vecVelocity.x = 0.50f * Cos(nSinCosIndex);
+ particle->m_vecVelocity.y = Cos(nSinCosIndex);
+ particle->m_vecVelocity.z = 0.25f * Sin(nSinCosIndex);
+
+ if ( particle->m_vecVelocity.Magnitude() > 2.0f
+ || vecPos.z > 40.0f
+ || (TheCamera.GetPosition() - vecPos).Magnitude() < 60.0f
+ )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+ }
+
+ vecPos += vecMoveStep;
+
+ if ( CWeather::Wind > 0.0f )
+ {
+ if ( vecMoveStep.Magnitude() != 0.0f )
+ {
+ vecWind.x = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind;
+ vecWind.y = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind;
+ vecWind *= PARTICLE_WIND_TEST_SCALE * psystem->m_fWindFactor * CTimer::GetTimeStep();
+ particle->m_vecVelocity += vecWind;
+ }
+ }
+
+ if ( psystem->m_Type == PARTICLE_RAINDROP
+ || psystem->m_Type == PARTICLE_RAINDROP_SMALL
+ || psystem->m_Type == PARTICLE_RAIN_SPLASH
+ || psystem->m_Type == PARTICLE_RAIN_SPLASH_BIGGROW
+ || psystem->m_Type == PARTICLE_CAR_SPLASH
+ || psystem->m_Type == PARTICLE_BOAT_SPLASH
+ || psystem->m_Type == PARTICLE_RAINDROP_2D )
+ {
+ //int32 nMaxDrops = int32(6.0f * TheCamera.m_CameraAverageSpeed + 1.0f);
+ float fDistToCam = 0.0f;
+
+ if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH )
+ {
+ float fWaterLevel = 0.0f;
+ CWaterLevel::GetWaterLevelNoWaves(vecPos.x, vecPos.y, vecPos.z, &fWaterLevel);
+
+ if ( vecPos.z + particle->m_fSize < fWaterLevel )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+
+ switch ( TheCamera.GetLookDirection() )
+ {
+ case LOOKING_LEFT:
+ case LOOKING_RIGHT:
+ case LOOKING_FORWARD:
+ //nMaxDrops /= 2;
+ break;
+
+ default:
+ //nMaxDrops = 0;
+ break;
+ }
+
+ fDistToCam = (TheCamera.GetPosition() - vecPos).Magnitude();
+ }
+ }
+
+ 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;
+ }
+ }
+
+ if ( !(psystem->Flags & SCREEN_TRAIL) )
+ {
+ float size;
+
+ if ( particle->m_fExpansionRate > 0.0f )
+ {
+ float speed = Max(vecWind.Magnitude(), vecMoveStep.Magnitude());
+
+ if ( psystem->m_Type == PARTICLE_EXHAUST_FUMES || psystem->m_Type == PARTICLE_ENGINE_STEAM )
+ speed *= 2.0f;
+
+ if ( ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH )
+ && particle->m_fSize > 1.2f )
+ {
+ size = particle->m_fSize - (1.0f + speed) * particle->m_fExpansionRate;
+ particle->m_vecVelocity.z -= 0.15f;
+ }
+ else
+ size = particle->m_fSize + (1.0f + speed) * particle->m_fExpansionRate;
+ }
+ else
+ 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(SHADOWTYPE_DARK, 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(SHADOWTYPE_DARK, 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, vecPos.z, point, entity,
+ true, true, false, false, true, false, nil) )
+ {
+ if ( vecPos.z <= point.point.z )
+ {
+ vecPos.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, vecPos.z, point, entity,
+ true, false, false, false, true, false, nil) )
+ {
+ if ( vecPos.z <= point.point.z )
+ {
+ vecPos.z = point.point.z;
+ if ( psystem->m_Type == PARTICLE_HELI_ATTACK )
+ {
+ bRemoveParticle = true;
+ AddParticle(PARTICLE_STEAM, vecPos, CVector(0.0f, 0.0f, 0.05f), nil, 0.2f, 0, 0, 0, 0);
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( particle->m_nFadeToBlackTimer != 0 )
+ {
+ particle->m_nColorIntensity = Clamp(particle->m_nColorIntensity - particle->m_nFadeToBlackTimer,
+ 0, 255);
+ }
+
+ if ( particle->m_nFadeAlphaTimer != 0 )
+ {
+ particle->m_nAlpha = Clamp(particle->m_nAlpha - particle->m_nFadeAlphaTimer,
+ 0, 255);
+ if ( particle->m_nAlpha == 0 )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+ }
+
+ 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 )
+#ifdef FIX_BUGS
+ particle->m_nRotation = CGeneral::LimitAngle(particle->m_nRotation + particle->m_nRotationStep);
+#else
+ particle->m_nRotation += particle->m_nRotationStep;
+#endif
+
+ particle->m_vecPosition = vecPos;
+ }
+ }
+}
+
+void CParticle::Render()
+{
+ PUSH_RENDERGROUP("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];
+ bool particleBanned = false;
+ CParticle *particle = psystem->m_pParticles;
+
+ RwRaster **frames = psystem->m_ppRaster;
+ tParticleType type = psystem->m_Type;
+
+ /*
+ something on ps2
+ 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;
+ }
+ */
+ 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;
+
+ if ( particle->m_nAlpha == 0 )
+ canDraw = false;
+
+ // TODO: psp vert buffer check
+
+ 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 )
+ {
+ float screenZ = CalcScreenZ(particle->m_vecPosition.z);
+
+ float stretchTexW;
+ float stretchTexH;
+
+ if ( i == PARTICLE_RAINDROP || i == PARTICLE_RAINDROP_SMALL || i == PARTICLE_RAINDROP_2D )
+ {
+ stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x * (float)particle->m_nCurrentFrame + 63.0f;
+ stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y * (float)particle->m_nCurrentFrame + 63.0f;
+ }
+ else
+ {
+ stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x + 63.0f;
+ stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y + 63.0f;
+ }
+
+ if ( canDraw )
+ {
+ if ( particle->m_nRotation != 0 )
+ {
+ CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(
+ particle->m_vecPosition.x,
+ particle->m_vecPosition.y,
+ particle->m_fSize * stretchTexW,
+ particle->m_fSize * stretchTexH,
+ particle->m_Color,
+ particle->m_nColorIntensity,
+ DEGTORAD((float)particle->m_nRotation),
+ particle->m_nAlpha);
+ }
+ else
+ {
+ CSprite::RenderBufferedOneXLUSprite2D(
+ particle->m_vecPosition.x,
+ particle->m_vecPosition.y,
+ particle->m_fSize * stretchTexW,
+ particle->m_fSize * stretchTexH,
+ 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) )
+ {
+
+ if ( i == PARTICLE_ENGINE_STEAM
+ || i == PARTICLE_ENGINE_SMOKE
+ || i == PARTICLE_ENGINE_SMOKE2
+ || i == PARTICLE_CARFLAME_SMOKE
+ || i == PARTICLE_CARCOLLISION_DUST
+ || i == PARTICLE_EXHAUST_FUMES
+ || i == PARTICLE_RUBBER_SMOKE
+ || i == PARTICLE_BURNINGRUBBER_SMOKE )
+ {
+ switch ( TheCamera.GetLookDirection() )
+ {
+ case LOOKING_LEFT:
+ case LOOKING_RIGHT:
+ w += CGeneral::GetRandomNumberInRange(1.0f, 7.5f) * psystem->m_vecTextureStretch.x;
+ h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y;
+ break;
+
+ default:
+ w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x;
+ h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y;
+ break;
+ }
+ }
+ else if ( i == PARTICLE_WATER_HYDRANT )
+ {
+ float screenZ = CalcScreenZ(particle->m_vecPosition.z);
+
+ int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed;
+
+ w += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x;
+ h += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.y;
+ }
+ else if ( i == PARTICLE_FLYERS )
+ {
+ w += psystem->m_vecTextureStretch.x;
+ h += psystem->m_vecTextureStretch.y;
+
+ w = Max(w, 12.0f);
+ h = Max(h, 12.0f);
+ }
+ else
+ {
+ w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x;
+ h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y;
+ }
+
+ if ( particle->m_nRotation != 0 && i != PARTICLE_BEASTIE )
+ {
+ 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,
+ DEGTORAD((float)particle->m_nRotation),
+ particle->m_nAlpha);
+ }
+ else if ( psystem->Flags & SCREEN_TRAIL )
+ {
+ float fRotation;
+ float fTrailLength;
+
+ if ( particle->m_fZGround == 0.0f )
+ {
+ fTrailLength = 0.0f;
+ fRotation = 0.0f;
+ }
+ else
+ {
+ CVector2D vecDist
+ (
+ coors.x - particle->m_fZGround,
+ coors.y - particle->m_fExpansionRate
+ );
+
+ 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);
+
+ 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_fZGround = coors.x; // WTF ?
+ particle->m_fExpansionRate = coors.y; // WTF ?
+ }
+ else if ( psystem->Flags & SPEED_TRAIL )
+ {
+ CVector vecPrevPos = particle->m_vecPosition - particle->m_vecVelocity;
+ float fRotation;
+ float fTrailLength;
+ CVector vecScreenPosition;
+
+ if ( CSprite::CalcScreenCoors(vecPrevPos, &vecScreenPosition, &fTrailLength, &fRotation, true) )
+ {
+ CVector2D vecDist
+ (
+ coors.x - vecScreenPosition.x,
+ coors.y - 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);
+ }
+ 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 = Abs(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
+ {
+ 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);
+
+ POP_RENDERGROUP();
+}
+
+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.02f, 0.02f),
+ CGeneral::GetRandomNumberInRange(-0.02f, 0.02f),
+ CGeneral::GetRandomNumberInRange(-0.02f, 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);
+ }
+}
+
+void CParticle::CalWindDir(CVector *vecDirIn, CVector *vecDirOut)
+{
+ vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y);
+
+ vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y) * CWeather::Wind;
+ vecDirOut->y = (Sin(128) * vecDirIn->x) - (Cos(128) * vecDirIn->y) * CWeather::Wind;
+}
+
+void CParticle::HandleShipsAtHorizonStuff()
+{
+ ;
+}
+
+void CParticle::HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos)
+{
+ float fHeadingRad = entity->GetForward().Heading();
+ float fHeading = RADTODEG(fHeadingRad);
+ float fBirdAngle = ::Cos(DEGTORAD(1.5f));
+
+ tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[PARTICLE_BIRD_FRONT];
+ CParticle *particle = psystem->m_pParticles;
+ CParticle *prevParticle = nil;
+ bool bRemoveParticle;
+
+ for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) )
+ {
+ bRemoveParticle = false;
+
+ CVector2D vecPos(particle->m_vecPosition.x, particle->m_vecPosition.y);
+ CVector2D vecCamPos(camPos.x, camPos.y);
+
+ CVector2D vecDist = vecPos - vecCamPos;
+ vecDist.Normalise();
+
+ float fHead = DEGTORAD(fHeading);
+
+ CVector2D vecDir(-::Sin(fHead), ::Cos(fHead));
+ vecDir.Normalise();
+
+ float fDot = DotProduct2D(vecDir, vecDist);
+
+ if ( fDot > 0.0f && fDot > fBirdAngle )
+ {
+ if ( (camPos - particle->m_vecPosition).MagnitudeSqr() < 40000.0f )
+ {
+ CStats::SeagullsKilled++;
+
+ bRemoveParticle = true;
+
+ for ( int32 i = 0; i < 8; i++ )
+ {
+ CParticle *pBirdDerbis = AddParticle(PARTICLE_BIRD_DEBRIS,
+ particle->m_vecPosition,
+ CVector
+ (
+ CGeneral::GetRandomNumberInRange(-3.0f, 3.0f),
+ CGeneral::GetRandomNumberInRange(-3.0f, 3.0f),
+ CGeneral::GetRandomNumberInRange(-3.0f, 3.0f)
+ ),
+ nil,
+ 0.3f,
+ particle->m_Color,
+ CGeneral::GetRandomNumberInRange(20, 40),
+ 0,
+ CGeneral::GetRandomNumber() & 3,
+ 200);
+ if ( pBirdDerbis )
+ pBirdDerbis->m_nAlpha = particle->m_nAlpha;
+ }
+ }
+ }
+ }
+
+}
+
+void
+CEntity::AddSteamsFromGround(CVector *unused)
+{
+ int i, n;
+ C2dEffect *effect;
+ CVector pos;
+
+ n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects();
+ for(i = 0; i < n; i++){
+ effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i);
+ if(effect->type != EFFECT_PARTICLE)
+ continue;
+
+ pos = GetMatrix() * effect->pos;
+ switch(effect->particle.particleType){
+ case 0:
+ CParticleObject::AddObject(POBJECT_PAVEMENT_STEAM, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ case 1:
+ CParticleObject::AddObject(POBJECT_WALL_STEAM, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ case 2:
+ CParticleObject::AddObject(POBJECT_DRY_ICE, pos, effect->particle.scale, false);
+ break;
+ case 3:
+ CParticleObject::AddObject(POBJECT_SMALL_FIRE, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ case 4:
+ CParticleObject::AddObject(POBJECT_DARK_SMOKE, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ case 5:
+ CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_VERT, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ case 6:
+ CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_HORIZ, pos, effect->particle.dir, effect->particle.scale, false);
+ break;
+ }
+ }
+}