summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ParticleMgr.cpp251
-rw-r--r--src/ParticleMgr.h205
-rw-r--r--src/ParticleObject.cpp13
-rw-r--r--src/ParticleObject.h2
-rw-r--r--src/Timer.h1
-rw-r--r--src/audio/AudioScriptObject.cpp7
-rw-r--r--src/audio/AudioScriptObject.h131
-rw-r--r--src/common.h67
-rw-r--r--src/math/Vector.cpp78
-rw-r--r--src/math/Vector.h165
-rw-r--r--src/render/Particle.cpp1866
-rw-r--r--src/render/Particle.h166
-rw-r--r--src/render/Shadows.cpp7
-rw-r--r--src/render/Shadows.h9
14 files changed, 2847 insertions, 121 deletions
diff --git a/src/ParticleMgr.cpp b/src/ParticleMgr.cpp
new file mode 100644
index 00000000..e4b3c7e5
--- /dev/null
+++ b/src/ParticleMgr.cpp
@@ -0,0 +1,251 @@
+#include "common.h"
+#include "patcher.h"
+#include "FileMgr.h"
+#include "ParticleMgr.h"
+
+UInt8 work_buff[55000];
+
+cParticleSystemMgr mod_ParticleSystemManager;
+
+const char *ParticleFilename = "PARTICLE.CFG";
+
+//cParticleSystemMgr::cParticleSystemMgr()
+void cParticleSystemMgr::ctor()
+{
+ memset(this, 0, sizeof(*this));
+}
+
+void cParticleSystemMgr::Initialise()
+{
+ LoadParticleData();
+
+ for ( Int32 i = 0; i < MAX_PARTICLES; i++ )
+ m_aParticles[i].m_pParticles = NULL;
+}
+
+void cParticleSystemMgr::LoadParticleData()
+{
+ CFileMgr::SetDir("DATA");
+ CFileMgr::LoadFile(ParticleFilename, work_buff, ARRAY_SIZE(work_buff), "r");
+ CFileMgr::SetDir("");
+
+ tParticleSystemData *entry = NULL;
+ Int32 type = PARTICLE_FIRST;
+
+ Char *lineStart = (Char *)work_buff;
+ Char *lineEnd = lineStart + 1;
+
+ Char line[500];
+ Char delims[4];
+
+ while ( true )
+ {
+ ASSERT(lineStart != NULL);
+ ASSERT(lineEnd != NULL);
+
+ while ( *lineEnd != '\n' )
+ ++lineEnd;
+
+ Int32 lineLength = lineEnd - lineStart;
+
+ ASSERT(lineLength < 500);
+
+ strncpy(line, lineStart, lineLength);
+
+ line[lineLength] = '\0';
+
+ if ( !strcmp(line, ";the end") )
+ break;
+
+ if ( *line != ';' )
+ {
+ Int32 param = CFG_PARAM_FIRST;
+
+ strcpy(delims, " \t");
+
+ Char *value = strtok(line, delims);
+
+ ASSERT(value != NULL);
+
+ do
+ {
+ switch ( param )
+ {
+ case CFG_PARAM_PARTICLE_TYPE_NAME:
+ ASSERT(type < MAX_PARTICLES);
+ entry = &m_aParticles[type];
+ ASSERT(entry != NULL);
+ entry->m_Type = (tParticleType)type++;
+ strcpy(entry->m_aName, value);
+ break;
+
+ case CFG_PARAM_RENDER_COLOURING_R:
+ entry->m_RenderColouring.red = atoi(value);
+ break;
+
+ case CFG_PARAM_RENDER_COLOURING_G:
+ entry->m_RenderColouring.green = atoi(value);
+ break;
+
+ case CFG_PARAM_RENDER_COLOURING_B:
+ entry->m_RenderColouring.blue = atoi(value);
+ break;
+
+ case CFG_PARAM_INITIAL_COLOR_VARIATION:
+ entry->m_InitialColorVariation = min(atoi(value), 100);
+ break;
+
+ case CFG_PARAM_FADE_DESTINATION_COLOR_R:
+ entry->m_FadeDestinationColor.red = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_DESTINATION_COLOR_G:
+ entry->m_FadeDestinationColor.green = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_DESTINATION_COLOR_B:
+ entry->m_FadeDestinationColor.blue = atoi(value);
+ break;
+
+ case CFG_PARAM_COLOR_FADE_TIME:
+ entry->m_ColorFadeTime = atoi(value);
+ break;
+
+ case CFG_PARAM_DEFAULT_INITIAL_RADIUS:
+ entry->m_fDefaultInitialRadius = atof(value);
+ break;
+
+ case CFG_PARAM_EXPANSION_RATE:
+ entry->m_fExpansionRate = atof(value);
+ break;
+
+ case CFG_PARAM_INITIAL_INTENSITY:
+ entry->m_nFadeToBlackInitialIntensity = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_TIME:
+ entry->m_nFadeToBlackTime = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_AMOUNT:
+ entry->m_nFadeToBlackAmount = atoi(value);
+ break;
+
+ case CFG_PARAM_INITIAL_ALPHA_INTENSITY:
+ entry->m_nFadeAlphaInitialIntensity = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_ALPHA_TIME:
+ entry->m_nFadeAlphaTime = atoi(value);
+ break;
+
+ case CFG_PARAM_FADE_ALPHA_AMOUNT:
+ entry->m_nFadeAlphaAmount = atoi(value);
+ break;
+
+ case CFG_PARAM_INITIAL_ANGLE:
+ entry->m_nZRotationInitialAngle = atoi(value);
+ break;
+
+ case CFG_PARAM_CHANGE_TIME:
+ entry->m_nZRotationChangeTime = atoi(value);
+ break;
+
+ case CFG_PARAM_ANGLE_CHANGE_AMOUNT:
+ entry->m_nZRotationAngleChangeAmount = atoi(value);
+ break;
+
+ case CFG_PARAM_INITIAL_Z_RADIUS:
+ entry->m_fInitialZRadius = atof(value);
+ break;
+
+ case CFG_PARAM_Z_RADIUS_CHANGE_TIME:
+ entry->m_nZRadiusChangeTime = atoi(value);
+ break;
+
+ case CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT:
+ entry->m_fZRadiusChangeAmount = atof(value);
+ break;
+
+ case CFG_PARAM_ANIMATION_SPEED:
+ entry->m_nAnimationSpeed = atoi(value);
+ break;
+
+ case CFG_PARAM_START_ANIMATION_FRAME:
+ entry->m_nStartAnimationFrame = atoi(value);
+ break;
+
+ case CFG_PARAM_FINAL_ANIMATION_FRAME:
+ entry->m_nFinalAnimationFrame = atoi(value);
+ break;
+
+ case CFG_PARAM_ROTATION_SPEED:
+ entry->m_nRotationSpeed = atoi(value);
+ break;
+
+ case CFG_PARAM_GRAVITATIONAL_ACCELERATION:
+ entry->m_fGravitationalAcceleration = atof(value);
+ break;
+
+ case CFG_PARAM_FRICTION_DECCELERATION:
+ entry->m_nFrictionDecceleration = atoi(value);
+ break;
+
+ case CFG_PARAM_LIFE_SPAN:
+ entry->m_nLifeSpan = atoi(value);
+ break;
+
+ case CFG_PARAM_POSITION_RANDOM_ERROR:
+ entry->m_fPositionRandomError = atof(value);
+ break;
+
+ case CFG_PARAM_VELOCITY_RANDOM_ERROR:
+ entry->m_fVelocityRandomError = atof(value);
+ break;
+
+ case CFG_PARAM_EXPANSION_RATE_ERROR:
+ entry->m_fExpansionRateError = atof(value);
+ break;
+
+ case CFG_PARAM_ROTATION_RATE_ERROR:
+ entry->m_nRotationRateError = atoi(value);
+ break;
+
+ case CFG_PARAM_LIFE_SPAN_ERROR_SHAPE:
+ entry->m_nLifeSpanErrorShape = atoi(value);
+ break;
+
+ case CFG_PARAM_TRAIL_LENGTH_MULTIPLIER:
+ entry->m_fTrailLengthMultiplier = atof(value);
+ break;
+
+ case CFG_PARAM_PARTICLE_CREATE_RANGE:
+ entry->m_fCreateRange = SQR(atof(value));
+ break;
+
+ case CFG_PARAM_FLAGS:
+ entry->Flags = atoi(value);
+ break;
+ }
+
+ value = strtok(NULL, delims);
+
+ param++;
+
+ if ( param > CFG_PARAM_LAST )
+ param = CFG_PARAM_FIRST;
+
+ } while ( value != NULL );
+ }
+
+ lineEnd++;
+ lineStart = lineEnd;
+ lineEnd++;
+ }
+}
+
+STARTPATCHES
+ InjectHook(0x50FCB0, &cParticleSystemMgr::ctor, PATCH_JUMP);
+ InjectHook(0x50FCD0, &cParticleSystemMgr::Initialise, PATCH_JUMP);
+ InjectHook(0x50FDF0, &cParticleSystemMgr::LoadParticleData, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/ParticleMgr.h b/src/ParticleMgr.h
new file mode 100644
index 00000000..e650255b
--- /dev/null
+++ b/src/ParticleMgr.h
@@ -0,0 +1,205 @@
+#pragma once
+
+class CParticle;
+
+enum tParticleType
+{
+ PARTICLE_SPARK = 0,
+ PARTICLE_SPARK_SMALL,
+ PARTICLE_WHEEL_DIRT,
+ PARTICLE_WHEEL_WATER,
+ PARTICLE_BLOOD,
+ PARTICLE_BLOOD_SMALL,
+ PARTICLE_BLOOD_SPURT,
+ PARTICLE_DEBRIS,
+ PARTICLE_DEBRIS2,
+ PARTICLE_WATER,
+ PARTICLE_FLAME,
+ PARTICLE_FIREBALL,
+ PARTICLE_GUNFLASH,
+ PARTICLE_GUNFLASH_NOANIM,
+ PARTICLE_GUNSMOKE,
+ PARTICLE_GUNSMOKE2,
+ PARTICLE_SMOKE,
+ PARTICLE_SMOKE_SLOWMOTION,
+ PARTICLE_GARAGEPAINT_SPRAY,
+ PARTICLE_SHARD,
+ PARTICLE_SPLASH,
+ PARTICLE_CARFLAME,
+ PARTICLE_STEAM,
+ PARTICLE_STEAM2,
+ PARTICLE_STEAM_NY,
+ PARTICLE_STEAM_NY_SLOWMOTION,
+ PARTICLE_ENGINE_STEAM,
+ PARTICLE_RAINDROP,
+ PARTICLE_RAINDROP_SMALL,
+ PARTICLE_RAIN_SPLASH,
+ PARTICLE_RAIN_SPLASH_BIGGROW,
+ PARTICLE_RAIN_SPLASHUP,
+ PARTICLE_WATERSPRAY,
+ PARTICLE_EXPLOSION_MEDIUM,
+ PARTICLE_EXPLOSION_LARGE,
+ PARTICLE_EXPLOSION_MFAST,
+ PARTICLE_EXPLOSION_LFAST,
+ PARTICLE_CAR_SPLASH,
+ PARTICLE_BOAT_SPLASH,
+ PARTICLE_BOAT_THRUSTJET,
+ PARTICLE_BOAT_WAKE,
+ PARTICLE_WATER_HYDRANT,
+ PARTICLE_WATER_CANNON,
+ PARTICLE_EXTINGUISH_STEAM,
+ PARTICLE_PED_SPLASH,
+ PARTICLE_PEDFOOT_DUST,
+ PARTICLE_HELI_DUST,
+ PARTICLE_HELI_ATTACK,
+ PARTICLE_ENGINE_SMOKE,
+ PARTICLE_ENGINE_SMOKE2,
+ PARTICLE_CARFLAME_SMOKE,
+ PARTICLE_FIREBALL_SMOKE,
+ PARTICLE_PAINT_SMOKE,
+ PARTICLE_TREE_LEAVES,
+ PARTICLE_CARCOLLISION_DUST,
+ PARTICLE_CAR_DEBRIS,
+ PARTICLE_HELI_DEBRIS,
+ PARTICLE_EXHAUST_FUMES,
+ PARTICLE_RUBBER_SMOKE,
+ PARTICLE_BURNINGRUBBER_SMOKE,
+ PARTICLE_BULLETHIT_SMOKE,
+ PARTICLE_GUNSHELL_FIRST,
+ PARTICLE_GUNSHELL,
+ PARTICLE_GUNSHELL_BUMP1,
+ PARTICLE_GUNSHELL_BUMP2,
+ PARTICLE_TEST,
+ PARTICLE_BIRD_FRONT,
+ PARTICLE_RAINDROP_2D,
+
+ MAX_PARTICLES,
+ PARTICLE_FIRST = PARTICLE_SPARK,
+ PARTICLE_LAST = PARTICLE_RAINDROP_2D
+};
+
+enum
+{
+ ZCHECK_FIRST = BIT(0),
+ ZCHECK_STEP = BIT(1),
+ DRAW_OPAQUE = BIT(2),
+ SCREEN_TRAIL = BIT(3),
+ SPEED_TRAIL = BIT(4),
+ RAND_VERT_V = BIT(5),
+ CYCLE_ANIM = BIT(6),
+ DRAW_DARK = BIT(7),
+ VERT_TRAIL = BIT(8),
+ _FLAG9 = BIT(9), // unused
+ DRAWTOP2D = BIT(10),
+ CLIPOUT2D = BIT(11),
+ ZCHECK_BUMP = BIT(12),
+ ZCHECK_BUMP_FIRST = BIT(13)
+};
+
+#pragma pack(push, 1)
+struct tParticleSystemData
+{
+ tParticleType m_Type;
+ Char m_aName[20];
+ Float m_fCreateRange;
+ Float m_fDefaultInitialRadius;
+ Float m_fExpansionRate;
+ UInt16 m_nZRotationInitialAngle;
+ Int16 m_nZRotationAngleChangeAmount;
+ UInt16 m_nZRotationChangeTime;
+ UInt16 m_nZRadiusChangeTime;
+ Float m_fInitialZRadius;
+ Float m_fZRadiusChangeAmount;
+ UInt16 m_nFadeToBlackTime;
+ Int16 m_nFadeToBlackAmount;
+ UInt8 m_nFadeToBlackInitialIntensity;
+ UInt8 m_nFadeAlphaInitialIntensity;
+ UInt16 m_nFadeAlphaTime;
+ Int16 m_nFadeAlphaAmount;
+ UInt16 m_nStartAnimationFrame;
+ UInt16 m_nFinalAnimationFrame;
+ UInt16 m_nAnimationSpeed;
+ UInt16 m_nRotationSpeed;
+ char _pad1[2];
+ Float m_fGravitationalAcceleration;
+ Int32 m_nFrictionDecceleration;
+ Int32 m_nLifeSpan;
+ Float m_fPositionRandomError;
+ Float m_fVelocityRandomError;
+ Float m_fExpansionRateError;
+ Int32 m_nRotationRateError;
+ UInt32 m_nLifeSpanErrorShape;
+ Float m_fTrailLengthMultiplier;
+ UInt32 Flags;
+ RwRGBA m_RenderColouring;
+ UInt8 m_InitialColorVariation;
+ RwRGBA m_FadeDestinationColor;
+ char _pad2[3];
+ UInt32 m_ColorFadeTime;
+
+ RwRaster **m_ppRaster;
+ CParticle *m_pParticles;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+class cParticleSystemMgr
+{
+ enum
+ {
+ CFG_PARAM_PARTICLE_TYPE_NAME = 0,
+ CFG_PARAM_RENDER_COLOURING_R,
+ CFG_PARAM_RENDER_COLOURING_G,
+ CFG_PARAM_RENDER_COLOURING_B,
+ CFG_PARAM_INITIAL_COLOR_VARIATION,
+ CFG_PARAM_FADE_DESTINATION_COLOR_R,
+ CFG_PARAM_FADE_DESTINATION_COLOR_G,
+ CFG_PARAM_FADE_DESTINATION_COLOR_B,
+ CFG_PARAM_COLOR_FADE_TIME,
+ CFG_PARAM_DEFAULT_INITIAL_RADIUS,
+ CFG_PARAM_EXPANSION_RATE,
+ CFG_PARAM_INITIAL_INTENSITY,
+ CFG_PARAM_FADE_TIME,
+ CFG_PARAM_FADE_AMOUNT,
+ CFG_PARAM_INITIAL_ALPHA_INTENSITY,
+ CFG_PARAM_FADE_ALPHA_TIME,
+ CFG_PARAM_FADE_ALPHA_AMOUNT,
+ CFG_PARAM_INITIAL_ANGLE,
+ CFG_PARAM_CHANGE_TIME,
+ CFG_PARAM_ANGLE_CHANGE_AMOUNT,
+ CFG_PARAM_INITIAL_Z_RADIUS,
+ CFG_PARAM_Z_RADIUS_CHANGE_TIME,
+ CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT,
+ CFG_PARAM_ANIMATION_SPEED,
+ CFG_PARAM_START_ANIMATION_FRAME,
+ CFG_PARAM_FINAL_ANIMATION_FRAME,
+ CFG_PARAM_ROTATION_SPEED,
+ CFG_PARAM_GRAVITATIONAL_ACCELERATION,
+ CFG_PARAM_FRICTION_DECCELERATION,
+ CFG_PARAM_LIFE_SPAN,
+ CFG_PARAM_POSITION_RANDOM_ERROR,
+ CFG_PARAM_VELOCITY_RANDOM_ERROR,
+ CFG_PARAM_EXPANSION_RATE_ERROR,
+ CFG_PARAM_ROTATION_RATE_ERROR,
+ CFG_PARAM_LIFE_SPAN_ERROR_SHAPE,
+ CFG_PARAM_TRAIL_LENGTH_MULTIPLIER,
+ CFG_PARAM_PARTICLE_CREATE_RANGE,
+ CFG_PARAM_FLAGS,
+
+ MAX_CFG_PARAMS,
+ CFG_PARAM_FIRST = CFG_PARAM_PARTICLE_TYPE_NAME,
+ CFG_PARAM_LAST = CFG_PARAM_FLAGS
+ };
+
+public:
+ tParticleSystemData m_aParticles[MAX_PARTICLES];
+
+ cParticleSystemMgr() { ctor(); } void ctor();
+
+ void Initialise();
+ void LoadParticleData();
+ //void RangeCheck(tParticleSystemData *pData);
+};
+#pragma pack(pop)
+
+extern cParticleSystemMgr mod_ParticleSystemManager; \ No newline at end of file
diff --git a/src/ParticleObject.cpp b/src/ParticleObject.cpp
index 5ed82995..bd5e6b0a 100644
--- a/src/ParticleObject.cpp
+++ b/src/ParticleObject.cpp
@@ -3,3 +3,16 @@
#include "ParticleObject.h"
WRAPPER void CParticleObject::AddObject(uint16, const CVector &pos, bool remove) { EAXJMP(0x4BC4D0); }
+
+
+// Converted from static void __cdecl CParticleObject::Initialise() 0x42C760
+void CParticleObject::Initialise()
+{
+ ((void (__cdecl *)())0x4BC440)();
+}
+
+// Converted from static void __cdecl CParticleObject::UpdateAll() 0x4BCA30
+void CParticleObject::UpdateAll()
+{
+ ((void (__cdecl *)())0x4BCA30)();
+} \ No newline at end of file
diff --git a/src/ParticleObject.h b/src/ParticleObject.h
index b2cfadb8..d6b48810 100644
--- a/src/ParticleObject.h
+++ b/src/ParticleObject.h
@@ -28,4 +28,6 @@ class CParticleObject
{
public:
static void AddObject(uint16, const CVector &pos, bool remove);
+ static void Initialise();
+ static void UpdateAll();
};
diff --git a/src/Timer.h b/src/Timer.h
index e92fe9e1..765cd050 100644
--- a/src/Timer.h
+++ b/src/Timer.h
@@ -2,6 +2,7 @@
class CTimer
{
+public: // remove when each variable will be encapsulated
static uint32 &m_snTimeInMilliseconds;
static uint32 &m_snTimeInMillisecondsPauseMode;
static uint32 &m_snTimeInMillisecondsNonClipped;
diff --git a/src/audio/AudioScriptObject.cpp b/src/audio/AudioScriptObject.cpp
new file mode 100644
index 00000000..7cb81f7a
--- /dev/null
+++ b/src/audio/AudioScriptObject.cpp
@@ -0,0 +1,7 @@
+#include "common.h"
+#include "AudioScriptObject.h"
+
+void PlayOneShotScriptObject(UInt8 id, CVector const &pos)
+{
+ ((void (__cdecl *)(UInt8, CVector const &))0x57C5F0)(id, pos);
+} \ No newline at end of file
diff --git a/src/audio/AudioScriptObject.h b/src/audio/AudioScriptObject.h
new file mode 100644
index 00000000..a30af679
--- /dev/null
+++ b/src/audio/AudioScriptObject.h
@@ -0,0 +1,131 @@
+#pragma once
+
+enum /*eSounds*/
+{
+ SOUND_TEST_1 = 0,
+ _SOUND_UNK_1 = 1,
+ _SOUND_UNK_2 = 2,
+ _SOUND_UNK_3 = 3,
+ _SOUND_CLUB_1_S = 4,
+ _SOUND_CLUB_1_L = 5,
+ _SOUND_CLUB_2_S = 6,
+ _SOUND_CLUB_2_L = 7,
+ _SOUND_CLUB_3_S = 8,
+ _SOUND_CLUB_3_L = 9,
+ _SOUND_CLUB_4_S = 10,
+ _SOUND_CLUB_4_L = 11,
+ _SOUND_CLUB_5_S = 12,
+ _SOUND_CLUB_5_L = 13,
+ _SOUND_CLUB_6_S = 14,
+ _SOUND_CLUB_6_L = 15,
+ _SOUND_CLUB_7_S = 16,
+ _SOUND_CLUB_7_L = 17,
+ _SOUND_CLUB_8_S = 18,
+ _SOUND_CLUB_8_L = 19,
+ _SOUND_CLUB_9_S = 20,
+ _SOUND_CLUB_9_L = 21,
+ _SOUND_CLUB_10_S = 22,
+ _SOUND_CLUB_10_L = 23,
+ _SOUND_CLUB_11_S = 24,
+ _SOUND_CLUB_11_L = 25,
+ _SOUND_CLUB_12_S = 26,
+ _SOUND_CLUB_12_L = 27,
+ _SOUND_CLUB_RAGGA_S = 28,
+ _SOUND_CLUB_RAGGA_L = 29,
+ SOUND_STRIP_CLUB_LOOP_1_S = 30,
+ _SOUND_STRIP_CLUB_LOOP_1_L = 31,
+ SOUND_STRIP_CLUB_LOOP_2_S = 32,
+ _SOUND_STRIP_CLUB_LOOP_2_L = 33,
+ _SOUND_SFX_WORKSHOP_1 = 34,
+ _SOUND_SFX_WORKSHOP_2 = 35,
+ _SOUND_SAWMILL_LOOP_S = 36,
+ SOUND_SAWMILL_LOOP_L = 37,
+ _SOUND_DOG_FOOD_FACTORY_S = 38,
+ _SOUND_DOG_FOOD_FACTORY_L = 39,
+ _SOUND_LAUNDERETTE_1 = 40,
+ _SOUND_LAUNDERETTE_2 = 41,
+ _SOUND_RESTAURANT_CHINATOWN_S = 42,
+ _SOUND_RESTAURANT_CHINATOWN_L = 43,
+ _SOUND_RESTAURANT_ITALY_S = 44,
+ _SOUND_RESTAURANT_ITALY_L = 45,
+ _SOUND_RESTAURANT_GENERIC_1_S = 46,
+ _SOUND_RESTAURANT_GENERIC_1_L = 47,
+ _SOUND_RESTAURANT_GENERIC_2_S = 48,
+ _SOUND_RESTAURANT_GENERIC_2_L = 49,
+ _SOUND_AIRPORT_ANNOUNCEMENT_S = 50,
+ _SOUND_AIRPORT_ANNOUNCEMENT_L = 51,
+ _SOUND_SHOP_LOOP_1 = 52,
+ _SOUND_SHOP_LOOP_2 = 53,
+ _SOUND_CINEMA_S = 54,
+ _SOUND_CINEMA_L = 55,
+ _SOUND_DOCKS_FOGHORN_S = 56,
+ _SOUND_DOCKS_FOGHORN_L = 57,
+ _SOUND_HOME_S = 58,
+ _SOUND_HOME_L = 59,
+ _SOUND_PIANO_BAR = 60,
+ _SOUND_CLUB = 61,
+ SOUND_PORN_CINEMA_1_S = 62,
+ _SOUND_PORN_CINEMA_1_L = 63,
+ SOUND_PORN_CINEMA_2_S = 64,
+ _SOUND_PORN_CINEMA_2_L = 65,
+ SOUND_PORN_CINEMA_3_S = 66,
+ _SOUND_PORN_CINEMA_3_L = 67,
+ _SOUND_BANK_ALARM_LOOP_S = 68,
+ SOUND_BANK_ALARM_LOOP_L = 69,
+ _SOUND_POLICE_BALL_LOOP_S = 70,
+ SOUND_POLICE_BALL_LOOP_L = 71,
+ _SOUND_RAVE_LOOP_INDUSTRIAL_S = 72,
+ SOUND_RAVE_LOOP_INDUSTRIAL_L = 73,
+ _SOUND_UNK_74 = 74,
+ _SOUND_UNK_75 = 75,
+ _SOUND_POLICE_CELL_BEATING_LOOP_S = 76,
+ SOUND_POLICE_CELL_BEATING_LOOP_L = 77,
+ SOUND_INJURED_PED_MALE_OUCH_S = 78,
+ SOUND_INJURED_PED_MALE_OUCH_L = 79,
+ SOUND_INJURED_PED_FEMALE_OUCH_S = 80,
+ SOUND_INJURED_PED_FEMALE_OUCH_L = 81,
+ SOUND_EVIDENCE_PICKUP = 82,
+ SOUND_UNLOAD_GOLD = 83,
+ _SOUND_RAVE_INDUSTRIAL_S = 84,
+ _SOUND_RAVE_INDUSTRIAL_L = 85,
+ _SOUND_RAVE_COMMERCIAL_S = 86,
+ _SOUND_RAVE_COMMERCIAL_L = 87,
+ _SOUND_RAVE_SUBURBAN_S = 88,
+ _SOUND_RAVE_SUBURBAN_L = 89,
+ _SOUND_GROAN_S = 90,
+ _SOUND_GROAN_L = 91,
+ SOUND_GATE_START_CLUNK = 92,
+ SOUND_GATE_STOP_CLUNK = 93,
+ SOUND_PART_MISSION_COMPLETE = 94,
+ SOUND_CHUNKY_RUN_SHOUT = 95,
+ SOUND_SECURITY_GUARD_RUN_AWAY_SHOUT = 96,
+ SOUND_RACE_START_1 = 97,
+ SOUND_RACE_START_2 = 98,
+ SOUND_RACE_START_3 = 99,
+ SOUND_RACE_START_GO = 100,
+ SOUND_SWAT_PED_SHOUT = 101,
+ SOUND_PRETEND_FIRE_LOOP = 102,
+ SOUND_AMMUNATION_CHAT_1 = 103,
+ SOUND_AMMUNATION_CHAT_2 = 104,
+ SOUND_AMMUNATION_CHAT_3 = 105,
+ _SOUND_BULLET_WALL_1 = 106,
+ _SOUND_BULLET_WALL_2 = 107,
+ _SOUND_BULLET_WALL_3 = 108,
+ _SOUND_UNK_109 = 109,
+ _SOUND_GLASSFX2_1 = 110,
+ _SOUND_GLASSFX2_2 = 111,
+ _SOUND_PHONE_RING = 112,
+ _SOUND_UNK_113 = 113,
+ _SOUND_GLASS_SMASH_1 = 114,
+ _SOUND_GLASS_SMASH_2 = 115,
+ _SOUND_GLASS_CRACK = 116,
+ _SOUND_GLASS_SHARD = 117,
+ _SOUND_WOODEN_BOX_SMASH = 118,
+ _SOUND_CARDBOARD_BOX_SMASH = 119,
+ _SOUND_COL_CAR = 120,
+ _SOUND_TYRE_BUMP = 121,
+ _SOUND_BULLET_SHELL_HIT_GROUND_1 = 122,
+ _SOUND_BULLET_SHELL_HIT_GROUND_2 = 123,
+};
+
+extern void PlayOneShotScriptObject(UInt8 id, CVector const &pos); \ No newline at end of file
diff --git a/src/common.h b/src/common.h
index 272e5017..a96030e4 100644
--- a/src/common.h
+++ b/src/common.h
@@ -25,6 +25,21 @@ typedef uint32_t uint32;
typedef int32_t int32;
typedef uintptr_t uintptr;
+typedef char Int8;
+typedef unsigned char UInt8;
+typedef signed char SInt8;
+typedef short Int16;
+typedef unsigned short UInt16;
+typedef signed short SInt16;
+typedef int Int32;
+typedef unsigned int UInt32;
+typedef signed int SInt32;
+typedef float Float;
+typedef double Double;
+typedef Int8 Bool; //typedef bool Bool;
+typedef char Char;
+
+
#define nil NULL
#include "config.h"
@@ -62,6 +77,15 @@ extern RsGlobalType &RsGlobal;
#define SCREENW (RsGlobal.maximumWidth)
#define SCREENH (RsGlobal.maximumHeight)
+#define DEFAULT_SCREEN_WIDTH (640)
+#define DEFAULT_SCREEN_HEIGHT (448)
+#define SCREEN_WIDTH Float(RsGlobal.width)
+#define SCREEN_HEIGHT Float(RsGlobal.height)
+#define SCREEN_STRETCH_X(a) Float( a * ( SCREEN_WIDTH / Float(DEFAULT_SCREEN_WIDTH) ) )
+#define SCREEN_STRETCH_Y(a) Float( a * ( SCREEN_HEIGHT / Float(DEFAULT_SCREEN_HEIGHT) ) )
+#define SCREEN_FROM_RIGHT(a) Float( SCREEN_WIDTH - SCREEN_STRETCH_X(a) )
+#define SCREEN_FROM_BOTTOM(a) Float( SCREEN_HEIGHT - SCREEN_STRETCH_Y(a) )
+
char *GetUserDirectory(void);
struct GlobalScene
@@ -79,22 +103,57 @@ extern GlobalScene &Scene;
class CRGBA
{
public:
- uint8 r, g, b, a;
+ union
+ {
+ uint32 color32;
+ struct { uint8 r, g, b, a; };
+ struct { uint8 red, green, blue, alpha; };
+#ifdef RWCORE_H
+ struct { RwRGBA rwRGBA; };
+#endif
+ };
+
CRGBA(void) { }
CRGBA(uint8 r, uint8 g, uint8 b, uint8 a) : r(r), g(g), b(b), a(a) { }
+#ifdef RWCORE_H
+ operator RwRGBA &(void)
+ {
+ return rwRGBA;
+ }
+
+ operator RwRGBA *(void)
+ {
+ return &rwRGBA;
+ }
+
+ operator RwRGBA (void) const
+ {
+ return rwRGBA;
+ }
+#endif
};
-inline float
-clamp(float v, float min, float max){ return v<min ? min : v>max ? max : v; }
+// inline float clamp(float v, float min, float max){ return v<min ? min : v>max ? max : v; }
+
inline float
sq(float x) { return x*x; }
+
+#define SQR(x) ( x * x )
+
#define PI M_PI
-#define DEGTORAD(d) (d/180.0f*PI)
+#define DEGTORAD(x) ((x) * PI / 180.0f)
+#define RADTODEG(x) ((x) * 180.0f / PI)
int myrand(void);
void mysrand(unsigned int seed);
#define debug printf
+#define ASSERT assert
+#define clamp(v, a, b) (max(min(v, b), a))
//#define min(a, b) ((a) < (b) ? (a) : (b))
//#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define PERCENT(x, p) ( ( Float(x) * ( Float(p) / 100.0f ) ) )
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#define BIT(num) (1<<(num)) \ No newline at end of file
diff --git a/src/math/Vector.cpp b/src/math/Vector.cpp
new file mode 100644
index 00000000..d9cc590f
--- /dev/null
+++ b/src/math/Vector.cpp
@@ -0,0 +1,78 @@
+#include "common.h"
+#include "Vector.h"
+
+void CVector::Normalise()
+{
+ float sq = MagnitudeSqr();
+ if(sq > 0.0f){
+ float invsqrt = 1.0f/sqrt(sq); // CMaths::RecipSqrt
+ x *= invsqrt;
+ y *= invsqrt;
+ z *= invsqrt;
+ }else
+ x = 1.0f;
+}
+
+// operator +
+CVector operator + (CVector const &refLeft, CVector const &refRight)
+{
+ return CVector(refLeft.x + refRight.x, refLeft.y + refRight.y, refLeft.z + refRight.z);
+}
+
+CVector operator + (CVector const &refLeft, float fRight)
+{
+ return CVector(refLeft.x + fRight, refLeft.y + fRight, refLeft.z + fRight);
+}
+
+CVector operator + (float fLeft, CVector const &refRight)
+{
+ return CVector(fLeft + refRight.x, fLeft + refRight.y, fLeft + refRight.z);
+}
+
+// operator -
+CVector operator - (CVector const &refLeft, CVector const &refRight)
+{
+ return CVector(refLeft.x - refRight.x, refLeft.y - refRight.y, refLeft.z - refRight.z);
+}
+
+CVector operator - (CVector const &refLeft, float fRight)
+{
+ return CVector(refLeft.x - fRight, refLeft.y - fRight, refLeft.z - fRight);
+}
+
+CVector operator - (float fLeft, CVector const &refRight)
+{
+ return CVector(fLeft - refRight.x, fLeft - refRight.y, fLeft - refRight.z);
+}
+
+// operator *
+CVector operator * (CVector const &refLeft, CVector const &refRight)
+{
+ return CVector(refLeft.x * refRight.x, refLeft.y * refRight.y, refLeft.z * refRight.z);
+}
+
+CVector operator * (CVector const &refLeft, float fRight)
+{
+ return CVector(refLeft.x * fRight, refLeft.y * fRight, refLeft.z * fRight);
+}
+
+CVector operator * (float fLeft, CVector const &refRight)
+{
+ return CVector(fLeft * refRight.x, fLeft * refRight.y, fLeft * refRight.z);
+}
+
+// operator /
+CVector operator / (CVector const &refLeft, CVector const &refRight)
+{
+ return CVector(refLeft.x / refRight.x, refLeft.y / refRight.y, refLeft.z / refRight.z);
+}
+
+CVector operator / (CVector const &refLeft, float fRight)
+{
+ return CVector(refLeft.x / fRight, refLeft.y / fRight, refLeft.z / fRight);
+}
+
+CVector operator / (float fLeft, CVector const &refRight)
+{
+ return CVector(fLeft / refRight.x, fLeft / refRight.y, fLeft / refRight.z);
+} \ No newline at end of file
diff --git a/src/math/Vector.h b/src/math/Vector.h
index fbc59832..e45906c8 100644
--- a/src/math/Vector.h
+++ b/src/math/Vector.h
@@ -6,67 +6,128 @@ public:
float x, y, z;
CVector(void) {}
CVector(float x, float y, float z) : x(x), y(y), z(z) {}
-// CVector(rw::V3d const &v) : x(v.x), y(v.y), z(v.z) {}
+// CVector(CVector &refVector) : x(refVector.x), y(refVector.y), z(refVector.z) { }
+// CVector(const CVector &refVector) : x(refVector.x), y(refVector.y), z(refVector.z) {}
+// CVector(CVector2D &refVector, float _z = 0.0f) : x(refVector.x), y(refVector.y), z(_z) {}
+#ifdef RWCORE_H
+ CVector(RwV3d const &v) : x(v.x), y(v.y), z(v.z) {}
+
+ operator RwV3d (void) const {
+ RwV3d vecRw = { this->x, this->y, this->z };
+ return vecRw;
+ }
+
+ operator RwV3d *(void)
+ {
+ return (RwV3d *)this;
+ }
+
+ operator RwV3d &(void)
+ {
+ return *((RwV3d *)this);
+ }
+#endif
float Magnitude(void) const { return sqrt(x*x + y*y + z*z); }
float MagnitudeSqr(void) const { return x*x + y*y + z*z; }
float Magnitude2D(void) const { return sqrt(x*x + y*y); }
- void Normalise(void){
- float sq = MagnitudeSqr();
- if(sq > 0.0f){
- float invsqrt = 1.0f/sqrt(sq);
- x *= invsqrt;
- y *= invsqrt;
- z *= invsqrt;
- }else
- x = 1.0f;
+ void Normalise(void);
+
+
+ // operator =
+ inline CVector const& operator = (CVector const &refRight)
+ {
+ x = refRight.x;
+ y = refRight.y;
+ z = refRight.z;
+ return *this;
+ }
+
+ inline CVector const& operator = (float fRight)
+ {
+ x = fRight;
+ y = fRight;
+ z = fRight;
+ return *this;
}
-// rw::V3d ToRW(void){
-// return rw::makeV3d(x, y, z);
-// }
-// void operator=(rw::V3d const &rhs){
-// x = rhs.x;
-// y = rhs.y;
-// z = rhs.z;
-// }
- CVector operator-(const CVector &rhs) const {
- return CVector(x-rhs.x, y-rhs.y, z-rhs.z);
+
+ // operator +=
+ inline CVector const& operator += (CVector const &refRight)
+ {
+ x += refRight.x;
+ y += refRight.y;
+ z += refRight.z;
+ return *this;
}
- CVector operator+(const CVector &rhs) const {
- return CVector(x+rhs.x, y+rhs.y, z+rhs.z);
+
+ inline CVector const& operator += (float fRight)
+ {
+ x += fRight;
+ y += fRight;
+ z += fRight;
+ return *this;
}
- CVector operator*(float t) const {
- return CVector(x*t, y*t, z*t);
+
+ // operator -=
+ inline CVector const& operator -= (CVector const &refRight)
+ {
+ x -= refRight.x;
+ y -= refRight.y;
+ z -= refRight.z;
+ return *this;
}
- CVector operator/(float t) const {
- return CVector(x/t, y/t, z/t);
+
+ inline CVector const& operator -= (float fRight)
+ {
+ x -= fRight;
+ y -= fRight;
+ z -= fRight;
+ return *this;
}
- CVector &operator-=(const CVector &rhs) {
- this->x -= rhs.x;
- this->y -= rhs.y;
- this->z -= rhs.z;
+
+ // operator *=
+ inline CVector const& operator *= (CVector const &refRight)
+ {
+ x *= refRight.x;
+ y *= refRight.y;
+ z *= refRight.z;
return *this;
}
- CVector &operator+=(const CVector &rhs) {
- this->x += rhs.x;
- this->y += rhs.y;
- this->z += rhs.z;
+
+ inline CVector const& operator *= (float fRight)
+ {
+ x *= fRight;
+ y *= fRight;
+ z *= fRight;
return *this;
}
- CVector &operator*=(float t) {
- this->x *= t;
- this->y *= t;
- this->z *= t;
+
+ // operator /=
+ inline CVector const& operator /= (CVector const &refRight)
+ {
+ x /= refRight.x;
+ y /= refRight.y;
+ z /= refRight.z;
return *this;
}
- CVector &operator/=(float t) {
- this->x /= t;
- this->y /= t;
- this->z /= t;
+
+ inline CVector const& operator /= (float fRight)
+ {
+ x /= fRight;
+ y /= fRight;
+ z /= fRight;
return *this;
}
+
+ inline CVector operator - () const
+ {
+ return CVector(-x, -y, -z);
+ }
+
bool IsZero(void) { return x == 0.0f && y == 0.0f && z == 0.0f; }
};
+//extern CVector operator*(CMatrix const& matrix, CVector const& vector);
+
inline float
DotProduct(const CVector &v1, const CVector &v2)
{
@@ -81,3 +142,23 @@ CrossProduct(const CVector &v1, const CVector &v2)
v1.z*v2.x - v1.x*v2.z,
v1.x*v2.y - v1.y*v2.x);
}
+
+// operator +
+extern CVector operator + (CVector const &refLeft, CVector const &refRight);
+extern CVector operator + (CVector const &refLeft, float fRight);
+extern CVector operator + (float fLeft, CVector const &refRight);
+
+// operator -
+extern CVector operator - (CVector const &refLeft, CVector const &refRight);
+extern CVector operator - (CVector const &refLeft, float fRight);
+extern CVector operator - (float fLeft, CVector const &refRight);
+
+// operator *
+extern CVector operator * (CVector const &refLeft, CVector const &refRight);
+extern CVector operator * (CVector const &refLeft, float fRight);
+extern CVector operator * (float fLeft, CVector const &refRight);
+
+// operator /
+extern CVector operator / (CVector const &refLeft, CVector const &refRight);
+extern CVector operator / (CVector const &refLeft, float fRight);
+extern CVector operator / (float fLeft, CVector const &refRight); \ No newline at end of file
diff --git a/src/render/Particle.cpp b/src/render/Particle.cpp
index 9bbc4587..2e0e1c35 100644
--- a/src/render/Particle.cpp
+++ b/src/render/Particle.cpp
@@ -1,10 +1,1868 @@
#include "common.h"
#include "patcher.h"
+#include "General.h"
+#include "Timer.h"
+#include "TxdStore.h"
+#include "Sprite.h"
+#include "Camera.h"
+#include "Collision.h"
+#include "World.h"
+#include "Shadows.h"
+#include "AudioScriptObject.h"
+#include "ParticleObject.h"
#include "Particle.h"
-WRAPPER void
-CParticle::AddParticle(tParticleType, const CVector &pos, const CVector &velocity, CEntity *ent,
- float size, int32 rotationStep, int32 rotation, int startFrame, int lifeSpan)
+#define MAX_PARTICLES_ON_SCREEN (1000)
+
+
+//(5)
+#define MAX_SMOKE_FILES ARRAY_SIZE(SmokeFiles)
+
+//(5)
+#define MAX_SMOKE2_FILES ARRAY_SIZE(Smoke2Files)
+//(5)
+#define MAX_RUBBER_FILES ARRAY_SIZE(RubberFiles)
+//(5)
+#define MAX_RAINSPLASH_FILES ARRAY_SIZE(RainSplashFiles)
+//(3)
+#define MAX_WATERSPRAY_FILES ARRAY_SIZE(WatersprayFiles)
+//(6)
+#define MAX_EXPLOSIONMEDIUM_FILES ARRAY_SIZE(ExplosionMediumFiles)
+//(4)
+#define MAX_GUNFLASH_FILES ARRAY_SIZE(GunFlashFiles)
+//(2)
+#define MAX_RAINSPLASHUP_FILES ARRAY_SIZE(RainSplashupFiles)
+//(4)
+#define MAX_BIRDFRONT_FILES ARRAY_SIZE(BirdfrontFiles)
+//(4)
+#define MAX_CARDEBRIS_FILES ARRAY_SIZE(CardebrisFiles)
+//(4)
+#define MAX_CARSPLASH_FILES ARRAY_SIZE(CarsplashFiles)
+
+//(4)
+#define MAX_RAINDROP_FILES ARRAY_SIZE(RaindropFiles)
+
+
+
+const Char SmokeFiles[][6+1] =
+{
+ "smoke1",
+ "smoke2",
+ "smoke3",
+ "smoke4",
+ "smoke5"
+};
+
+
+const Char Smoke2Files[][9+1] =
+{
+ "smokeII_1",
+ "smokeII_2",
+ "smokeII_3",
+ "smokeII_4",
+ "smokeII_5"
+};
+
+const Char RubberFiles[][7+1] =
+{
+ "rubber1",
+ "rubber2",
+ "rubber3",
+ "rubber4",
+ "rubber5"
+};
+
+const Char RainSplashFiles[][7+1] =
+{
+ "splash1",
+ "splash2",
+ "splash3",
+ "splash4",
+ "splash5"
+};
+
+const Char WatersprayFiles[][11+1] =
+{
+ "waterspray1",
+ "waterspray2",
+ "waterspray3"
+};
+
+const Char ExplosionMediumFiles[][7+1] =
+{
+ "explo01",
+ "explo02",
+ "explo03",
+ "explo04",
+ "explo05",
+ "explo06"
+};
+
+const Char GunFlashFiles[][9+1] =
+{
+ "gunflash1",
+ "gunflash2",
+ "gunflash3",
+ "gunflash4"
+};
+
+const Char RaindropFiles[][9+1] =
+{
+ "raindrop1",
+ "raindrop2",
+ "raindrop3",
+ "raindrop4"
+};
+
+const Char RainSplashupFiles[][10+1] =
+{
+ "splash_up1",
+ "splash_up2"
+};
+
+const Char BirdfrontFiles[][8+1] =
+{
+ "birdf_01",
+ "birdf_02",
+ "birdf_03",
+ "birdf_04"
+};
+
+const Char CardebrisFiles[][12+1] =
+{
+ "cardebris_01",
+ "cardebris_02",
+ "cardebris_03",
+ "cardebris_04"
+};
+
+const Char CarsplashFiles[][12+1] =
+{
+ "carsplash_01",
+ "carsplash_02",
+ "carsplash_03",
+ "carsplash_04"
+};
+
+CParticle gParticleArray[MAX_PARTICLES_ON_SCREEN];
+
+RwTexture *gpSmokeTex[MAX_SMOKE_FILES];
+RwTexture *gpSmoke2Tex[MAX_SMOKE2_FILES];
+RwTexture *gpRubberTex[MAX_RUBBER_FILES];
+RwTexture *gpRainSplashTex[MAX_RAINSPLASH_FILES];
+RwTexture *gpWatersprayTex[MAX_WATERSPRAY_FILES];
+RwTexture *gpExplosionMediumTex[MAX_EXPLOSIONMEDIUM_FILES];
+RwTexture *gpGunFlashTex[MAX_GUNFLASH_FILES];
+RwTexture *gpRainSplashupTex[MAX_RAINSPLASHUP_FILES];
+RwTexture *gpBirdfrontTex[MAX_BIRDFRONT_FILES];
+RwTexture *gpCarDebrisTex[MAX_CARDEBRIS_FILES];
+RwTexture *gpCarSplashTex[MAX_CARSPLASH_FILES];
+
+RwTexture *gpFlame1Tex;
+RwTexture *gpFlame5Tex;
+RwTexture *gpRainDropSmallTex;
+RwTexture *gpBloodTex;
+RwTexture *gpLeafTex;
+RwTexture *gpCloudTex1; // unused
+RwTexture *gpCloudTex4;
+RwTexture *gpBloodSmallTex;
+RwTexture *gpGungeTex;
+RwTexture *gpCollisionSmokeTex;
+RwTexture *gpBulletHitTex;
+RwTexture *gpGunShellTex;
+RwTexture *gpWakeOldTex;
+RwTexture *gpPointlightTex;
+
+RwRaster *gpSmokeRaster[MAX_SMOKE_FILES];
+RwRaster *gpSmoke2Raster[MAX_SMOKE2_FILES];
+RwRaster *gpRubberRaster[MAX_RUBBER_FILES];
+RwRaster *gpRainSplashRaster[MAX_RAINSPLASH_FILES];
+RwRaster *gpWatersprayRaster[MAX_WATERSPRAY_FILES];
+RwRaster *gpExplosionMediumRaster[MAX_EXPLOSIONMEDIUM_FILES];
+RwRaster *gpGunFlashRaster[MAX_GUNFLASH_FILES];
+RwRaster *gpRainSplashupRaster[MAX_RAINSPLASHUP_FILES];
+RwRaster *gpBirdfrontRaster[MAX_BIRDFRONT_FILES];
+RwRaster *gpCarDebrisRaster[MAX_CARDEBRIS_FILES];
+RwRaster *gpCarSplashRaster[MAX_CARSPLASH_FILES];
+
+RwRaster *gpFlame1Raster;
+RwRaster *gpFlame5Raster;
+RwRaster *gpRainDropSmallRaster;
+RwRaster *gpBloodRaster;
+RwRaster *gpLeafRaster;
+RwRaster *gpCloudRaster1; // unused
+RwRaster *gpCloudRaster4;
+RwRaster *gpBloodSmallRaster;
+RwRaster *gpGungeRaster;
+RwRaster *gpCollisionSmokeRaster;
+RwRaster *gpBulletHitRaster;
+RwRaster *gpGunShellRaster;
+RwRaster *gpWakeOldRaster;
+
+
+//RwRaster *gpPointlightRaster; // CPointLights::RenderFogEffect
+RwRaster *&gpPointlightRaster = *(RwRaster **)0x8F5FE0;
+
+//RwTexture *gpRainDropTex[MAX_RAINDROP_FILES]; // CWeather::RenderRainStreaks
+RwTexture * (&gpRainDropTex)[MAX_RAINDROP_FILES] = *(RwTexture * (*)[MAX_RAINDROP_FILES])*(int *)0x880660;
+
+
+RwRaster *gpRainDropRaster[MAX_RAINDROP_FILES];
+
+//Float CParticle::ms_afRandTable[CParticle::RAND_TABLE_SIZE]; //
+Float (&CParticle::ms_afRandTable)[CParticle::RAND_TABLE_SIZE] = *(Float (*)[CParticle::RAND_TABLE_SIZE])*(int *)0x6E9878;
+
+
+CParticle *CParticle::m_pUnusedListHead;
+
+
+//Float CParticle::m_SinTable[CParticle::SIN_COS_TABLE_SIZE]; //
+//Float CParticle::m_CosTable[CParticle::SIN_COS_TABLE_SIZE]; /
+Float (&CParticle::m_SinTable)[CParticle::SIN_COS_TABLE_SIZE] = *(Float (*)[CParticle::SIN_COS_TABLE_SIZE])*(int *)0x877358;
+Float (&CParticle::m_CosTable)[CParticle::SIN_COS_TABLE_SIZE] = *(Float (*)[CParticle::SIN_COS_TABLE_SIZE])*(int *)0x70DA18;
+
+Int32 Randomizer;
+
+Int32 nParticleCreationInterval = 1;
+Float fParticleScaleLimit = 0.5f;
+
+
+
+RwTexture *&gpBloodPoolTex = *(RwTexture **)0x9415F8;
+
+
+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 = NULL;
+ 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], NULL);
+ gpSmokeRaster[i] = RwTextureGetRaster(gpSmokeTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_SMOKE2_FILES; i++ )
+ {
+ gpSmoke2Tex[i] = RwTextureRead(Smoke2Files[i], NULL);
+ gpSmoke2Raster[i] = RwTextureGetRaster(gpSmoke2Tex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_RUBBER_FILES; i++ )
+ {
+ gpRubberTex[i] = RwTextureRead(RubberFiles[i], NULL);
+ gpRubberRaster[i] = RwTextureGetRaster(gpRubberTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
+ {
+ gpRainSplashTex[i] = RwTextureRead(RainSplashFiles[i], NULL);
+ gpRainSplashRaster[i] = RwTextureGetRaster(gpRainSplashTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
+ {
+ gpWatersprayTex[i] = RwTextureRead(WatersprayFiles[i], NULL);
+ gpWatersprayRaster[i] = RwTextureGetRaster(gpWatersprayTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
+ {
+ gpExplosionMediumTex[i] = RwTextureRead(ExplosionMediumFiles[i], NULL);
+ gpExplosionMediumRaster[i] = RwTextureGetRaster(gpExplosionMediumTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
+ {
+ gpGunFlashTex[i] = RwTextureRead(GunFlashFiles[i], NULL);
+ gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINDROP_FILES; i++ )
+ {
+ gpRainDropTex[i] = RwTextureRead(RaindropFiles[i], NULL);
+ gpRainDropRaster[i] = RwTextureGetRaster(gpRainDropTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
+ {
+ gpRainSplashupTex[i] = RwTextureRead(RainSplashupFiles[i], NULL);
+ gpRainSplashupRaster[i] = RwTextureGetRaster(gpRainSplashupTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
+ {
+ gpBirdfrontTex[i] = RwTextureRead(BirdfrontFiles[i], NULL);
+ gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
+ {
+ gpCarDebrisTex[i] = RwTextureRead(CardebrisFiles[i], NULL);
+ gpCarDebrisRaster[i] = RwTextureGetRaster(gpCarDebrisTex[i]);
+ }
+
+ for ( Int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
+ {
+ gpCarSplashTex[i] = RwTextureRead(CarsplashFiles[i], NULL);
+ gpCarSplashRaster[i] = RwTextureGetRaster(gpCarSplashTex[i]);
+ }
+
+ gpFlame1Tex = RwTextureRead("flame1", NULL);
+ gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex);
+
+ gpFlame5Tex = RwTextureRead("flame5", NULL);
+
+ gpFlame5Raster = RwTextureGetRaster(gpFlame1Tex); // copy-paste bug ?
+
+ gpRainDropSmallTex = RwTextureRead("rainsmall", NULL);
+ gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex);
+
+ gpBloodTex = RwTextureRead("blood", NULL);
+ gpBloodRaster = RwTextureGetRaster(gpBloodTex);
+
+ gpLeafTex = RwTextureRead("gameleaf01_64", NULL);
+ gpLeafRaster = RwTextureGetRaster(gpLeafTex);
+
+ gpCloudTex1 = RwTextureRead("cloud3", NULL);
+ gpCloudRaster1 = RwTextureGetRaster(gpCloudTex1);
+
+ gpCloudTex4 = RwTextureRead("cloudmasked", NULL);
+ gpCloudRaster4 = RwTextureGetRaster(gpCloudTex4);
+
+ gpBloodSmallTex = RwTextureRead("bloodsplat2", NULL);
+ gpBloodSmallRaster = RwTextureGetRaster(gpBloodSmallTex);
+
+ gpGungeTex = RwTextureRead("gunge", NULL);
+ gpGungeRaster = RwTextureGetRaster(gpGungeTex);
+
+ gpCollisionSmokeTex = RwTextureRead("collisionsmoke", NULL);
+ gpCollisionSmokeRaster = RwTextureGetRaster(gpCollisionSmokeTex);
+
+ gpBulletHitTex = RwTextureRead("bullethitsmoke", NULL);
+ gpBulletHitRaster = RwTextureGetRaster(gpBulletHitTex);
+
+ gpGunShellTex = RwTextureRead("gunshell", NULL);
+ gpGunShellRaster = RwTextureGetRaster(gpGunShellTex);
+
+ gpWakeOldTex = RwTextureRead("wake_old", NULL);
+ gpWakeOldRaster = RwTextureGetRaster(gpWakeOldTex);
+
+ gpPointlightTex = RwTextureRead("pointlight", NULL);
+ gpPointlightRaster = RwTextureGetRaster(gpPointlightTex);
+
+ CTxdStore::PopCurrentTxd();
+
+ for ( Int32 i = 0; i < MAX_PARTICLES; i++ )
+ {
+ tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i];
+
+ switch ( i )
+ {
+ case PARTICLE_BLOOD:
+ entry->m_ppRaster = &gpBloodRaster;
+ break;
+
+ case PARTICLE_BLOOD_SMALL:
+ case PARTICLE_BLOOD_SPURT:
+ entry->m_ppRaster = &gpBloodSmallRaster;
+ break;
+
+ case PARTICLE_DEBRIS2:
+ entry->m_ppRaster = &gpGungeRaster;
+ break;
+
+ case PARTICLE_GUNFLASH:
+ case PARTICLE_GUNFLASH_NOANIM:
+ entry->m_ppRaster = gpGunFlashRaster;
+ break;
+
+ case PARTICLE_GUNSMOKE:
+ case PARTICLE_SPLASH:
+ entry->m_ppRaster = NULL;
+ break;
+
+ case PARTICLE_FLAME:
+ case PARTICLE_CARFLAME:
+ entry->m_ppRaster = &gpFlame1Raster;
+ break;
+
+ case PARTICLE_FIREBALL:
+ entry->m_ppRaster = &gpFlame5Raster;
+ break;
+
+ case PARTICLE_RAIN_SPLASH:
+ case PARTICLE_RAIN_SPLASH_BIGGROW:
+ entry->m_ppRaster = gpRainSplashRaster;
+ break;
+
+ case PARTICLE_RAIN_SPLASHUP:
+ entry->m_ppRaster = gpRainSplashupRaster;
+ break;
+
+ case PARTICLE_WATERSPRAY:
+ entry->m_ppRaster = gpWatersprayRaster;
+ break;
+
+ case PARTICLE_SHARD:
+ case PARTICLE_RAINDROP:
+ case PARTICLE_RAINDROP_2D:
+ entry->m_ppRaster = gpRainDropRaster;
+ break;
+
+ case PARTICLE_EXPLOSION_MEDIUM:
+ case PARTICLE_EXPLOSION_LARGE:
+ case PARTICLE_EXPLOSION_MFAST:
+ case PARTICLE_EXPLOSION_LFAST:
+ entry->m_ppRaster = gpExplosionMediumRaster;
+ break;
+
+ case PARTICLE_BOAT_WAKE:
+ entry->m_ppRaster = &gpWakeOldRaster;
+ break;
+
+ case PARTICLE_CAR_SPLASH:
+ case PARTICLE_WATER_HYDRANT:
+ case PARTICLE_PED_SPLASH:
+ entry->m_ppRaster = gpCarSplashRaster;
+ break;
+
+ case PARTICLE_SPARK:
+ case PARTICLE_SPARK_SMALL:
+ case PARTICLE_RAINDROP_SMALL:
+ case PARTICLE_HELI_ATTACK:
+ entry->m_ppRaster = &gpRainDropSmallRaster;
+ break;
+
+ case PARTICLE_DEBRIS:
+ case PARTICLE_TREE_LEAVES:
+ entry->m_ppRaster = &gpLeafRaster;
+ break;
+
+ case PARTICLE_CAR_DEBRIS:
+ case PARTICLE_HELI_DEBRIS:
+ entry->m_ppRaster = gpCarDebrisRaster;
+ break;
+
+ case PARTICLE_WHEEL_DIRT:
+ case PARTICLE_STEAM2:
+ case PARTICLE_STEAM_NY:
+ case PARTICLE_STEAM_NY_SLOWMOTION:
+ case PARTICLE_ENGINE_STEAM:
+ case PARTICLE_BOAT_THRUSTJET:
+ case PARTICLE_PEDFOOT_DUST:
+ case PARTICLE_EXHAUST_FUMES:
+ entry->m_ppRaster = gpSmoke2Raster;
+ break;
+
+ case PARTICLE_GUNSMOKE2:
+ case PARTICLE_RUBBER_SMOKE:
+ entry->m_ppRaster = gpRubberRaster;
+ break;
+
+ case PARTICLE_CARCOLLISION_DUST:
+ case PARTICLE_BURNINGRUBBER_SMOKE:
+ entry->m_ppRaster = &gpCollisionSmokeRaster;
+ break;
+
+ case PARTICLE_WHEEL_WATER:
+ case PARTICLE_WATER:
+ case PARTICLE_SMOKE:
+ case PARTICLE_SMOKE_SLOWMOTION:
+ case PARTICLE_GARAGEPAINT_SPRAY:
+ case PARTICLE_STEAM:
+ case PARTICLE_BOAT_SPLASH:
+ case PARTICLE_WATER_CANNON:
+ case PARTICLE_EXTINGUISH_STEAM:
+ case PARTICLE_HELI_DUST:
+ case PARTICLE_PAINT_SMOKE:
+ case PARTICLE_BULLETHIT_SMOKE:
+ entry->m_ppRaster = gpSmokeRaster;
+ break;
+
+ case PARTICLE_GUNSHELL_FIRST:
+ case PARTICLE_GUNSHELL:
+ case PARTICLE_GUNSHELL_BUMP1:
+ case PARTICLE_GUNSHELL_BUMP2:
+ entry->m_ppRaster = &gpGunShellRaster;
+ break;
+
+ case PARTICLE_ENGINE_SMOKE:
+ case PARTICLE_ENGINE_SMOKE2:
+ case PARTICLE_CARFLAME_SMOKE:
+ case PARTICLE_FIREBALL_SMOKE:
+ case PARTICLE_TEST:
+ entry->m_ppRaster = &gpCloudRaster4;
+ break;
+
+ case PARTICLE_BIRD_FRONT:
+ entry->m_ppRaster = gpBirdfrontRaster;
+ break;
+ }
+ }
+
+ debug("CParticle ready");
+}
+
+void CParticle::Shutdown()
+{
+ debug("Shutting down CParticle...");
+
+ for ( Int32 i = 0; i < MAX_SMOKE_FILES; i++ )
+ {
+ RwTextureDestroy(gpSmokeTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpSmokeTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_SMOKE2_FILES; i++ )
+ {
+ RwTextureDestroy(gpSmoke2Tex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpSmoke2Tex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_RUBBER_FILES; i++ )
+ {
+ RwTextureDestroy(gpRubberTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpRubberTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainSplashTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpRainSplashTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ )
+ {
+ RwTextureDestroy(gpWatersprayTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpWatersprayTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ )
+ {
+ RwTextureDestroy(gpExplosionMediumTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpExplosionMediumTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_GUNFLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpGunFlashTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpGunFlashTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINDROP_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainDropTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpRainDropTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ )
+ {
+ RwTextureDestroy(gpRainSplashupTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpRainSplashupTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ )
+ {
+ RwTextureDestroy(gpBirdfrontTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpBirdfrontTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ )
+ {
+ RwTextureDestroy(gpCarDebrisTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpCarDebrisTex[i] = NULL;
+#endif
+ }
+
+ for ( Int32 i = 0; i < MAX_CARSPLASH_FILES; i++ )
+ {
+ RwTextureDestroy(gpCarSplashTex[i]);
+#if GTA3_1_1_PATCH == TRUE
+ gpCarSplashTex[i] = NULL;
+#endif
+ }
+
+ RwTextureDestroy(gpFlame1Tex);
+#if GTA3_1_1_PATCH == TRUE
+ gpFlame1Tex = NULL;
+#endif
+
+ RwTextureDestroy(gpFlame5Tex);
+#if GTA3_1_1_PATCH == TRUE
+ gpFlame5Tex = NULL;
+#endif
+
+ RwTextureDestroy(gpRainDropSmallTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpRainDropSmallTex = NULL;
+#endif
+
+ RwTextureDestroy(gpBloodTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpBloodTex = NULL;
+#endif
+
+ RwTextureDestroy(gpLeafTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpLeafTex = NULL;
+#endif
+
+ RwTextureDestroy(gpCloudTex1);
+#if GTA3_1_1_PATCH == TRUE
+ gpCloudTex1 = NULL;
+#endif
+
+ RwTextureDestroy(gpCloudTex4);
+#if GTA3_1_1_PATCH == TRUE
+ gpCloudTex4 = NULL;
+#endif
+
+ RwTextureDestroy(gpBloodSmallTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpBloodSmallTex = NULL;
+#endif
+
+ RwTextureDestroy(gpGungeTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpGungeTex = NULL;
+#endif
+
+ RwTextureDestroy(gpCollisionSmokeTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpCollisionSmokeTex = NULL;
+#endif
+
+ RwTextureDestroy(gpBulletHitTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpBulletHitTex = NULL;
+#endif
+
+ RwTextureDestroy(gpGunShellTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpGunShellTex = NULL;
+#endif
+
+ RwTextureDestroy(gpWakeOldTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpWakeOldTex = NULL;
+#endif
+
+ RwTextureDestroy(gpPointlightTex);
+#if GTA3_1_1_PATCH == TRUE
+ gpPointlightTex = NULL;
+#endif
+
+ Int32 slot;
+
+ slot = CTxdStore::FindTxdSlot("particle");
+ CTxdStore::RemoveTxdSlot(slot);
+
+ debug("CParticle shut down");
+}
+
+CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, Float fSize, Int32 nRotationSpeed, Int32 nRotation, Int32 nCurFrame, Int32 nLifeSpan)
+{
+ CRGBA color(0, 0, 0, 0);
+ return AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan);
+}
+
+CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, Float fSize, RwRGBA const &color, Int32 nRotationSpeed, Int32 nRotation, Int32 nCurFrame, Int32 nLifeSpan)
+{
+ if ( CTimer::m_UserPause || CTimer::m_CodePause )
+ return NULL;
+
+ 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::m_FrameCounter )
+ {
+ return NULL;
+ }
+
+ CParticle *pParticle = m_pUnusedListHead;
+
+ if ( pParticle == NULL )
+ return NULL;
+
+ tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[type];
+
+ if ( psystem->m_fCreateRange != 0.0f && psystem->m_fCreateRange < ( TheCamera.GetPosition() - vecPos ).MagnitudeSqr() )
+ return NULL;
+
+
+ pParticle->m_fSize = psystem->m_fDefaultInitialRadius;
+ pParticle->m_fExpansionRate = psystem->m_fExpansionRate;
+
+ if ( nLifeSpan != 0 )
+ pParticle->m_nTimeWhenWillBeDestroyed = CTimer::m_snTimeInMilliseconds + nLifeSpan;
+ else
+ pParticle->m_nTimeWhenWillBeDestroyed = CTimer::m_snTimeInMilliseconds + psystem->m_nLifeSpan;
+
+ pParticle->m_nColorIntensity = psystem->m_nFadeToBlackInitialIntensity;
+ pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity;
+ pParticle->m_nCurrentZRotation = psystem->m_nZRotationInitialAngle;
+ pParticle->m_fCurrentZRadius = psystem->m_fInitialZRadius;
+
+ if ( nCurFrame != 0 )
+ pParticle->m_nCurrentFrame = nCurFrame;
+ else
+ pParticle->m_nCurrentFrame = psystem->m_nStartAnimationFrame;
+
+ pParticle->m_nFadeToBlackTimer = 0;
+ pParticle->m_nFadeAlphaTimer = 0;
+ pParticle->m_nZRotationTimer = 0;
+ pParticle->m_nZRadiusTimer = 0;
+ pParticle->m_nAnimationSpeedTimer = 0;
+ pParticle->m_fZGround = 0.0f;
+ pParticle->m_vecPosition = vecPos;
+ pParticle->m_vecVelocity = vecDir;
+ pParticle->m_vecParticleMovementOffset = CVector(0.0f, 0.0f, 0.0f);
+ pParticle->m_nTimeWhenColorWillBeChanged = 0;
+
+ if ( color.alpha != 0 )
+ RwRGBAAssign(&pParticle->m_Color, &color);
+ else
+ {
+ RwRGBAAssign(&pParticle->m_Color, &psystem->m_RenderColouring);
+
+ if ( psystem->m_ColorFadeTime != 0 )
+ pParticle->m_nTimeWhenColorWillBeChanged = CTimer::m_snTimeInMilliseconds + 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 ( pParticle->m_nRotation >= 360 )
+ pParticle->m_nRotation -= 360;
+ else if ( pParticle->m_nRotation < 0 )
+ pParticle->m_nRotation += 360;
+
+ if ( nRotationSpeed != 0 )
+ pParticle->m_nRotationStep = nRotationSpeed;
+ else
+ pParticle->m_nRotationStep = psystem->m_nRotationSpeed;
+
+ if ( CGeneral::GetRandomNumber() & 1 )
+ pParticle->m_nRotationStep = -pParticle->m_nRotationStep;
+
+ pParticle->m_vecScreenPosition.x = 0.0f; // bug ?
+
+ if ( psystem->m_fPositionRandomError != 0.0f )
+ {
+ pParticle->m_vecPosition.x += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+ pParticle->m_vecPosition.y += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+
+ if ( psystem->Flags & RAND_VERT_V )
+ pParticle->m_vecPosition.z += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+ }
+
+ if ( psystem->m_fVelocityRandomError != 0.0f )
+ {
+ pParticle->m_vecVelocity.x += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+ pParticle->m_vecVelocity.y += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+
+ if ( psystem->Flags & RAND_VERT_V )
+ pParticle->m_vecVelocity.z += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+ }
+
+ if ( psystem->m_fExpansionRateError != 0.0f )
+ pParticle->m_fExpansionRate += psystem->m_fExpansionRateError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE] + psystem->m_fExpansionRateError;
+
+ if ( psystem->m_nRotationRateError != 0 )
+ pParticle->m_nRotationStep += CGeneral::GetRandomNumberInRange(-psystem->m_nRotationRateError, psystem->m_nRotationRateError);
+
+ if ( psystem->m_nLifeSpanErrorShape != 0 )
+ {
+ Float randVal = ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE];
+ if ( randVal > 0.0f )
+ pParticle->m_nTimeWhenWillBeDestroyed += Int32(Float(psystem->m_nLifeSpan) * randVal * Float(psystem->m_nLifeSpanErrorShape));
+ else
+ pParticle->m_nTimeWhenWillBeDestroyed += Int32(Float(psystem->m_nLifeSpan) * randVal / Float(psystem->m_nLifeSpanErrorShape));
+ }
+
+ if ( psystem->Flags & ZCHECK_FIRST )
+ {
+ static Bool bValidGroundFound = false;
+ static CVector LastTestCoors;
+ static Float LastTestGroundZ;
+
+ if ( bValidGroundFound
+ && vecPos.x == LastTestCoors.x
+ && vecPos.y == LastTestCoors.y
+ && vecPos.z == LastTestCoors.z )
+ {
+ pParticle->m_fZGround = LastTestGroundZ;
+ }
+ else
+ {
+ bValidGroundFound = false;
+
+ CColPoint point;
+ CEntity *entity;
+
+ if ( !CWorld::ProcessVerticalLine(
+ pParticle->m_vecPosition + CVector(0.0f, 0.0f, 0.5f),
+ -100.0f, point, entity, true, true, false, false, true, false, NULL) )
+ {
+ return NULL;
+ }
+
+ if ( point.point.z >= pParticle->m_vecPosition.z )
+ return NULL;
+
+ 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 NULL;
+
+ 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;
+ }
+
+ 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::m_UserPause || CTimer::m_CodePause )
+ return;
+
+ CRGBA color(0, 0, 0, 0);
+
+ Float fFricDeccel50 = pow(0.50f, CTimer::ms_fTimeStep);
+ Float fFricDeccel80 = pow(0.80f, CTimer::ms_fTimeStep);
+ Float fFricDeccel90 = pow(0.90f, CTimer::ms_fTimeStep);
+ Float fFricDeccel95 = pow(0.95f, CTimer::ms_fTimeStep);
+ Float fFricDeccel96 = pow(0.96f, CTimer::ms_fTimeStep);
+ Float fFricDeccel99 = pow(0.99f, CTimer::ms_fTimeStep);
+
+ CParticleObject::UpdateAll();
+
+ for ( Int32 i = 0; i < MAX_PARTICLES; i++ )
+ {
+ tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i];
+ CParticle *particle = psystem->m_pParticles;
+ CParticle *prevParticle = NULL;
+ Bool bRemoveParticle;
+
+ if ( particle == NULL )
+ continue;
+
+ for ( ; particle != NULL; _Next(particle, prevParticle, psystem, bRemoveParticle) )
+ {
+ bRemoveParticle = false;
+
+ CVector moveStep = particle->m_vecPosition + ( particle->m_vecVelocity * CTimer::ms_fTimeStep );
+
+ if ( CTimer::m_snTimeInMilliseconds > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+
+ if ( particle->m_nTimeWhenColorWillBeChanged != 0 )
+ {
+ if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::m_snTimeInMilliseconds )
+ {
+ Float colorMul = 1.0f - Float(particle->m_nTimeWhenColorWillBeChanged - CTimer::m_snTimeInMilliseconds) / Float(psystem->m_ColorFadeTime);
+
+ particle->m_Color.red = clamp(
+ psystem->m_RenderColouring.red + Int32(Float(psystem->m_FadeDestinationColor.red - psystem->m_RenderColouring.red) * colorMul),
+ 0, 255);
+
+ particle->m_Color.green = clamp(
+ psystem->m_RenderColouring.green + Int32(Float(psystem->m_FadeDestinationColor.green - psystem->m_RenderColouring.green) * colorMul),
+ 0, 255);
+
+ particle->m_Color.blue = clamp(
+ psystem->m_RenderColouring.blue + Int32(Float(psystem->m_FadeDestinationColor.blue - psystem->m_RenderColouring.blue) * colorMul),
+ 0, 255);
+ }
+ else
+ RwRGBAAssign(&particle->m_Color, &psystem->m_FadeDestinationColor);
+ }
+
+ if ( psystem->Flags & CLIPOUT2D )
+ {
+ if ( particle->m_vecPosition.x < -10.0f || particle->m_vecPosition.x > SCREEN_WIDTH + 10.0f
+ || particle->m_vecPosition.y < -10.0f || particle->m_vecPosition.y > SCREEN_HEIGHT + 10.0f )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+ }
+
+ Float size = particle->m_fSize + particle->m_fExpansionRate;
+
+ if ( size < 0.0f )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+
+ particle->m_fSize = size;
+
+ switch ( psystem->m_nFrictionDecceleration )
+ {
+ case 50:
+ particle->m_vecVelocity *= fFricDeccel50;
+ break;
+
+ case 80:
+ particle->m_vecVelocity *= fFricDeccel80;
+ break;
+
+ case 90:
+ particle->m_vecVelocity *= fFricDeccel90;
+ break;
+
+ case 95:
+ particle->m_vecVelocity *= fFricDeccel95;
+ break;
+
+ case 96:
+ particle->m_vecVelocity *= fFricDeccel96;
+ break;
+
+ case 99:
+ particle->m_vecVelocity *= fFricDeccel99;
+ break;
+ }
+
+ if ( psystem->m_fGravitationalAcceleration > 0.0f )
+ {
+ if ( -50.0f * psystem->m_fGravitationalAcceleration < particle->m_vecVelocity.z )
+ particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::ms_fTimeStep;
+
+ 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), NULL, 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), NULL, 0.0f, 0, 0, 0, 0);
+ }
+
+ continue;
+ }
+ break;
+
+ case PARTICLE_WHEEL_WATER:
+ {
+ bRemoveParticle = true;
+
+ Int32 randVal = Int32(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), NULL, 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), NULL, 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 )
+ {
+ Int32 randTime = Int32(CGeneral::GetRandomNumber());
+
+ CShadows::AddPermanentShadow(1, gpBloodPoolTex, &vecPosn,
+ 0.1f, 0.0f, 0.0f, -0.1f,
+ 255,
+ 255, 0, 0,
+ 4.0f, (randTime & 0xFFF) + 2000, 1.0f);
+ }
+ else if ( randVal == 2 )
+ {
+ Int32 randTime = Int32(CGeneral::GetRandomNumber());
+
+ CShadows::AddPermanentShadow(1, gpBloodPoolTex, &vecPosn,
+ 0.2f, 0.0f, 0.0f, -0.2f,
+ 255,
+ 255, 0, 0,
+ 4.0f, (randTime & 0xFFF) + 8000, 1.0f);
+ }
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ else if ( psystem->Flags & ZCHECK_STEP )
+ {
+ CColPoint point;
+ CEntity *entity;
+
+ if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, moveStep.z, point, entity,
+ true, true, false, false, true, false, NULL) )
+ {
+ if ( moveStep.z <= point.point.z )
+ {
+ moveStep.z = point.point.z;
+ if ( psystem->m_Type == PARTICLE_DEBRIS2 )
+ {
+ particle->m_vecVelocity *= CVector(0.8f, 0.8f, -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)
+ ),
+ NULL,
+ particle->m_fSize, color, particle->m_nRotationStep, 0, 0, 0);
+
+ PlayOneShotScriptObject(_SOUND_BULLET_SHELL_HIT_GROUND_1, 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)),
+ NULL,
+ particle->m_fSize, color, 0, 0, 0, 0);
+
+ PlayOneShotScriptObject(_SOUND_BULLET_SHELL_HIT_GROUND_2, particle->m_vecPosition);
+ }
+ break;
+
+ case PARTICLE_GUNSHELL_BUMP2:
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+ 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::ms_fTimeStep;
+ }
+ else
+ {
+ if ( psystem->Flags & ZCHECK_STEP )
+ {
+ CColPoint point;
+ CEntity *entity;
+
+ if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, moveStep.z, point, entity,
+ true, false, false, false, true, false, NULL) )
+ {
+ if ( moveStep.z <= point.point.z )
+ {
+ moveStep.z = point.point.z;
+ if ( psystem->m_Type == PARTICLE_HELI_ATTACK )
+ {
+ bRemoveParticle = true;
+ AddParticle(PARTICLE_STEAM, moveStep, CVector(0.0f, 0.0f, 0.05f), NULL, 0.2f, 0, 0, 0, 0);
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( psystem->m_nFadeToBlackAmount != 0 )
+ {
+ if ( particle->m_nFadeToBlackTimer >= psystem->m_nFadeToBlackTime )
+ {
+ particle->m_nFadeToBlackTimer = 0;
+
+ particle->m_nColorIntensity = clamp(particle->m_nColorIntensity - psystem->m_nFadeToBlackAmount,
+ 0, 255);
+ }
+ else
+ ++particle->m_nFadeToBlackTimer;
+ }
+
+ if ( psystem->m_nFadeAlphaAmount != 0 )
+ {
+ if ( particle->m_nFadeAlphaTimer >= psystem->m_nFadeAlphaTime )
+ {
+ particle->m_nFadeAlphaTimer = 0;
+
+ particle->m_nAlpha = clamp(particle->m_nAlpha - psystem->m_nFadeAlphaAmount,
+ 0, 255);
+
+ if ( particle->m_nAlpha == 0 )
+ {
+ bRemoveParticle = true;
+ continue;
+ }
+ }
+ else
+ ++particle->m_nFadeAlphaTimer;
+ }
+
+ if ( psystem->m_nZRotationAngleChangeAmount != 0 )
+ {
+ if ( particle->m_nZRotationTimer >= psystem->m_nZRotationChangeTime )
+ {
+ particle->m_nZRotationTimer = 0;
+ particle->m_nCurrentZRotation += psystem->m_nZRotationAngleChangeAmount;
+ }
+ else
+ ++particle->m_nZRotationTimer;
+ }
+
+ if ( psystem->m_fZRadiusChangeAmount != 0.0f )
+ {
+ if ( particle->m_nZRadiusTimer >= psystem->m_nZRadiusChangeTime )
+ {
+ particle->m_nZRadiusTimer = 0;
+ particle->m_fCurrentZRadius += psystem->m_fZRadiusChangeAmount;
+ }
+ else
+ ++particle->m_nZRadiusTimer;
+ }
+
+ if ( psystem->m_nAnimationSpeed != 0 )
+ {
+ if ( particle->m_nAnimationSpeedTimer > psystem->m_nAnimationSpeed )
+ {
+ particle->m_nAnimationSpeedTimer = 0;
+
+ if ( ++particle->m_nCurrentFrame > psystem->m_nFinalAnimationFrame )
+ {
+ if ( psystem->Flags & CYCLE_ANIM )
+ particle->m_nCurrentFrame = psystem->m_nStartAnimationFrame;
+ else
+ --particle->m_nCurrentFrame;
+ }
+ }
+ else
+ ++particle->m_nAnimationSpeedTimer;
+ }
+
+ if ( particle->m_nRotationStep != 0 )
+ {
+ particle->m_nRotation += particle->m_nRotationStep;
+
+ if ( particle->m_nRotation >= 360 )
+ particle->m_nRotation -= 360;
+ else if ( particle->m_nRotation < 0 )
+ particle->m_nRotation += 360;
+ }
+
+ if ( particle->m_fCurrentZRadius != 0.0f )
+ {
+ Int32 nSinCosIndex = particle->m_nCurrentZRotation % (SIN_COS_TABLE_SIZE - 1);
+
+ Float fX = (m_CosTable[nSinCosIndex] - m_SinTable[nSinCosIndex])
+ * particle->m_fCurrentZRadius;
+
+ Float fY = (m_SinTable[nSinCosIndex] + m_CosTable[nSinCosIndex])
+ * particle->m_fCurrentZRadius;
+
+ moveStep -= particle->m_vecParticleMovementOffset;
+
+ moveStep += CVector(fX, fY, 0.0f);
+
+ particle->m_vecParticleMovementOffset = CVector(fX, fY, 0.0f);
+ }
+
+ particle->m_vecPosition = moveStep;
+ }
+ }
+}
+
+void CParticle::Render()
+{
+ RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP);
+ RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
+
+ CSprite::InitSpriteBuffer2D();
+
+ UInt32 flags = DRAW_OPAQUE;
+
+ RwRaster *prevFrame = NULL;
+
+ 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 != NULL )
+ {
+ RwRaster *curFrame = *frames;
+ if ( curFrame != prevFrame )
+ {
+ CSprite::FlushSpriteBuffer();
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame);
+ prevFrame = curFrame;
+ }
+ }
+ }
+
+ while ( particle != NULL )
+ {
+ Bool canDraw = true;
+
+ if ( particle->m_nAlpha == 0 )
+ canDraw = false;
+
+ if ( canDraw && psystem->m_nFinalAnimationFrame != 0 && frames != NULL )
+ {
+ RwRaster *curFrame = frames[particle->m_nCurrentFrame];
+ if ( prevFrame != curFrame )
+ {
+ CSprite::FlushSpriteBuffer();
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame);
+ prevFrame = curFrame;
+ }
+ }
+
+ if ( canDraw && psystem->Flags & DRAWTOP2D )
+ {
+ if ( particle->m_nRotation != 0 )
+ {
+ CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(
+ particle->m_vecPosition.x,
+ particle->m_vecPosition.y,
+ particle->m_fSize * 63.0f,
+ particle->m_fSize * 63.0f,
+ particle->m_Color,
+ particle->m_nColorIntensity,
+ (Float)particle->m_nRotation,
+ particle->m_nAlpha);
+ }
+ else
+ {
+ CSprite::RenderBufferedOneXLUSprite2D(
+ particle->m_vecPosition.x,
+ particle->m_vecPosition.y,
+ particle->m_fSize * 63.0f,
+ particle->m_fSize * 63.0f,
+ particle->m_Color,
+ particle->m_nColorIntensity,
+ particle->m_nAlpha);
+ }
+
+ canDraw = false;
+ }
+
+ if ( canDraw )
+ {
+ CVector coors;
+ Float w;
+ Float h;
+
+ if ( CSprite::CalcScreenCoors(particle->m_vecPosition, coors, &w, &h, true) )
+ {
+ if ( (!particleBanned || SCREEN_WIDTH * fParticleScaleLimit >= w)
+ && SCREEN_HEIGHT * fParticleScaleLimit >= h )
+ {
+ if ( particle->m_nRotation != 0 )
+ {
+ CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
+ particle->m_fSize * w, particle->m_fSize * h,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ Float(particle->m_nRotation),
+ particle->m_nAlpha);
+ }
+ else if ( psystem->Flags & SCREEN_TRAIL )
+ {
+ Float fRotation;
+ Float fTrailLength;
+
+ if ( particle->m_vecScreenPosition.x == 0.0f )
+ {
+ fTrailLength = 0.0f;
+ fRotation = 0.0f;
+ }
+ else
+ {
+ CVector2D vecDist
+ (
+ coors.x - particle->m_vecScreenPosition.x,
+ coors.y - particle->m_vecScreenPosition.y
+ );
+
+ Float fDist = vecDist.Magnitude();
+
+ fTrailLength = fDist;
+
+ //Float fRot = atan2( vecDist.x / fDist, sqrtf(1.0f - vecDist.x / fDist * (vecDist.x / fDist)) );
+ Float fRot = asinf(vecDist.x / fDist);
+
+ fRotation = fRot;
+
+ if ( vecDist.y < 0.0f )
+ fRotation = -1.0f * fRot + DEGTORAD(180.0f);
+
+ fRotation = RADTODEG(fRotation);
+
+ if ( fRotation < 0.0f )
+ fRotation += 360.0f;
+
+ Float fSpeed = particle->m_vecVelocity.Magnitude();
+
+ Float fNewTrailLength = fSpeed * CTimer::ms_fTimeStep * w * 2.0f;
+
+ if ( fDist > fNewTrailLength )
+ fTrailLength = fNewTrailLength;
+ }
+
+ CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
+ particle->m_fSize * w,
+ particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ fRotation,
+ particle->m_nAlpha);
+
+ particle->m_vecScreenPosition = coors;
+ }
+ else if ( psystem->Flags & SPEED_TRAIL )
+ {
+ CVector vecPrevPos = particle->m_vecPosition - particle->m_vecVelocity;
+ Float fRotation;
+ Float fTrailLength;
+
+ if ( CSprite::CalcScreenCoors(vecPrevPos, particle->m_vecScreenPosition, &fTrailLength, &fRotation, true) )
+ {
+ CVector2D vecDist
+ (
+ coors.x - particle->m_vecScreenPosition.x,
+ coors.y - particle->m_vecScreenPosition.y
+ );
+
+ Float fDist = vecDist.Magnitude();
+
+ fTrailLength = fDist;
+
+ //Float fRot = atan2(vecDist.x / fDist, sqrt(1.0f - vecDist.x / fDist * (vecDist.x / fDist)));
+ Float fRot = asinf(vecDist.x / fDist);
+
+ fRotation = fRot;
+
+ if ( vecDist.y < 0.0f )
+ fRotation = -1.0f * fRot + DEGTORAD(180.0f);
+
+ fRotation = RADTODEG(fRotation);
+
+ if ( fRotation < 0.0f )
+ fRotation += 360.0f;
+ }
+ else
+ {
+ fRotation = 0.0f;
+ fTrailLength = 0.0f;
+ }
+
+ CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z,
+ particle->m_fSize * w,
+ particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ fRotation,
+ particle->m_nAlpha);
+ }
+ else if ( psystem->Flags & VERT_TRAIL )
+ {
+ Float fTrailLength = fabsf(particle->m_vecVelocity.z * 10.0f);
+
+ CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
+ particle->m_fSize * w,
+ (particle->m_fSize + fTrailLength * psystem->m_fTrailLengthMultiplier) * h,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ particle->m_nAlpha);
+ }
+ else if ( i == PARTICLE_RAINDROP_SMALL )
+ {
+ CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
+ particle->m_fSize * w * 0.05f,
+ particle->m_fSize * h,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ particle->m_nAlpha);
+ }
+ else if ( i == PARTICLE_BOAT_WAKE )
+ {
+ CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
+ particle->m_fSize * w,
+ psystem->m_fDefaultInitialRadius * h,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ particle->m_nAlpha);
+ }
+ else
+ {
+ CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z,
+ particle->m_fSize * w,
+ particle->m_fSize * h,
+ particle->m_Color.red,
+ particle->m_Color.green,
+ particle->m_Color.blue,
+ particle->m_nColorIntensity,
+ 1.0f / coors.z,
+ particle->m_nAlpha);
+ }
+ }
+ }
+ }
+
+ particle = particle->m_pNext;
+ }
+
+ CSprite::FlushSpriteBuffer();
+
+ }
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
+}
+
+void CParticle::RemovePSystem(tParticleType type)
+{
+ tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[type];
+
+ for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = psystemdata->m_pParticles )
+ RemoveParticle(particle, NULL, psystemdata);
+}
+
+void CParticle::RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData)
+{
+ if ( pPrevParticle )
+ pPrevParticle->m_pNext = pParticle->m_pNext;
+ else
+ pPSystemData->m_pParticles = pParticle->m_pNext;
+
+ pParticle->m_pNext = m_pUnusedListHead;
+ m_pUnusedListHead = pParticle;
+}
+
+void CParticle::AddJetExplosion(CVector const &vecPos, Float fPower, Float fSize)
+{
+ CRGBA color(240, 240, 240, 255);
+
+ if ( fPower < 1.0f )
+ fPower = 1.0f;
+
+ CVector vecRandOffset
+ (
+ CGeneral::GetRandomNumberInRange(-0.4f, 0.4f),
+ CGeneral::GetRandomNumberInRange(-0.4f, 0.4f),
+ CGeneral::GetRandomNumberInRange(0.1f, 0.3f)
+ );
+
+ vecRandOffset *= 2.0f;
+
+ CVector vecStepPos = vecPos;
+
+ for ( Int32 i = 0; i < Int32(fPower * 4.0f); i++ )
+ {
+ AddParticle(PARTICLE_EXPLOSION_MFAST,
+ vecStepPos,
+ CVector
+ (
+ CGeneral::GetRandomNumberInRange(-0.2f, 0.2f),
+ CGeneral::GetRandomNumberInRange(-0.2f, 0.2f),
+ CGeneral::GetRandomNumberInRange(-0.2f, 0.0f)
+ ),
+ NULL,
+ 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)
+ ),
+ NULL,
+ 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)
+ ),
+ NULL,
+ fSize, color, 0, 0, 0, 0);
+
+ vecStepPos += vecRandOffset;
+ }
+}
+
+void CParticle::AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix)
{
- EAXJMP(0x50D140);
+ 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),
+ NULL,
+ 0.3f, color, 0, 0, 0, 0);
+ }
}
+
+STARTPATCHES
+ //InjectHook(0x50C410, &CParticle::ctor, PATCH_JUMP);
+ //InjectHook(0x50C420, &CParticle::dtor, PATCH_JUMP);
+ InjectHook(0x50C430, CParticle::ReloadConfig, PATCH_JUMP);
+ InjectHook(0x50C570, CParticle::Initialise, PATCH_JUMP);
+ InjectHook(0x50CF40, CParticle::Shutdown, PATCH_JUMP);
+ //InjectHook(0x50D140, CParticle::AddParticle, PATCH_JUMP);
+ InjectHook(0x50D190, (CParticle* (__cdecl *)(tParticleType, CVector const&, CVector const&, CEntity*, float, RwRGBA const&, int, int, int, int))CParticle::AddParticle, PATCH_JUMP);
+ InjectHook(0x50DCF0, CParticle::Update, PATCH_JUMP);
+ InjectHook(0x50EE20, CParticle::Render, PATCH_JUMP);
+ InjectHook(0x50F6E0, CParticle::RemovePSystem, PATCH_JUMP);
+ InjectHook(0x50F720, CParticle::RemoveParticle, PATCH_JUMP);
+ InjectHook(0x50F760, CParticle::AddJetExplosion, PATCH_JUMP);
+ InjectHook(0x50FAA0, CParticle::AddYardieDoorSmoke, PATCH_JUMP);
+
+ENDPATCHES \ No newline at end of file
diff --git a/src/render/Particle.h b/src/render/Particle.h
index f711ecf1..4b96dfe0 100644
--- a/src/render/Particle.h
+++ b/src/render/Particle.h
@@ -1,82 +1,106 @@
#pragma once
+#include "ParticleMgr.h"
-enum tParticleType
-{
- PARTICLE_SPARK,
- PARTICLE_SPARK_SMALL,
- PARTICLE_WHEEL_DIRT,
- PARTICLE_WHEEL_WATER,
- PARTICLE_BLOOD,
- PARTICLE_BLOOD_SMALL,
- PARTICLE_BLOOD_SPURT,
- PARTICLE_DEBRIS,
- PARTICLE_DEBRIS2,
- PARTICLE_WATER,
- PARTICLE_FLAME,
- PARTICLE_FIREBALL,
- PARTICLE_GUNFLASH,
- PARTICLE_GUNFLASH_NOANIM,
- PARTICLE_GUNSMOKE,
- PARTICLE_GUNSMOKE2,
- PARTICLE_SMOKE,
- PARTICLE_SMOKE_SLOWMOTION,
- PARTICLE_GARAGEPAINT_SPRAY,
- PARTICLE_SHARD,
- PARTICLE_SPLASH,
- PARTICLE_CARFLAME,
- PARTICLE_STEAM,
- PARTICLE_STEAM2,
- PARTICLE_STEAM_NY,
- PARTICLE_STEAM_NY_SLOWMOTION,
- PARTICLE_ENGINE_STEAM,
- PARTICLE_RAINDROP,
- PARTICLE_RAINDROP_SMALL,
- PARTICLE_RAIN_SPLASH,
- PARTICLE_RAIN_SPLASH_BIGGROW,
- PARTICLE_RAIN_SPLASHUP,
- PARTICLE_WATERSPRAY,
- PARTICLE_EXPLOSION_MEDIUM,
- PARTICLE_EXPLOSION_LARGE,
- PARTICLE_EXPLOSION_MFAST,
- PARTICLE_EXPLOSION_LFAST,
- PARTICLE_CAR_SPLASH,
- PARTICLE_BOAT_SPLASH,
- PARTICLE_BOAT_THRUSTJET,
- PARTICLE_BOAT_WAKE,
- PARTICLE_WATER_HYDRANT,
- PARTICLE_WATER_CANNON,
- PARTICLE_EXTINGUISH_STEAM,
- PARTICLE_PED_SPLASH,
- PARTICLE_PEDFOOT_DUST,
- PARTICLE_HELI_DUST,
- PARTICLE_HELI_ATTACK,
- PARTICLE_ENGINE_SMOKE,
- PARTICLE_ENGINE_SMOKE2,
- PARTICLE_CARFLAME_SMOKE,
- PARTICLE_FIREBALL_SMOKE,
- PARTICLE_PAINT_SMOKE,
- PARTICLE_TREE_LEAVES,
- PARTICLE_CARCOLLISION_DUST,
- PARTICLE_CAR_DEBRIS,
- PARTICLE_HELI_DEBRIS,
- PARTICLE_EXHAUST_FUMES,
- PARTICLE_RUBBER_SMOKE,
- PARTICLE_BURNINGRUBBER_SMOKE,
- PARTICLE_BULLETHIT_SMOKE,
- PARTICLE_GUNSHELL_FIRST,
- PARTICLE_GUNSHELL,
- PARTICLE_GUNSHELL_BUMP1,
- PARTICLE_GUNSHELL_BUMP2,
- PARTICLE_TEST,
- PARTICLE_BIRD_FRONT,
- PARTICLE_RAINDROP_2D,
-};
class CEntity;
class CParticle
{
+ enum
+ {
+ RAND_TABLE_SIZE = 20,
+ SIN_COS_TABLE_SIZE = 1024
+ };
+
+public:
+ CVector m_vecPosition;
+ CVector m_vecVelocity;
+ CVector m_vecScreenPosition;
+ UInt32 m_nTimeWhenWillBeDestroyed;
+ UInt32 m_nTimeWhenColorWillBeChanged;
+ Float m_fZGround;
+ CVector m_vecParticleMovementOffset;
+ Int16 m_nCurrentZRotation;
+ UInt16 m_nZRotationTimer;
+ Float m_fCurrentZRadius;
+ UInt16 m_nZRadiusTimer;
+ char _pad0[2];
+ Float m_fSize;
+ Float m_fExpansionRate;
+ UInt16 m_nFadeToBlackTimer;
+ UInt16 m_nFadeAlphaTimer;
+ UInt8 m_nColorIntensity;
+ UInt8 m_nAlpha;
+ UInt16 m_nCurrentFrame;
+ Int16 m_nAnimationSpeedTimer;
+ Int16 m_nRotationStep;
+ Int16 m_nRotation;
+ RwRGBA m_Color;
+ char _pad1[2];
+ CParticle *m_pNext;
+
+ CParticle()
+ {
+ ;
+ }
+
+ ~CParticle()
+ {
+ ;
+ }
+
+ //static Float ms_afRandTable[RAND_TABLE_SIZE];
+ static Float (&ms_afRandTable)[RAND_TABLE_SIZE];
+ static CParticle *m_pUnusedListHead;
+
+ /*
+ static Float m_SinTable[SIN_COS_TABLE_SIZE];
+ static Float m_CosTable[SIN_COS_TABLE_SIZE];
+ */
+ static Float (&m_SinTable)[SIN_COS_TABLE_SIZE];
+ static Float (&m_CosTable)[SIN_COS_TABLE_SIZE];
+
+
+ static void ReloadConfig();
+ static void Initialise();
+ static void Shutdown();
+
+ static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity = NULL, Float fSize = 0.0f, Int32 nRotationSpeed = 0, Int32 nRotation = 0, Int32 nCurFrame = 0, Int32 nLifeSpan = 0);
+ static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, Float fSize, RwRGBA const &color, Int32 nRotationSpeed = 0, Int32 nRotation = 0, Int32 nCurFrame = 0, Int32 nLifeSpan = 0);
+
+ static void Update();
+ static void Render();
+
+ static void RemovePSystem(tParticleType type);
+ static void RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData);
+
+ static inline void _Next(CParticle *&pParticle, CParticle *&pPrevParticle, tParticleSystemData *pPSystemData, Bool bRemoveParticle)
+ {
+ if ( bRemoveParticle )
+ {
+ RemoveParticle(pParticle, pPrevParticle, pPSystemData);
+
+ if ( pPrevParticle )
+ pParticle = pPrevParticle->m_pNext;
+ else
+ pParticle = pPSystemData->m_pParticles;
+ }
+ else
+ {
+ pPrevParticle = pParticle;
+ pParticle = pParticle->m_pNext;
+ }
+ }
+
+ static void AddJetExplosion(CVector const &vecPos, Float fPower, Float fSize);
+ static void AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix);
+
+};
+/*
+class CParticle
+{
public:
static void AddParticle(tParticleType, const CVector &pos, const CVector &velocity, CEntity *ent = nil,
float size = 0.0, int32 rotationStep = 0, int32 rotation = 0, int startFrame = 0, int lifeSpan = 0);
};
+*/ \ No newline at end of file
diff --git a/src/render/Shadows.cpp b/src/render/Shadows.cpp
new file mode 100644
index 00000000..ead84e92
--- /dev/null
+++ b/src/render/Shadows.cpp
@@ -0,0 +1,7 @@
+#include "common.h"
+#include "Shadows.h"
+
+void CShadows::AddPermanentShadow(unsigned char ShadowType, RwTexture* pTexture, CVector* pPosn, float fX1, float fY1, float fX2, float fY2, short nTransparency, unsigned char nRed, unsigned char nGreen, unsigned char nBlue, float fZDistance, unsigned int nTime, float fScale)
+{
+ ((void (__cdecl *)(unsigned char, RwTexture*, CVector*, float, float, float, float, short, unsigned char, unsigned char, unsigned char, float, unsigned int, float))0x56EC50)(ShadowType, pTexture, pPosn, fX1, fY1, fX2, fY2, nTransparency, nRed, nGreen, nBlue, fZDistance, nTime, fScale);
+}
diff --git a/src/render/Shadows.h b/src/render/Shadows.h
new file mode 100644
index 00000000..3c7b4981
--- /dev/null
+++ b/src/render/Shadows.h
@@ -0,0 +1,9 @@
+#pragma once
+
+struct RwTexture;
+
+class CShadows
+{
+public:
+ static void AddPermanentShadow(unsigned char ShadowType, RwTexture* pTexture, CVector* pPosn, float fX1, float fY1, float fX2, float fY2, short nTransparency, unsigned char nRed, unsigned char nGreen, unsigned char nBlue, float fZDistance, unsigned int nTime, float fScale);
+}; \ No newline at end of file