From 197767d1c83f8d940698cd200785e91fbc2ccc98 Mon Sep 17 00:00:00 2001 From: Fire-Head Date: Wed, 29 May 2019 21:02:58 +0300 Subject: [PATCH] Updates, CTimer started --- src/General.h | 8 +- src/ParticleMgr.cpp | 3 +- src/ParticleMgr.h | 9 +- src/RecordDataForChase.cpp | 5 + src/RecordDataForChase.h | 7 ++ src/RecordDataForGame.cpp | 4 + src/RecordDataForGame.h | 7 ++ src/Timer.cpp | 218 +++++++++++++++++++++++++++++++++++++ src/Timer.h | 15 ++- src/audio/DMAudio.cpp | 6 + src/audio/DMAudio.h | 1 + src/common.h | 16 +++ src/config.h | 2 + src/main.cpp | 24 +++- src/render/Particle.cpp | 38 +++---- src/render/Particle.h | 10 +- 16 files changed, 337 insertions(+), 36 deletions(-) create mode 100644 src/RecordDataForChase.cpp create mode 100644 src/RecordDataForChase.h create mode 100644 src/RecordDataForGame.cpp create mode 100644 src/RecordDataForGame.h diff --git a/src/General.h b/src/General.h index 7aacee39..38737bd5 100644 --- a/src/General.h +++ b/src/General.h @@ -1,3 +1,6 @@ + + + class CGeneral { public: @@ -11,5 +14,8 @@ public: { return myrand() & 0xFFFF; } // Probably don't want to ever reach high static float GetRandomNumberInRange(float low, float high) - { return low + (high - low)*(GetRandomNumber()/65536.0f); } + { return low + (high - low)*(GetRandomNumber()/float(RAND_MAX + 1)); } + + static Int32 GetRandomNumberInRange(Int32 low, Int32 high) + { return low + (high - low)*(GetRandomNumber()/float(RAND_MAX + 1)); } }; diff --git a/src/ParticleMgr.cpp b/src/ParticleMgr.cpp index e4b3c7e5..d259c4b4 100644 --- a/src/ParticleMgr.cpp +++ b/src/ParticleMgr.cpp @@ -3,11 +3,12 @@ #include "FileMgr.h" #include "ParticleMgr.h" +_TODO("work_buff"); UInt8 work_buff[55000]; cParticleSystemMgr mod_ParticleSystemManager; -const char *ParticleFilename = "PARTICLE.CFG"; +const Char *ParticleFilename = "PARTICLE.CFG"; //cParticleSystemMgr::cParticleSystemMgr() void cParticleSystemMgr::ctor() diff --git a/src/ParticleMgr.h b/src/ParticleMgr.h index e650255b..a2dcbdb5 100644 --- a/src/ParticleMgr.h +++ b/src/ParticleMgr.h @@ -96,7 +96,7 @@ enum ZCHECK_BUMP_FIRST = BIT(13) }; -#pragma pack(push, 1) + struct tParticleSystemData { tParticleType m_Type; @@ -140,9 +140,9 @@ struct tParticleSystemData RwRaster **m_ppRaster; CParticle *m_pParticles; }; -#pragma pack(pop) +VALIDATE_SIZE(tParticleSystemData, 0x88); + -#pragma pack(push, 1) class cParticleSystemMgr { enum @@ -200,6 +200,7 @@ public: void LoadParticleData(); //void RangeCheck(tParticleSystemData *pData); }; -#pragma pack(pop) + +VALIDATE_SIZE(cParticleSystemMgr, 0x2420); extern cParticleSystemMgr mod_ParticleSystemManager; \ No newline at end of file diff --git a/src/RecordDataForChase.cpp b/src/RecordDataForChase.cpp new file mode 100644 index 00000000..6bff623e --- /dev/null +++ b/src/RecordDataForChase.cpp @@ -0,0 +1,5 @@ +#include "common.h" +#include "RecordDataForChase.h" + + +UInt8 &CRecordDataForChase::Status = *(UInt8*)0x95CDCE; \ No newline at end of file diff --git a/src/RecordDataForChase.h b/src/RecordDataForChase.h new file mode 100644 index 00000000..0dc72bed --- /dev/null +++ b/src/RecordDataForChase.h @@ -0,0 +1,7 @@ +#pragma once + +class CRecordDataForChase +{ +public: + static UInt8 &Status; +}; \ No newline at end of file diff --git a/src/RecordDataForGame.cpp b/src/RecordDataForGame.cpp new file mode 100644 index 00000000..05b4223c --- /dev/null +++ b/src/RecordDataForGame.cpp @@ -0,0 +1,4 @@ +#include "common.h" +#include "RecordDataForGame.h" + +UInt16 &CRecordDataForGame::RecordingState = *(UInt16 *)0x95CC24; \ No newline at end of file diff --git a/src/RecordDataForGame.h b/src/RecordDataForGame.h new file mode 100644 index 00000000..5d007ce5 --- /dev/null +++ b/src/RecordDataForGame.h @@ -0,0 +1,7 @@ +#pragma once + +class CRecordDataForGame +{ +public: + static UInt16 &RecordingState; +}; \ No newline at end of file diff --git a/src/Timer.cpp b/src/Timer.cpp index 9cc35e42..02dbf55a 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -1,6 +1,10 @@ #include "common.h" #include "patcher.h" +#include "DMAudio.h" #include "Timer.h" +#include "RecordDataForGame.h" +#include "RecordDataForChase.h" +#include uint32 &CTimer::m_snTimeInMilliseconds = *(uint32*)0x885B48; uint32 &CTimer::m_snTimeInMillisecondsPauseMode = *(uint32*)0x5F7614; @@ -12,3 +16,217 @@ float &CTimer::ms_fTimeStep = *(float*)0x8E2CB4; float &CTimer::ms_fTimeStepNonClipped = *(float*)0x8E2C4C; bool &CTimer::m_UserPause = *(bool*)0x95CD7C; bool &CTimer::m_CodePause = *(bool*)0x95CDB1; + +UInt32 oldPcTimer; +UInt32 suspendPcTimer; + +UInt32 _nCyclesPerMS = 1; + +LARGE_INTEGER _oldPerfCounter; +LARGE_INTEGER perfSuspendCounter; + +UInt32 suspendDepth; + +_TODO("We need skeleton.c for RsTimer()"); + +RwUInt32 RsTimer(void) +{ + return ((RwUInt32 (__cdecl *)())0x584890)(); +} + +void CTimer::Initialise(void) +{ + debug("Initialising CTimer...\n"); + + ms_fTimeScale = 1.0f; + ms_fTimeStep = 1.0f; + suspendDepth = 0; + m_UserPause = false; + m_CodePause = false; + m_snTimeInMillisecondsNonClipped = 0; + m_snPreviousTimeInMilliseconds = 0; + m_snTimeInMilliseconds = 1; + + LARGE_INTEGER perfFreq; + if ( QueryPerformanceFrequency(&perfFreq) ) + { + OutputDebugString("Performance counter available\n"); + _nCyclesPerMS = UInt32(perfFreq.QuadPart / 1000); + QueryPerformanceCounter(&_oldPerfCounter); + } + else + { + OutputDebugString("Performance counter not available, using millesecond timer\n"); + _nCyclesPerMS = 0; + oldPcTimer = RsTimer(); + } + + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds; + + m_FrameCounter = 0; + + DMAudio.ResetTimers(m_snPreviousTimeInMilliseconds); + + debug("CTimer ready\n"); +} + +void CTimer::Shutdown(void) +{ + ; +} + +void CTimer::Update(void) +{ + m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; + + if ( (Double)_nCyclesPerMS != 0.0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + + Int64 updInCycles = (pc.LowPart - _oldPerfCounter.LowPart) & 0x7FFFFFFF; + + _oldPerfCounter = pc; + + Double updInCyclesScaled = (Double)updInCycles * ms_fTimeScale; + + Double upd = updInCyclesScaled / (Double)_nCyclesPerMS; + + m_snTimeInMillisecondsPauseMode += Int64(upd); + + if ( GetIsPaused() ) + ms_fTimeStep = 0.0f; + else + { + m_snTimeInMilliseconds = Int64(upd); + m_snTimeInMillisecondsNonClipped += Int64(upd); + ms_fTimeStep = updInCyclesScaled / (Double)_nCyclesPerMS / 20.0; + } + } + else + { + UInt32 timer = RsTimer(); + + UInt32 updInMs = timer - oldPcTimer; + + Double upd = (Double)updInMs * ms_fTimeScale; + + oldPcTimer = timer; + + m_snTimeInMillisecondsPauseMode += Int64(upd); + + if ( GetIsPaused() ) + ms_fTimeStep = 0.0f; + else + { + m_snTimeInMilliseconds += Int64(upd); + m_snTimeInMillisecondsNonClipped += Int64(upd); + ms_fTimeStep = upd / 1000.0f * 50.0f; + } + } + + if ( ms_fTimeStep < 0.01f && !GetIsPaused() ) + ms_fTimeStep = 0.01f; + + ms_fTimeStepNonClipped = ms_fTimeStep; + + if ( CRecordDataForGame::RecordingState != _TODOCONST(2) ) + { + ms_fTimeStep = min(3.0f, ms_fTimeStep); + + if ( (m_snTimeInMilliseconds - m_snPreviousTimeInMilliseconds) > 60 ) + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 60; + } + + if ( CRecordDataForChase::Status == _TODOCONST(1) ) + { + ms_fTimeStep = 1.0f; + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 16; + } + + m_FrameCounter++; +} + +void CTimer::Suspend(void) +{ + if ( ++suspendDepth > 1 ) + return; + + if ( (Double)_nCyclesPerMS != 0.0 ) + QueryPerformanceCounter(&perfSuspendCounter); + else + suspendPcTimer = RsTimer(); +} + +void CTimer::Resume(void) +{ + if ( --suspendDepth != 0 ) + return; + + if ( (Double)_nCyclesPerMS != 0.0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + + _oldPerfCounter.LowPart += pc.LowPart - perfSuspendCounter.LowPart; + } + else + oldPcTimer += RsTimer() - suspendPcTimer; +} + +UInt32 CTimer::GetCyclesPerMillisecond(void) +{ + if (_nCyclesPerMS != 0) + return _nCyclesPerMS; + else + return 1; +} + +UInt32 CTimer::GetCurrentTimeInCycles(void) +{ + if ( _nCyclesPerMS != 0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + return (pc.LowPart - _oldPerfCounter.LowPart) & 0x7FFFFFFF; + } + else + return RsTimer() - oldPcTimer; +} + +Bool CTimer::GetIsSlowMotionActive(void) +{ + return ms_fTimeScale < 1.0f; +} + +void CTimer::Stop(void) +{ + m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; +} + +void CTimer::StartUserPause(void) +{ + m_UserPause = true; +} + +void CTimer::EndUserPause(void) +{ + m_UserPause = false; +} + +#if 0 +STARTPATCHES + InjectHook(0x4ACE60, CTimer::Initialise, PATCH_JUMP); + InjectHook(0x4ACF60, CTimer::Shutdown, PATCH_JUMP); + InjectHook(0x4ACF70, CTimer::Update, PATCH_JUMP); + InjectHook(0x4AD310, CTimer::Suspend, PATCH_JUMP); + InjectHook(0x4AD370, CTimer::Resume, PATCH_JUMP); + InjectHook(0x4AD3F0, CTimer::GetCyclesPerMillisecond, PATCH_JUMP); + InjectHook(0x4AD410, CTimer::GetCurrentTimeInCycles, PATCH_JUMP); + InjectHook(0x4AD450, CTimer::GetIsSlowMotionActive, PATCH_JUMP); + InjectHook(0x4AD480, CTimer::Stop, PATCH_JUMP); + InjectHook(0x4AD490, CTimer::StartUserPause, PATCH_JUMP); + InjectHook(0x4AD4A0, CTimer::EndUserPause, PATCH_JUMP); +ENDPATCHES +#endif + diff --git a/src/Timer.h b/src/Timer.h index 765cd050..d5e51dfc 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -2,7 +2,6 @@ class CTimer { -public: // remove when each variable will be encapsulated static uint32 &m_snTimeInMilliseconds; static uint32 &m_snTimeInMillisecondsPauseMode; static uint32 &m_snTimeInMillisecondsNonClipped; @@ -18,4 +17,18 @@ public: static void SetTimeStep(float ts) { ms_fTimeStep = ts; } static uint32 GetFrameCounter(void) { return m_FrameCounter; } static uint32 GetTimeInMilliseconds(void) { return m_snTimeInMilliseconds; } + + static inline Bool GetIsPaused() { return m_UserPause || m_CodePause; } + + static void Initialise(void); + static void Shutdown(void); + static void Update(void); + static void Suspend(void); + static void Resume(void); + static UInt32 GetCyclesPerMillisecond(void); + static UInt32 GetCurrentTimeInCycles(void); + static Bool GetIsSlowMotionActive(void); + static void Stop(void); + static void StartUserPause(void); + static void EndUserPause(void); }; diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index 1ab5a52f..400f3fdc 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -5,3 +5,9 @@ cDMAudio &DMAudio = *(cDMAudio*)0x95CDBE; WRAPPER void cDMAudio::ReportCollision(CEntity *A, CEntity *B, uint8 surfA, uint8 surfB, float impulse, float speed) { EAXJMP(0x57CBE0); } + + +void cDMAudio::ResetTimers(UInt32 timerval) +{ + ((void (__thiscall *)(cDMAudio *, UInt32))0x57CCD0)(this, timerval); +} \ No newline at end of file diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 140c6493..b6d5fc64 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -6,5 +6,6 @@ class cDMAudio { public: void ReportCollision(CEntity *A, CEntity *B, uint8 surfA, uint8 surfB, float impulse, float speed); + void ResetTimers(UInt32 timerval); }; extern cDMAudio &DMAudio; diff --git a/src/common.h b/src/common.h index a96030e4..e8e3cf69 100644 --- a/src/common.h +++ b/src/common.h @@ -39,6 +39,9 @@ typedef double Double; typedef Int8 Bool; //typedef bool Bool; typedef char Char; +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +typedef signed __int64 SInt64; #define nil NULL @@ -144,12 +147,25 @@ sq(float x) { return x*x; } #define DEGTORAD(x) ((x) * PI / 180.0f) #define RADTODEG(x) ((x) * 180.0f / PI) + +#if USE_PS2_RAND == TRUE +#define MY_RAND_MAX 32767 +#else +#define MY_RAND_MAX 65535 +#endif + int myrand(void); void mysrand(unsigned int seed); #define debug printf #define ASSERT assert +#define _TODO(x) +#define _TODOCONST(x) (x) + +#define VALIDATE_SIZE(struc, size) static_assert(sizeof(struc) == size, "Invalid structure size of " #struc) +#define VALIDATE_OFFSET(struc, member, offset) static_assert(offsetof(struc, member) == offset, "The offset of " #member " in " #struc " is not " #offset "...") + #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)) diff --git a/src/config.h b/src/config.h index df99487f..183a442f 100644 --- a/src/config.h +++ b/src/config.h @@ -53,4 +53,6 @@ enum Config { NUMHOURS = 24, }; +#define USE_PS2_RAND FALSE + #endif diff --git a/src/main.cpp b/src/main.cpp index 4f125098..853308a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,17 +24,37 @@ void operator delete(void *ptr) noexcept { gtadelete(ptr); } unsigned __int64 myrand_seed = 1; -int -myrand(void) + +int _cwrand() // original codewarrior rand { + return ((int (__cdecl *)())0x5A41D0)(); +} + +int +myps2rand(void) +{ + return _cwrand(); myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1; return ((myrand_seed >> 32) & 0x7FFFFFFF); } +int myrand(void) +{ +#if USE_PS2_RAND == TRUE + return myps2rand(); +#else + return _cwrand(); +#endif +} + void mysrand(unsigned int seed) { +#if USE_PS2_RAND == TRUE myrand_seed = seed; +#else + ; +#endif } // platform stuff diff --git a/src/render/Particle.cpp b/src/render/Particle.cpp index 2e0e1c35..aade3578 100644 --- a/src/render/Particle.cpp +++ b/src/render/Particle.cpp @@ -766,7 +766,7 @@ CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVe 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 ) + if ( CTimer::GetIsPaused() ) return NULL; if ( ( type == PARTICLE_ENGINE_SMOKE @@ -777,7 +777,7 @@ CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVe || type == PARTICLE_BURNINGRUBBER_SMOKE || type == PARTICLE_EXHAUST_FUMES || type == PARTICLE_CARCOLLISION_DUST ) - && nParticleCreationInterval & CTimer::m_FrameCounter ) + && nParticleCreationInterval & CTimer::GetFrameCounter() ) { return NULL; } @@ -797,9 +797,9 @@ CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVe pParticle->m_fExpansionRate = psystem->m_fExpansionRate; if ( nLifeSpan != 0 ) - pParticle->m_nTimeWhenWillBeDestroyed = CTimer::m_snTimeInMilliseconds + nLifeSpan; + pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + nLifeSpan; else - pParticle->m_nTimeWhenWillBeDestroyed = CTimer::m_snTimeInMilliseconds + psystem->m_nLifeSpan; + pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + psystem->m_nLifeSpan; pParticle->m_nColorIntensity = psystem->m_nFadeToBlackInitialIntensity; pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity; @@ -829,7 +829,7 @@ CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVe RwRGBAAssign(&pParticle->m_Color, &psystem->m_RenderColouring); if ( psystem->m_ColorFadeTime != 0 ) - pParticle->m_nTimeWhenColorWillBeChanged = CTimer::m_snTimeInMilliseconds + psystem->m_ColorFadeTime; + pParticle->m_nTimeWhenColorWillBeChanged = CTimer::GetTimeInMilliseconds() + psystem->m_ColorFadeTime; if ( psystem->m_InitialColorVariation != 0 ) { @@ -995,17 +995,17 @@ CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVe void CParticle::Update() { - if ( CTimer::m_UserPause || CTimer::m_CodePause ) + if ( CTimer::GetIsPaused() ) 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); + 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(); @@ -1023,9 +1023,9 @@ void CParticle::Update() { bRemoveParticle = false; - CVector moveStep = particle->m_vecPosition + ( particle->m_vecVelocity * CTimer::ms_fTimeStep ); + CVector moveStep = particle->m_vecPosition + ( particle->m_vecVelocity * CTimer::GetTimeStep() ); - if ( CTimer::m_snTimeInMilliseconds > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 ) + if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 ) { bRemoveParticle = true; continue; @@ -1033,9 +1033,9 @@ void CParticle::Update() if ( particle->m_nTimeWhenColorWillBeChanged != 0 ) { - if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::m_snTimeInMilliseconds ) + if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::GetTimeInMilliseconds() ) { - Float colorMul = 1.0f - Float(particle->m_nTimeWhenColorWillBeChanged - CTimer::m_snTimeInMilliseconds) / Float(psystem->m_ColorFadeTime); + 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), @@ -1103,7 +1103,7 @@ void CParticle::Update() 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; + particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); if ( psystem->Flags & ZCHECK_FIRST ) { @@ -1301,7 +1301,7 @@ void CParticle::Update() 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; + particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); } else { @@ -1616,7 +1616,7 @@ void CParticle::Render() Float fSpeed = particle->m_vecVelocity.Magnitude(); - Float fNewTrailLength = fSpeed * CTimer::ms_fTimeStep * w * 2.0f; + Float fNewTrailLength = fSpeed * CTimer::GetTimeStep() * w * 2.0f; if ( fDist > fNewTrailLength ) fTrailLength = fNewTrailLength; diff --git a/src/render/Particle.h b/src/render/Particle.h index 4b96dfe0..68e01879 100644 --- a/src/render/Particle.h +++ b/src/render/Particle.h @@ -96,11 +96,5 @@ public: 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 + +VALIDATE_SIZE(CParticle, 0x68); \ No newline at end of file