#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 "MBlur.h" #include "main.h" #include "AudioScriptObject.h" #include "ParticleObject.h" #include "Particle.h" #include "soundlist.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 *gpDotTex; RwTexture *gpHeatHazeTex; RwTexture *gpBeastieTex; RwTexture *gpRainDripTex[MAX_RAINDRIP_FILES]; RwTexture *gpRainDripDarkTex[MAX_RAINDRIP_FILES]; RwRaster *gpSparkRaster; RwRaster *gpNewspaperRaster; RwRaster *gpGunSmokeRaster; RwRaster *gpDotRaster; RwRaster *gpHeatHazeRaster; RwRaster *gpBeastieRaster; RwRaster *gpRainDripRaster[MAX_RAINDRIP_FILES]; RwRaster *gpRainDripDarkRaster[MAX_RAINDRIP_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 PARTICLE_WIND_TEST_SCALE = 0.002f; float fParticleScaleLimit = 0.5f; bool clearWaterDrop; int32 numWaterDropOnScreen; #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]); } gpSmoke2Tex = RwTextureRead("smokeII_3", nil); gpSmoke2Raster = RwTextureGetRaster(gpSmoke2Tex); 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], nil); gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]); } gpRainDropTex = RwTextureRead("raindrop4", nil); gpRainDropRaster = RwTextureGetRaster(gpRainDropTex); 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], nil); gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]); } for ( int32 i = 0; i < MAX_BOAT_FILES; i++ ) { gpBoatTex[i] = RwTextureRead(BoatFiles[i], nil); gpBoatRaster[i] = RwTextureGetRaster(gpBoatTex[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]); } gpBoatWakeTex = RwTextureRead("boatwake2", nil); gpBoatWakeRaster = RwTextureGetRaster(gpBoatWakeTex); gpFlame1Tex = RwTextureRead("flame1", nil); gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex); gpFlame5Tex = RwTextureRead("flame5", 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); gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex); gpBloodTex = RwTextureRead("blood", nil); gpBloodRaster = RwTextureGetRaster(gpBloodTex); gpLeafTex[0] = RwTextureRead("gameleaf01_64", nil); gpLeafRaster[0] = RwTextureGetRaster(gpLeafTex[0]); gpLeafTex[1] = RwTextureRead("letter", nil); gpLeafRaster[1] = RwTextureGetRaster(gpLeafTex[1]); 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); gpPointlightTex = RwTextureRead("pointlight", nil); gpPointlightRaster = RwTextureGetRaster(gpPointlightTex); gpSparkTex = RwTextureRead("spark", nil); gpSparkRaster = RwTextureGetRaster(gpSparkTex); gpNewspaperTex = RwTextureRead("newspaper02_64", nil); gpNewspaperRaster = RwTextureGetRaster(gpNewspaperTex); gpGunSmokeTex = RwTextureRead("gunsmoke3", nil); gpGunSmokeRaster = RwTextureGetRaster(gpGunSmokeTex); gpDotTex = RwTextureRead("dot", nil); gpDotRaster = RwTextureGetRaster(gpDotTex); gpHeatHazeTex = RwTextureRead("heathaze", nil); gpHeatHazeRaster = RwTextureGetRaster(gpHeatHazeTex); gpBeastieTex = RwTextureRead("beastie", nil); gpBeastieRaster = RwTextureGetRaster(gpBeastieTex); gpRainDripTex[0] = RwTextureRead("raindrip64", nil); gpRainDripRaster[0] = RwTextureGetRaster(gpRainDripTex[0]); gpRainDripTex[1] = RwTextureRead("raindripb64", nil); gpRainDripRaster[1] = RwTextureGetRaster(gpRainDripTex[1]); gpRainDripDarkTex[0] = RwTextureRead("raindrip64_d", nil); gpRainDripDarkRaster[0] = RwTextureGetRaster(gpRainDripDarkTex[0]); gpRainDripDarkTex[1] = RwTextureRead("raindripb64_d", nil); gpRainDripDarkRaster[1] = RwTextureGetRaster(gpRainDripDarkTex[1]); CTxdStore::PopCurrentTxd(); for ( int32 i = 0; i < MAX_PARTICLES; i++ ) { tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i]; switch ( i ) { case PARTICLE_SPARK: case PARTICLE_SPARK_SMALL: case PARTICLE_RAINDROP_SMALL: case PARTICLE_HELI_ATTACK: entry->m_ppRaster = &gpRainDropSmallRaster; break; case PARTICLE_WATER_SPARK: entry->m_ppRaster = &gpSparkRaster; break; case PARTICLE_WHEEL_DIRT: case PARTICLE_SAND: case PARTICLE_STEAM2: case PARTICLE_STEAM_NY: case PARTICLE_STEAM_NY_SLOWMOTION: case PARTICLE_GROUND_STEAM: case PARTICLE_ENGINE_STEAM: case PARTICLE_PEDFOOT_DUST: case PARTICLE_CAR_DUST: case PARTICLE_EXHAUST_FUMES: entry->m_ppRaster = &gpSmoke2Raster; break; case PARTICLE_WHEEL_WATER: case PARTICLE_WATER: case PARTICLE_SMOKE: case PARTICLE_SMOKE_SLOWMOTION: case PARTICLE_DRY_ICE: case PARTICLE_GARAGEPAINT_SPRAY: case PARTICLE_STEAM: 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_BLOOD: entry->m_ppRaster = &gpBloodRaster; break; case PARTICLE_BLOOD_SMALL: case PARTICLE_BLOOD_SPURT: entry->m_ppRaster = &gpBloodSmallRaster; break; case PARTICLE_DEBRIS: case PARTICLE_TREE_LEAVES: entry->m_ppRaster = gpLeafRaster; break; case PARTICLE_DEBRIS2: entry->m_ppRaster = &gpGungeRaster; break; case PARTICLE_FLYERS: entry->m_ppRaster = &gpNewspaperRaster; break; case PARTICLE_FLAME: case PARTICLE_CARFLAME: entry->m_ppRaster = &gpFlame1Raster; break; case PARTICLE_FIREBALL: entry->m_ppRaster = &gpFlame5Raster; break; case PARTICLE_GUNFLASH: case PARTICLE_GUNFLASH_NOANIM: entry->m_ppRaster = gpGunFlashRaster; break; case PARTICLE_GUNSMOKE: case PARTICLE_WATERDROP: case PARTICLE_BLOODDROP: case PARTICLE_HEATHAZE: case PARTICLE_HEATHAZE_IN_DIST: entry->m_ppRaster = nil; break; case PARTICLE_GUNSMOKE2: case PARTICLE_BOAT_THRUSTJET: case PARTICLE_RUBBER_SMOKE: entry->m_ppRaster = gpRubberRaster; break; case PARTICLE_CIGARETTE_SMOKE: entry->m_ppRaster = &gpGunSmokeRaster; break; case PARTICLE_TEARGAS: entry->m_ppRaster = &gpHeatHazeRaster; break; case PARTICLE_SHARD: case PARTICLE_RAINDROP: case PARTICLE_RAINDROP_2D: entry->m_ppRaster = &gpRainDropRaster; break; case PARTICLE_SPLASH: case PARTICLE_PED_SPLASH: case PARTICLE_CAR_SPLASH: case PARTICLE_WATER_HYDRANT: entry->m_ppRaster = gpCarSplashRaster; 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_EXPLOSION_MEDIUM: case PARTICLE_EXPLOSION_LARGE: case PARTICLE_EXPLOSION_MFAST: case PARTICLE_EXPLOSION_LFAST: entry->m_ppRaster = gpExplosionMediumRaster; break; case PARTICLE_BOAT_SPLASH: entry->m_ppRaster = &gpBoatWakeRaster; break; case PARTICLE_ENGINE_SMOKE: case PARTICLE_ENGINE_SMOKE2: case PARTICLE_CARFLAME_SMOKE: case PARTICLE_FIREBALL_SMOKE: case PARTICLE_ROCKET_SMOKE: case PARTICLE_TEST: entry->m_ppRaster = &gpCloudRaster4; break; case PARTICLE_CARCOLLISION_DUST: case PARTICLE_BURNINGRUBBER_SMOKE: entry->m_ppRaster = &gpCollisionSmokeRaster; break; case PARTICLE_CAR_DEBRIS: case PARTICLE_HELI_DEBRIS: case PARTICLE_BIRD_DEBRIS: entry->m_ppRaster = gpCarDebrisRaster; break; case PARTICLE_GUNSHELL_FIRST: case PARTICLE_GUNSHELL: case PARTICLE_GUNSHELL_BUMP1: case PARTICLE_GUNSHELL_BUMP2: entry->m_ppRaster = &gpGunShellRaster; 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; } } 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; 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(gpDotTex); gpDotTex = nil; RwTextureDestroy(gpHeatHazeTex); gpHeatHazeTex = nil; RwTextureDestroy(gpBeastieTex); gpBeastieTex = 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() ) return nil; 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; } 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; 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_nZRotationTimer = 0; pParticle->m_nZRadiusTimer = 0; pParticle->m_nAnimationSpeedTimer = 0; pParticle->m_fZGround = 0.0f; if ( type != PARTICLE_HEATHAZE ) pParticle->m_vecPosition = vecPos; else { CVector screen; float w, h; if ( !CSprite::CalcScreenCoors(vecPos, &screen, &w, &h, true) ) return nil; pParticle->m_vecPosition = screen; psystem->m_vecTextureStretch.x = w; psystem->m_vecTextureStretch.y = h; } 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; 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 -= 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(); // 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); } 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 ( numWaterDropOnScreen == 0 ) clearWaterDrop = false; if ( psystem->m_Type == PARTICLE_WATERDROP ) { if ( CGame::IsInInterior() || clearWaterDrop == true ) { bRemoveParticle = true; continue; } static uint8 nWaterDropCount; if ( nWaterDropCount == 5 ) { vecMoveStep = CVector(0.0f, 0.0f, 0.0f); particle->m_nTimeWhenWillBeDestroyed += 1250; nWaterDropCount = 0; } else { if ( TheCamera.m_CameraAverageSpeed > 0.35f ) { if ( vecMoveStep.Magnitude() > 0.5f ) { if ( vecMoveStep.Magnitude() > 0.4f && vecMoveStep.Magnitude() < 0.8f ) { vecMoveStep.x += TheCamera.m_CameraAverageSpeed * 1.5f; vecMoveStep.y += TheCamera.m_CameraAverageSpeed * 1.5f; } else if ( vecMoveStep.Magnitude() != 0.0f ) { vecMoveStep.x += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); vecMoveStep.y += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); } } } nWaterDropCount++; } if ( vecPos.z <= 1.5f ) vecMoveStep.z = 0.0f; } if ( psystem->m_Type == PARTICLE_HEATHAZE || psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) { #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 vecMoveStep.x = Sin(nSinCosIndex); vecMoveStep.y = Sin(nSinCosIndex); if ( psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) particle->m_nRotation = int16((float)particle->m_nRotation + 0.75f); else particle->m_nRotation = int16((float)particle->m_nRotation + 1.0f); } 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 ( psystem->m_Type == PARTICLE_FIREBALL ) { AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, particle->m_fSize * 5.0f); } if ( psystem->m_Type == PARTICLE_GUNSMOKE2 ) { if ( CTimer::GetFrameCounter() & 10 ) { #ifdef FIX_BUGS if ( FindPlayerPed() && FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) #else if ( FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) #endif { AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f)); } } } 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 ) { if ( vecPos.z + particle->m_fSize < 5.0f ) { 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 ( numWaterDropOnScreen < nMaxDrops && numWaterDropOnScreen < 63 && fDistToCam < 10.0f && clearWaterDrop == false && !CGame::IsInInterior() ) { CVector vecWaterdropTarget ( CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(0.1f, 0.75f), -0.01f ); CVector vecWaterdropPos; if ( TheCamera.m_CameraAverageSpeed < 0.35f ) vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(50, int32(SCREEN_WIDTH) - 50); else vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(200, int32(SCREEN_WIDTH) - 200); if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT / 2, SCREEN_HEIGHT); else { if ( TheCamera.m_CameraAverageSpeed < 0.35f ) vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(0, int32(SCREEN_HEIGHT)); else vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(150, int32(SCREEN_HEIGHT) - 200); } vecWaterdropPos.z = 2.0f; if ( AddParticle(PARTICLE_WATERDROP, vecWaterdropPos, vecWaterdropTarget, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f), 0, 0, CGeneral::GetRandomNumber() & 1, 0) != nil ) { numWaterDropOnScreen++; } } } 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 ( psystem->m_Type == PARTICLE_WATERDROP ) size = (size - Abs(vecMoveStep.x) * 0.000150000007f) + (Abs(vecMoveStep.z) * 0.0500000007f); //TODO: 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_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 ) #ifdef FIX_BUGS particle->m_nRotation = CGeneral::LimitAngle(particle->m_nRotation + particle->m_nRotationStep); #else particle->m_nRotation += particle->m_nRotationStep; #endif if ( particle->m_fCurrentZRadius != 0.0f ) { int32 nSinCosIndex = particle->m_nCurrentZRotation % SIN_COS_TABLE_SIZE; float fX = (Cos(nSinCosIndex) - Sin(nSinCosIndex)) * particle->m_fCurrentZRadius; float fY = (Sin(nSinCosIndex) + Cos(nSinCosIndex)) * particle->m_fCurrentZRadius; vecPos -= particle->m_vecParticleMovementOffset; vecPos += CVector(fX, fY, 0.0f); particle->m_vecParticleMovementOffset = CVector(fX, fY, 0.0f); } 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; 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; 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 = (particle->m_vecPosition.z - CDraw::GetNearClipZ()) * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) * CDraw::GetFarClipZ() / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * particle->m_vecPosition.z ) + CSprite::GetNearScreenZ(); 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 ( i == PARTICLE_WATERDROP ) { int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; stretchTexH += (1.0f - (float)timeLeft ) * psystem->m_vecTextureStretch.y; RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); FxType fxtype; if ( particle->m_nCurrentFrame != 0 ) fxtype = FXTYPE_WATER2; else fxtype = FXTYPE_WATER1; CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); canDraw = false; } if ( i == PARTICLE_BLOODDROP ) { int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; stretchTexH += (1.0f + (float)timeLeft) * psystem->m_vecTextureStretch.y; stretchTexW += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x; RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); FxType fxtype; if ( particle->m_nCurrentFrame ) fxtype = FXTYPE_BLOOD2; else fxtype = FXTYPE_BLOOD1; CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); canDraw = false; } if ( i == PARTICLE_HEATHAZE_IN_DIST ) { RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); canDraw = false; } if ( i == PARTICLE_HEATHAZE ) { RwRect rect; switch ( TheCamera.GetLookDirection() ) { case LOOKING_LEFT: rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 2.0f)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; case LOOKING_RIGHT: rect.x = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 4.0f)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; default: rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; } CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); canDraw = false; } 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 ) { 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 ( i == PARTICLE_WATER_HYDRANT || (!particleBanned || SCREEN_WIDTH * fParticleScaleLimit >= w) && SCREEN_HEIGHT * fParticleScaleLimit >= h ) { if ( i == PARTICLE_WATER_HYDRANT ) { RwRect rect; if ( w > 0.0f ) { rect.x = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); rect.w = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); } else { rect.w = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); rect.x = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); } if ( h > 0.0f ) { rect.y = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); rect.h = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); } else { rect.h = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); rect.y = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); } float screenZ = (coors.z - CDraw::GetNearClipZ()) * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) * CDraw::GetFarClipZ() / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * coors.z ) + CSprite::GetNearScreenZ(); CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_SPLASH1); } else { 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 = 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 )*/ 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 ( pPSystemData->m_Type == PARTICLE_WATERDROP ) --numWaterDropOnScreen; 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() { tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[PARTICLE_SHIP_SIDE]; for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = particle->m_pNext ) { if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 32000 && CTimer::GetTimeInMilliseconds() < particle->m_nTimeWhenWillBeDestroyed - 22000 ) { particle->m_nAlpha = Min(particle->m_nAlpha + 1, 96); } if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 10000 ) particle->m_nFadeAlphaTimer = 1; } } 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; } } }