mirror of
https://github.com/DrBeef/JKXR.git
synced 2024-11-29 23:42:38 +00:00
709 lines
21 KiB
C
709 lines
21 KiB
C
|
/*
|
||
|
===========================================================================
|
||
|
Copyright (C) 2000 - 2013, Raven Software, Inc.
|
||
|
Copyright (C) 2001 - 2013, Activision, Inc.
|
||
|
Copyright (C) 2013 - 2015, OpenJK contributors
|
||
|
|
||
|
This file is part of the OpenJK source code.
|
||
|
|
||
|
OpenJK is free software; you can redistribute it and/or modify it
|
||
|
under the terms of the GNU General Public License version 2 as
|
||
|
published by the Free Software Foundation.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||
|
===========================================================================
|
||
|
*/
|
||
|
|
||
|
#if !defined(FX_UTIL_H_INC)
|
||
|
#include "FxUtil.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include "../qcommon/sstring.h"
|
||
|
typedef sstring_t fxString_t;
|
||
|
|
||
|
#include "../game/genericparser2.h"
|
||
|
#include "qcommon/safe/string.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
|
||
|
#ifndef FX_SCHEDULER_H_INC
|
||
|
#define FX_SCHEDULER_H_INC
|
||
|
|
||
|
#define FX_FILE_PATH "effects"
|
||
|
|
||
|
#define FX_MAX_TRACE_DIST WORLD_SIZE
|
||
|
#define FX_MAX_EFFECTS 150 // how many effects the system can store
|
||
|
#define FX_MAX_EFFECT_COMPONENTS 24 // how many primitives an effect can hold, this should be plenty
|
||
|
#define FX_MAX_PRIM_NAME 32
|
||
|
|
||
|
//-----------------------------------------------
|
||
|
// These are spawn flags for primitiveTemplates
|
||
|
//-----------------------------------------------
|
||
|
|
||
|
#define FX_ORG_ON_SPHERE 0x00001 // Pretty dang expensive, calculates a point on a sphere/ellipsoid
|
||
|
#define FX_AXIS_FROM_SPHERE 0x00002 // Can be used in conjunction with org_on_sphere to cause particles to move out
|
||
|
// from the center of the sphere
|
||
|
#define FX_ORG_ON_CYLINDER 0x00004 // calculate point on cylinder/disk
|
||
|
|
||
|
#define FX_ORG2_FROM_TRACE 0x00010
|
||
|
#define FX_TRACE_IMPACT_FX 0x00020 // if trace impacts, we should play one of the specified impact fx files
|
||
|
#define FX_ORG2_IS_OFFSET 0x00040 // template specified org2 should be the offset from a trace endpos or
|
||
|
// passed in org2. You might use this to lend a random flair to the endpos.
|
||
|
// Note: this is done pre-trace, so you may have to specify large numbers for this
|
||
|
|
||
|
#define FX_CHEAP_ORG_CALC 0x00100 // Origin is calculated relative to passed in axis unless this is on.
|
||
|
#define FX_CHEAP_ORG2_CALC 0x00200 // Origin2 is calculated relative to passed in axis unless this is on.
|
||
|
#define FX_VEL_IS_ABSOLUTE 0x00400 // Velocity isn't relative to passed in axis with this flag on.
|
||
|
#define FX_ACCEL_IS_ABSOLUTE 0x00800 // Acceleration isn't relative to passed in axis with this flag on.
|
||
|
|
||
|
#define FX_RAND_ROT_AROUND_FWD 0x01000 // Randomly rotates up and right around forward vector
|
||
|
#define FX_EVEN_DISTRIBUTION 0x02000 // When you have a delay, it normally picks a random time to play. When
|
||
|
// this flag is on, it generates an even time distribution
|
||
|
#define FX_RGB_COMPONENT_INTERP 0x04000 // Picks a color on the line defined by RGB min & max, default is to pick color in cube defined by min & max
|
||
|
|
||
|
#define FX_AFFECTED_BY_WIND 0x10000 // we take into account our wind vector when we spawn in
|
||
|
|
||
|
#define FX_SND_LESS_ATTENUATION 0x20000 // attenuate sounds less
|
||
|
|
||
|
//-----------------------------------------------------------------
|
||
|
//
|
||
|
// CMediaHandles
|
||
|
//
|
||
|
// Primitive templates might want to use a list of sounds, shaders
|
||
|
// or models to get a bit more variation in their effects.
|
||
|
//
|
||
|
//-----------------------------------------------------------------
|
||
|
class CMediaHandles
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
std::vector<int> mMediaList;
|
||
|
|
||
|
public:
|
||
|
|
||
|
void AddHandle( int item ) { mMediaList.push_back( item ); }
|
||
|
int GetHandle() { if (mMediaList.size()==0) {return 0;}
|
||
|
else {return mMediaList[irand(0,(int)mMediaList.size()-1)];} }
|
||
|
|
||
|
void operator=(const CMediaHandles &that );
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------
|
||
|
//
|
||
|
// CFxRange
|
||
|
//
|
||
|
// Primitive templates typically use this class to define each of
|
||
|
// its members. This is done to make it easier to create effects
|
||
|
// with a desired range of characteristics.
|
||
|
//
|
||
|
//-----------------------------------------------------------------
|
||
|
class CFxRange
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
float mMin;
|
||
|
float mMax;
|
||
|
|
||
|
public:
|
||
|
|
||
|
CFxRange() {mMin=0; mMax=0;}
|
||
|
|
||
|
inline void SetRange(float min,float max) {mMin=min; mMax=max;}
|
||
|
inline void SetMin(float min) {mMin=min;}
|
||
|
inline void SetMax(float max) {mMax=max;}
|
||
|
|
||
|
inline float GetMax() const {return mMax;}
|
||
|
inline float GetMin() const {return mMin;}
|
||
|
|
||
|
inline float GetVal(float percent) const {if(mMin == mMax){return mMin;}
|
||
|
return (mMin + (mMax - mMin) * percent);}
|
||
|
inline float GetVal() const {if(mMin == mMax){return mMin;}
|
||
|
return flrand(mMin, mMax);}
|
||
|
inline int GetRoundedVal() const {if(mMin == mMax){return mMin;}
|
||
|
return (int)(flrand(mMin, mMax) + 0.5f);}
|
||
|
|
||
|
inline void ForceRange(float min,float max) {if(mMin < min){mMin=min;} if(mMin > max){mMin=max;}
|
||
|
if(mMax < min){mMax=min;} if(mMax > max){mMax=max;}}
|
||
|
inline void Sort() {if(mMin > mMax){float temp = mMin; mMin=mMax;mMax=temp;}}
|
||
|
void operator=(const CFxRange &that) {mMin=that.mMin; mMax=that.mMax;}
|
||
|
|
||
|
bool operator==(const CFxRange &rhs) const { return ((mMin == rhs.mMin) &&
|
||
|
(mMax == rhs.mMax)); }
|
||
|
};
|
||
|
|
||
|
|
||
|
//----------------------------
|
||
|
// Supported primitive types
|
||
|
//----------------------------
|
||
|
|
||
|
enum EPrimType
|
||
|
{
|
||
|
None = 0,
|
||
|
Particle, // sprite
|
||
|
Line,
|
||
|
Tail, // comet-like tail thing
|
||
|
Cylinder,
|
||
|
Emitter, // emits effects as it moves, can also attach a chunk
|
||
|
Sound,
|
||
|
Decal, // projected onto architecture
|
||
|
OrientedParticle,
|
||
|
Electricity,
|
||
|
FxRunner,
|
||
|
Light,
|
||
|
CameraShake,
|
||
|
ScreenFlash
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------
|
||
|
//
|
||
|
// CPrimitiveTemplate
|
||
|
//
|
||
|
// The primitive template is used to spawn 1 or more fx primitives
|
||
|
// with the range of characteristics defined by the template.
|
||
|
//
|
||
|
// As such, I just made this one huge shared class knowing that
|
||
|
// there won't be many of them in memory at once, and we won't
|
||
|
// be dynamically creating and deleting them mid-game. Also,
|
||
|
// note that not every primitive type will use all of these fields.
|
||
|
//
|
||
|
//-----------------------------------------------------------------
|
||
|
class CPrimitiveTemplate
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
|
||
|
// These kinds of things should not even be allowed to be accessed publicly
|
||
|
bool mCopy;
|
||
|
int mRefCount; // For a copy of a primitive...when we figure out how many items we want to spawn,
|
||
|
// we'll store that here and then decrement us for each we actually spawn. When we
|
||
|
// hit zero, we are no longer used and so we can just free ourselves
|
||
|
|
||
|
char mName[FX_MAX_PRIM_NAME];
|
||
|
|
||
|
EPrimType mType;
|
||
|
|
||
|
CFxRange mSpawnDelay;
|
||
|
CFxRange mSpawnCount;
|
||
|
CFxRange mLife;
|
||
|
int mCullRange;
|
||
|
|
||
|
CMediaHandles mMediaHandles;
|
||
|
CMediaHandles mImpactFxHandles;
|
||
|
CMediaHandles mDeathFxHandles;
|
||
|
CMediaHandles mEmitterFxHandles;
|
||
|
CMediaHandles mPlayFxHandles;
|
||
|
|
||
|
int mFlags; // These need to get passed on to the primitive
|
||
|
int mSpawnFlags; // These are only used to control spawning, but never get passed to prims.
|
||
|
|
||
|
vec3_t mMin;
|
||
|
vec3_t mMax;
|
||
|
|
||
|
CFxRange mOrigin1X;
|
||
|
CFxRange mOrigin1Y;
|
||
|
CFxRange mOrigin1Z;
|
||
|
|
||
|
CFxRange mOrigin2X;
|
||
|
CFxRange mOrigin2Y;
|
||
|
CFxRange mOrigin2Z;
|
||
|
|
||
|
CFxRange mRadius; // spawn on sphere/ellipse/disk stuff.
|
||
|
CFxRange mHeight;
|
||
|
CFxRange mWindModifier;
|
||
|
|
||
|
CFxRange mRotation;
|
||
|
CFxRange mRotationDelta;
|
||
|
|
||
|
CFxRange mAngle1;
|
||
|
CFxRange mAngle2;
|
||
|
CFxRange mAngle3;
|
||
|
|
||
|
CFxRange mAngle1Delta;
|
||
|
CFxRange mAngle2Delta;
|
||
|
CFxRange mAngle3Delta;
|
||
|
|
||
|
CFxRange mVelX;
|
||
|
CFxRange mVelY;
|
||
|
CFxRange mVelZ;
|
||
|
|
||
|
CFxRange mAccelX;
|
||
|
CFxRange mAccelY;
|
||
|
CFxRange mAccelZ;
|
||
|
|
||
|
CFxRange mGravity;
|
||
|
|
||
|
CFxRange mDensity;
|
||
|
CFxRange mVariance;
|
||
|
|
||
|
CFxRange mRedStart;
|
||
|
CFxRange mGreenStart;
|
||
|
CFxRange mBlueStart;
|
||
|
|
||
|
CFxRange mRedEnd;
|
||
|
CFxRange mGreenEnd;
|
||
|
CFxRange mBlueEnd;
|
||
|
|
||
|
CFxRange mRGBParm;
|
||
|
|
||
|
CFxRange mAlphaStart;
|
||
|
CFxRange mAlphaEnd;
|
||
|
CFxRange mAlphaParm;
|
||
|
|
||
|
CFxRange mSizeStart;
|
||
|
CFxRange mSizeEnd;
|
||
|
CFxRange mSizeParm;
|
||
|
|
||
|
CFxRange mSize2Start;
|
||
|
CFxRange mSize2End;
|
||
|
CFxRange mSize2Parm;
|
||
|
|
||
|
CFxRange mLengthStart;
|
||
|
CFxRange mLengthEnd;
|
||
|
CFxRange mLengthParm;
|
||
|
|
||
|
CFxRange mTexCoordS;
|
||
|
CFxRange mTexCoordT;
|
||
|
|
||
|
CFxRange mElasticity;
|
||
|
|
||
|
private:
|
||
|
|
||
|
// Lower level parsing utilities
|
||
|
bool ParseVector( const gsl::cstring_view& val, vec3_t min, vec3_t max );
|
||
|
bool ParseFloat( const gsl::cstring_view& val, float& min, float& max );
|
||
|
bool ParseGroupFlags( const gsl::cstring_view& val, int& flags );
|
||
|
|
||
|
// Base key processing
|
||
|
// Note that these all have their own parse functions in case it becomes important to do certain kinds
|
||
|
// of validation specific to that type.
|
||
|
bool ParseMin( const gsl::cstring_view& val );
|
||
|
bool ParseMax( const gsl::cstring_view& val );
|
||
|
bool ParseDelay( const gsl::cstring_view& val );
|
||
|
bool ParseCount( const gsl::cstring_view& val );
|
||
|
bool ParseLife( const gsl::cstring_view& val );
|
||
|
bool ParseElasticity( const gsl::cstring_view& val );
|
||
|
bool ParseFlags( const gsl::cstring_view& val );
|
||
|
bool ParseSpawnFlags( const gsl::cstring_view& val );
|
||
|
|
||
|
bool ParseOrigin1( const gsl::cstring_view& val );
|
||
|
bool ParseOrigin2( const gsl::cstring_view& val );
|
||
|
bool ParseRadius( const gsl::cstring_view& val );
|
||
|
bool ParseHeight( const gsl::cstring_view& val );
|
||
|
bool ParseWindModifier( const gsl::cstring_view& val );
|
||
|
bool ParseRotation( const gsl::cstring_view& val );
|
||
|
bool ParseRotationDelta( const gsl::cstring_view& val );
|
||
|
bool ParseAngle( const gsl::cstring_view& val );
|
||
|
bool ParseAngleDelta( const gsl::cstring_view& val );
|
||
|
bool ParseVelocity( const gsl::cstring_view& val );
|
||
|
bool ParseAcceleration( const gsl::cstring_view& val );
|
||
|
bool ParseGravity( const gsl::cstring_view& val );
|
||
|
bool ParseDensity( const gsl::cstring_view& val );
|
||
|
bool ParseVariance( const gsl::cstring_view& val );
|
||
|
|
||
|
/// Case insensitive map from cstring_view to Value
|
||
|
template< typename Value >
|
||
|
using StringViewIMap = std::map< gsl::cstring_view, Value, Q::CStringViewILess >;
|
||
|
using ParseMethod = bool ( CPrimitiveTemplate::* )( const gsl::cstring_view& );
|
||
|
// Group type processing
|
||
|
bool ParseGroup( const CGPGroup& grp, const StringViewIMap< ParseMethod >& parseMethods, gsl::czstring name );
|
||
|
bool ParseRGB( const CGPGroup& grp );
|
||
|
bool ParseAlpha( const CGPGroup& grp );
|
||
|
bool ParseSize( const CGPGroup& grp );
|
||
|
bool ParseSize2( const CGPGroup& grp );
|
||
|
bool ParseLength( const CGPGroup& grp );
|
||
|
|
||
|
bool ParseModels( const CGPProperty& grp );
|
||
|
bool ParseShaders( const CGPProperty& grp );
|
||
|
bool ParseSounds( const CGPProperty& grp );
|
||
|
|
||
|
bool ParseImpactFxStrings( const CGPProperty& grp );
|
||
|
bool ParseDeathFxStrings( const CGPProperty& grp );
|
||
|
bool ParseEmitterFxStrings( const CGPProperty& grp );
|
||
|
bool ParsePlayFxStrings( const CGPProperty& grp );
|
||
|
|
||
|
// Group keys
|
||
|
bool ParseRGBStart( const gsl::cstring_view& val );
|
||
|
bool ParseRGBEnd( const gsl::cstring_view& val );
|
||
|
bool ParseRGBParm( const gsl::cstring_view& val );
|
||
|
bool ParseRGBFlags( const gsl::cstring_view& val );
|
||
|
|
||
|
bool ParseAlphaStart( const gsl::cstring_view& val );
|
||
|
bool ParseAlphaEnd( const gsl::cstring_view& val );
|
||
|
bool ParseAlphaParm( const gsl::cstring_view& val );
|
||
|
bool ParseAlphaFlags( const gsl::cstring_view& val );
|
||
|
|
||
|
bool ParseSizeStart( const gsl::cstring_view& val );
|
||
|
bool ParseSizeEnd( const gsl::cstring_view& val );
|
||
|
bool ParseSizeParm( const gsl::cstring_view& val );
|
||
|
bool ParseSizeFlags( const gsl::cstring_view& val );
|
||
|
|
||
|
bool ParseSize2Start( const gsl::cstring_view& val );
|
||
|
bool ParseSize2End( const gsl::cstring_view& val );
|
||
|
bool ParseSize2Parm( const gsl::cstring_view& val );
|
||
|
bool ParseSize2Flags( const gsl::cstring_view& val );
|
||
|
|
||
|
bool ParseLengthStart( const gsl::cstring_view& val );
|
||
|
bool ParseLengthEnd( const gsl::cstring_view& val );
|
||
|
bool ParseLengthParm( const gsl::cstring_view& val );
|
||
|
bool ParseLengthFlags( const gsl::cstring_view& val );
|
||
|
|
||
|
|
||
|
public:
|
||
|
|
||
|
CPrimitiveTemplate();
|
||
|
CPrimitiveTemplate( const CPrimitiveTemplate& rhs );
|
||
|
~CPrimitiveTemplate() {};
|
||
|
|
||
|
bool ParsePrimitive( const CGPGroup& grp );
|
||
|
|
||
|
void operator=(const CPrimitiveTemplate &that);
|
||
|
};
|
||
|
|
||
|
// forward declaration
|
||
|
struct SEffectTemplate;
|
||
|
|
||
|
// Effects are built of one or more primitives
|
||
|
struct SEffectTemplate
|
||
|
{
|
||
|
bool mInUse;
|
||
|
bool mCopy;
|
||
|
char mEffectName[MAX_QPATH]; // is this extraneous??
|
||
|
int mPrimitiveCount;
|
||
|
int mRepeatDelay;
|
||
|
CPrimitiveTemplate *mPrimitives[FX_MAX_EFFECT_COMPONENTS];
|
||
|
|
||
|
bool operator == (const char * name) const
|
||
|
{
|
||
|
return !Q_stricmp( mEffectName, name );
|
||
|
}
|
||
|
void operator=(const SEffectTemplate &that);
|
||
|
};
|
||
|
|
||
|
template<typename T, int N>
|
||
|
class PoolAllocator
|
||
|
{
|
||
|
public:
|
||
|
PoolAllocator()
|
||
|
: pool (new T[N])
|
||
|
, freeAndAllocated (new int[N])
|
||
|
, numFree (N)
|
||
|
, highWatermark (0)
|
||
|
{
|
||
|
for ( int i = 0; i < N; i++ )
|
||
|
{
|
||
|
freeAndAllocated[i] = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
T *Alloc()
|
||
|
{
|
||
|
if ( numFree == 0 )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
T *ptr = new (&pool[freeAndAllocated[0]]) T;
|
||
|
|
||
|
std::rotate (freeAndAllocated, freeAndAllocated + 1, freeAndAllocated + N);
|
||
|
numFree--;
|
||
|
|
||
|
highWatermark = Q_max(highWatermark, N - numFree);
|
||
|
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
void TransferTo ( PoolAllocator<T, N>& allocator )
|
||
|
{
|
||
|
allocator.freeAndAllocated = freeAndAllocated;
|
||
|
allocator.highWatermark = highWatermark;
|
||
|
allocator.numFree = numFree;
|
||
|
allocator.pool = pool;
|
||
|
|
||
|
highWatermark = 0;
|
||
|
numFree = N;
|
||
|
freeAndAllocated = NULL;
|
||
|
pool = NULL;
|
||
|
}
|
||
|
|
||
|
bool OwnsPtr ( const T *ptr ) const
|
||
|
{
|
||
|
return ptr >= pool && ptr < (pool + N);
|
||
|
}
|
||
|
|
||
|
void Free ( T *ptr )
|
||
|
{
|
||
|
for ( int i = numFree; i < N; i++ )
|
||
|
{
|
||
|
T *p = &pool[freeAndAllocated[i]];
|
||
|
|
||
|
if ( p == ptr )
|
||
|
{
|
||
|
if ( i > numFree )
|
||
|
{
|
||
|
std::rotate (freeAndAllocated + numFree, freeAndAllocated + i, freeAndAllocated + i + 1);
|
||
|
}
|
||
|
|
||
|
p->~T();
|
||
|
numFree++;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GetHighWatermark() const { return highWatermark; }
|
||
|
|
||
|
~PoolAllocator()
|
||
|
{
|
||
|
for ( int i = numFree; i < N; i++ )
|
||
|
{
|
||
|
T *p = &pool[freeAndAllocated[i]];
|
||
|
|
||
|
p->~T();
|
||
|
}
|
||
|
|
||
|
delete [] freeAndAllocated;
|
||
|
delete [] pool;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
PoolAllocator ( const PoolAllocator<T, N>& );
|
||
|
PoolAllocator& operator = ( const PoolAllocator<T, N>& );
|
||
|
|
||
|
T *pool;
|
||
|
|
||
|
// The first 'numFree' elements are the indexes of the free slots.
|
||
|
// The remaining elements are the indexes of the allocated slots.
|
||
|
int *freeAndAllocated;
|
||
|
int numFree;
|
||
|
|
||
|
int highWatermark;
|
||
|
};
|
||
|
|
||
|
template<typename T, int N>
|
||
|
class PagedPoolAllocator
|
||
|
{
|
||
|
public:
|
||
|
PagedPoolAllocator ()
|
||
|
: numPages (1)
|
||
|
, pages (new PoolAllocator<T, N>[1]())
|
||
|
{
|
||
|
}
|
||
|
|
||
|
T *Alloc ()
|
||
|
{
|
||
|
T *ptr = NULL;
|
||
|
for ( int i = 0; i < numPages && ptr == NULL; i++ )
|
||
|
{
|
||
|
ptr = pages[i].Alloc ();
|
||
|
}
|
||
|
|
||
|
if ( ptr == NULL )
|
||
|
{
|
||
|
PoolAllocator<T, N> *newPages = new PoolAllocator<T, N>[numPages + 1] ();
|
||
|
for ( int i = 0; i < numPages; i++ )
|
||
|
{
|
||
|
pages[i].TransferTo (newPages[i]);
|
||
|
}
|
||
|
|
||
|
delete[] pages;
|
||
|
pages = newPages;
|
||
|
|
||
|
ptr = pages[numPages].Alloc ();
|
||
|
if ( ptr == NULL )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
numPages++;
|
||
|
}
|
||
|
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
void Free ( T *ptr )
|
||
|
{
|
||
|
for ( int i = 0; i < numPages; i++ )
|
||
|
{
|
||
|
if ( pages[i].OwnsPtr (ptr) )
|
||
|
{
|
||
|
pages[i].Free (ptr);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GetHighWatermark () const
|
||
|
{
|
||
|
int total = 0;
|
||
|
for ( int i = 0; i < numPages; i++ )
|
||
|
{
|
||
|
total += pages[i].GetHighWatermark ();
|
||
|
}
|
||
|
|
||
|
return total;
|
||
|
}
|
||
|
|
||
|
~PagedPoolAllocator ()
|
||
|
{
|
||
|
delete[] pages;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int numPages;
|
||
|
PoolAllocator<T, N> *pages;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------
|
||
|
//
|
||
|
// CFxScheduler
|
||
|
//
|
||
|
// The scheduler not only handles requests to play an effect, it
|
||
|
// tracks the request throughout its life if necessary, creating
|
||
|
// any of the delayed components as needed.
|
||
|
//
|
||
|
//-----------------------------------------------------------------
|
||
|
// needs to be in global space now (loadsave stuff)
|
||
|
|
||
|
#define MAX_LOOPED_FX 32
|
||
|
// We hold a looped effect here
|
||
|
struct SLoopedEffect
|
||
|
{
|
||
|
int mId; // effect id
|
||
|
int mBoltInfo; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
|
||
|
int mNextTime; //time to render again
|
||
|
int mLoopStopTime; //time to die
|
||
|
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
|
||
|
bool mIsRelative; // bolt this puppy on keep it updated
|
||
|
|
||
|
|
||
|
void sg_export(
|
||
|
ojk::SavedGameHelper& saved_game) const
|
||
|
{
|
||
|
saved_game.write<int32_t>(mId);
|
||
|
saved_game.write<int32_t>(mBoltInfo);
|
||
|
saved_game.write<int32_t>(mNextTime);
|
||
|
saved_game.write<int32_t>(mLoopStopTime);
|
||
|
saved_game.write<int8_t>(mPortalEffect);
|
||
|
saved_game.write<int8_t>(mIsRelative);
|
||
|
saved_game.skip(2);
|
||
|
}
|
||
|
|
||
|
void sg_import(
|
||
|
ojk::SavedGameHelper& saved_game)
|
||
|
{
|
||
|
saved_game.read<int32_t>(mId);
|
||
|
saved_game.read<int32_t>(mBoltInfo);
|
||
|
saved_game.read<int32_t>(mNextTime);
|
||
|
saved_game.read<int32_t>(mLoopStopTime);
|
||
|
saved_game.read<int8_t>(mPortalEffect);
|
||
|
saved_game.read<int8_t>(mIsRelative);
|
||
|
saved_game.skip(2);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CFxScheduler
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
// We hold a scheduled effect here
|
||
|
struct SScheduledEffect
|
||
|
{
|
||
|
CPrimitiveTemplate *mpTemplate; // primitive template
|
||
|
int mStartTime;
|
||
|
char mModelNum; // uset to determine which ghoul2 model we want to bolt this effect to
|
||
|
char mBoltNum; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
|
||
|
short mEntNum; // used to determine which entity this ghoul model is attached to.
|
||
|
short mClientID; // FIXME: redundant. this is used for muzzle bolts, merge into normal bolting
|
||
|
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
|
||
|
bool mIsRelative; // bolt this puppy on keep it updated
|
||
|
vec3_t mOrigin;
|
||
|
vec3_t mAxis[3];
|
||
|
};
|
||
|
|
||
|
/* Looped Effects get stored and reschedule at mRepeatRate */
|
||
|
|
||
|
// must be in sync with gLoopedEffectArray[MAX_LOOPED_FX]!
|
||
|
//
|
||
|
SLoopedEffect mLoopedEffectArray[MAX_LOOPED_FX];
|
||
|
|
||
|
int ScheduleLoopedEffect( int id, int boltInfo, bool isPortal, int iLoopTime, bool isRelative );
|
||
|
void AddLoopedEffects( );
|
||
|
|
||
|
|
||
|
// this makes looking up the index based on the string name much easier
|
||
|
typedef std::map<fxString_t, int> TEffectID;
|
||
|
|
||
|
typedef std::list<SScheduledEffect*> TScheduledEffect;
|
||
|
|
||
|
// Effects
|
||
|
SEffectTemplate mEffectTemplates[FX_MAX_EFFECTS];
|
||
|
TEffectID mEffectIDs; // if you only have the unique effect name, you'll have to use this to get the ID.
|
||
|
|
||
|
// List of scheduled effects that will need to be created at the correct time.
|
||
|
TScheduledEffect mFxSchedule;
|
||
|
|
||
|
PagedPoolAllocator<SScheduledEffect, 1024> mScheduledEffectsPool;
|
||
|
|
||
|
// Private function prototypes
|
||
|
SEffectTemplate *GetNewEffectTemplate( int *id, const char *file );
|
||
|
|
||
|
void AddPrimitiveToEffect( SEffectTemplate *fx, CPrimitiveTemplate *prim );
|
||
|
int ParseEffect( const char *file, const CGPGroup& base );
|
||
|
|
||
|
void CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, vec3_t axis[3], int lateTime, int clientID = -1, int modelNum = -1, int boltNum = -1 );
|
||
|
void CreateEffect( CPrimitiveTemplate *fx, int clientID, int lateTime );
|
||
|
|
||
|
public:
|
||
|
|
||
|
CFxScheduler();
|
||
|
|
||
|
void LoadSave_Read();
|
||
|
void LoadSave_Write();
|
||
|
void FX_CopeWithAnyLoadedSaveGames();
|
||
|
|
||
|
int RegisterEffect( const char *file, bool bHasCorrectPath = false ); // handles pre-caching
|
||
|
|
||
|
|
||
|
// Nasty overloaded madness
|
||
|
void PlayEffect( int id, vec3_t org, bool isPortal = false ); // uses a default up axis
|
||
|
void PlayEffect( int id, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
|
||
|
void PlayEffect( int id, vec3_t origin, vec3_t axis[3], const int boltInfo=-1, const int entNum=-1, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
|
||
|
void PlayEffect( const char *file, vec3_t org, bool isPortal = false ); // uses a default up axis
|
||
|
void PlayEffect( const char *file, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
|
||
|
void PlayEffect( const char *file, vec3_t origin, vec3_t axis[3], const int boltInfo, const int entNum, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
|
||
|
|
||
|
//for muzzle
|
||
|
void PlayEffect( const char *file, int clientID, bool isPortal = false );
|
||
|
|
||
|
void StopEffect( const char *file, const int boltInfo, bool isPortal = false ); //find a scheduled Looping effect with these parms and kill it
|
||
|
|
||
|
void AddScheduledEffects( bool portal ); // call once per CGame frame [rww ammendment - twice now actually, but first only renders portal effects]
|
||
|
|
||
|
int NumScheduledFx() { return (int)mFxSchedule.size(); }
|
||
|
void Clean(bool bRemoveTemplates = true, int idToPreserve = 0); // clean out the system
|
||
|
|
||
|
// FX Override functions
|
||
|
SEffectTemplate *GetEffectCopy( int fxHandle, int *newHandle );
|
||
|
SEffectTemplate *GetEffectCopy( const char *file, int *newHandle );
|
||
|
|
||
|
CPrimitiveTemplate *GetPrimitiveCopy( SEffectTemplate *effectCopy, const char *componentName );
|
||
|
};
|
||
|
|
||
|
//-------------------
|
||
|
// The one and only
|
||
|
//-------------------
|
||
|
extern CFxScheduler theFxScheduler;
|
||
|
|
||
|
|
||
|
#endif // FX_SCHEDULER_H_INC
|