jkxr/Projects/Android/jni/OpenJK/codeJK2/cgame/FxTemplate.cpp

1844 lines
47 KiB
C++
Raw Normal View History

/*
===========================================================================
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_SCHEDULER_H_INC)
#include "FxScheduler.h"
#endif
#include "../game/genericparser2.h"
#include "qcommon/safe/string.h"
#include <array>
//------------------------------------------------------
// CPrimitiveTemplate
// Set up our minimal default values
//
// Input:
// none
//
// Return:
// none
//------------------------------------------------------
CPrimitiveTemplate::CPrimitiveTemplate()
{
// We never start out as a copy or with a name
mCopy = false;
mName[0] = 0;
mCullRange = 0;
mFlags = mSpawnFlags = 0;
mLife.SetRange( 1.0f, 1.0f );
mSpawnCount.SetRange( 1.0f, 1.0f );
mRadius.SetRange( 1.0f, 1.0f );
mHeight.SetRange( 1.0f, 1.0f );
VectorSet( mMin, 0.0f, 0.0f, 0.0f );
VectorSet( mMax, 0.0f, 0.0f, 0.0f );
mRedStart.SetRange( 1.0f, 1.0f );
mGreenStart.SetRange( 1.0f, 1.0f );
mBlueStart.SetRange( 1.0f, 1.0f );
mRedEnd.SetRange( 1.0f, 1.0f );
mGreenEnd.SetRange( 1.0f, 1.0f );
mBlueEnd.SetRange( 1.0f, 1.0f );
mAlphaStart.SetRange( 1.0f, 1.0f );
mAlphaEnd.SetRange( 1.0f, 1.0f );
mSizeStart.SetRange( 1.0f, 1.0f );
mSizeEnd.SetRange( 1.0f, 1.0f );
mSize2Start.SetRange( 1.0f, 1.0f );
mSize2End.SetRange( 1.0f, 1.0f );
mLengthStart.SetRange( 1.0f, 1.0f );
mLengthEnd.SetRange( 1.0f, 1.0f );
mTexCoordS.SetRange( 1.0f, 1.0f );
mTexCoordT.SetRange( 1.0f, 1.0f );
mVariance.SetRange( 1.0f, 1.0f );
mDensity.SetRange( 10.0f, 10.0f );// default this high so it doesn't do bad things
}
//-----------------------------------------------------------
void CPrimitiveTemplate::operator=(const CPrimitiveTemplate &that)
{
// I'm assuming that doing a memcpy wouldn't work here
// If you are looking at this and know a better way to do this, please tell me.
Q_strncpyz( mName, that.mName, sizeof(mName) );
mType = that.mType;
mSpawnDelay = that.mSpawnDelay;
mSpawnCount = that.mSpawnCount;
mLife = that.mLife;
mCullRange = that.mCullRange;
mMediaHandles = that.mMediaHandles;
mImpactFxHandles = that.mImpactFxHandles;
mDeathFxHandles = that.mDeathFxHandles;
mEmitterFxHandles = that.mEmitterFxHandles;
mPlayFxHandles = that.mPlayFxHandles;
mFlags = that.mFlags;
mSpawnFlags = that.mSpawnFlags;
VectorCopy( that.mMin, mMin );
VectorCopy( that.mMax, mMax );
mOrigin1X = that.mOrigin1X;
mOrigin1Y = that.mOrigin1Y;
mOrigin1Z = that.mOrigin1Z;
mOrigin2X = that.mOrigin2X;
mOrigin2Y = that.mOrigin2Y;
mOrigin2Z = that.mOrigin2Z;
mRadius = that.mRadius;
mHeight = that.mHeight;
mRotation = that.mRotation;
mRotationDelta = that.mRotationDelta;
mAngle1 = that.mAngle1;
mAngle2 = that.mAngle2;
mAngle3 = that.mAngle3;
mAngle1Delta = that.mAngle1Delta;
mAngle2Delta = that.mAngle2Delta;
mAngle3Delta = that.mAngle3Delta;
mVelX = that.mVelX;
mVelY = that.mVelY;
mVelZ = that.mVelZ;
mAccelX = that.mAccelX;
mAccelY = that.mAccelY;
mAccelZ = that.mAccelZ;
mGravity = that.mGravity;
mDensity = that.mDensity;
mVariance = that.mVariance;
mRedStart = that.mRedStart;
mGreenStart = that.mGreenStart;
mBlueStart = that.mBlueStart;
mRedEnd = that.mRedEnd;
mGreenEnd = that.mGreenEnd;
mBlueEnd = that.mBlueEnd;
mRGBParm = that.mRGBParm;
mAlphaStart = that.mAlphaStart;
mAlphaEnd = that.mAlphaEnd;
mAlphaParm = that.mAlphaParm;
mSizeStart = that.mSizeStart;
mSizeEnd = that.mSizeEnd;
mSizeParm = that.mSizeParm;
mSize2Start = that.mSize2Start;
mSize2End = that.mSize2End;
mSize2Parm = that.mSize2Parm;
mLengthStart = that.mLengthStart;
mLengthEnd = that.mLengthEnd;
mLengthParm = that.mLengthParm;
mTexCoordS = that.mTexCoordS;
mTexCoordT = that.mTexCoordT;
mElasticity = that.mElasticity;
}
//------------------------------------------------------
// ParseFloat
// Removes up to two values from a passed in string and
// sets these values into the passed in min and max
// fields. if no max is present, min is copied into it.
//
// input:
// string that contains up to two float values
// min & max are used to return the parse values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseFloat( const gsl::cstring_view& val, float& min, float& max )
{
// attempt to read out the values
int v = Q::sscanf( val, min, max );
if ( v == 0 )
{ // nothing was there, failure
return false;
}
else if ( v == 1 )
{ // only one field entered, this is ok, but we should copy min into max
max = min;
}
return true;
}
//------------------------------------------------------
// ParseVector
// Removes up to six values from a passed in string and
// sets these values into the passed in min and max vector
// fields. if no max is present, min is copied into it.
//
// input:
// string that contains up to six float values
// min & max are used to return the parse values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseVector( const gsl::cstring_view& val, vec3_t min, vec3_t max )
{
// we don't allow passing in a null
if ( min == nullptr || max == nullptr )
{
return false;
}
// attempt to read out our values
int v = Q::sscanf( val, min[0], min[1], min[2], max[0], max[1], max[2] );
// Check for completeness
if ( v < 3 || v == 4 || v == 5 )
{ // not a complete value
return false;
}
else if ( v == 3 )
{ // only a min was entered, so copy the result into max
VectorCopy( min, max );
}
return true;
}
namespace detail
{
// calls Q::sscanf with the elements of the given array as arguments
template< std::size_t remaining >
struct ScanStrings
{
template< std::size_t count, typename... Args >
static int call( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr, Args&... args )
{
return ScanStrings< remaining - 1 >::call( val, arr, arr[ remaining - 1 ], args... );
}
};
template<>
struct ScanStrings< 0 >
{
template< std::size_t count, typename... Args >
static int call( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr, Args&... args )
{
return Q::sscanf( val, args... );
}
};
}
template< std::size_t count >
static gsl::array_view< gsl::cstring_view > scanStrings( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr )
{
int numParsed = detail::ScanStrings< count >::call( val, arr );
return{ arr.data(), arr.data() + numParsed };
}
//------------------------------------------------------
// ParseGroupFlags
// Group flags are generic in nature, so we can easily
// use a generic function to parse them in, then the
// caller can shift them into the appropriate range.
//
// input:
// string that contains the flag strings
// *flags returns the set bit flags
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseGroupFlags( const gsl::cstring_view& val, int& flags )
{
// For a sub group, really you probably only have one or two flags set
std::array< gsl::cstring_view, 4 > flag;
const auto availableFlag = scanStrings( val, flag );
// Clear out the flags field, then convert the flag string to an actual value ( use generic flags )
flags = 0;
bool ok = true;
for( auto& cur : availableFlag )
{
static StringViewIMap< int > flagNames{
{ CSTRING_VIEW( "linear" ), FX_LINEAR },
{ CSTRING_VIEW( "nonlinear" ), FX_NONLINEAR },
{ CSTRING_VIEW( "wave" ), FX_WAVE },
{ CSTRING_VIEW( "random" ), FX_RAND },
{ CSTRING_VIEW( "clamp" ), FX_CLAMP },
};
auto pos = flagNames.find( cur );
if( pos == flagNames.end() )
{
ok = false;
}
else
{
flags |= pos->second;
}
}
return ok;
}
//------------------------------------------------------
// ParseMin
// Reads in a min bounding box field in vector format
//
// input:
// string that contains three float values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseMin( const gsl::cstring_view& val )
{
vec3_t min;
if ( ParseVector( val, min, min ) == true )
{
VectorCopy( min, mMin );
// We assume that if a min is being set that we are using physics and a bounding box
mFlags |= (FX_USE_BBOX | FX_APPLY_PHYSICS);
return true;
}
return false;
}
//------------------------------------------------------
// ParseMax
// Reads in a max bounding box field in vector format
//
// input:
// string that contains three float values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseMax( const gsl::cstring_view& val )
{
vec3_t max;
if ( ParseVector( val, max, max ) == true )
{
VectorCopy( max, mMax );
// We assume that if a max is being set that we are using physics and a bounding box
mFlags |= (FX_USE_BBOX | FX_APPLY_PHYSICS);
return true;
}
return false;
}
//------------------------------------------------------
// ParseLife
// Reads in a ranged life value
//
// input:
// string that contains a float range ( two vals )
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLife( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mLife.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseDelay
// Reads in a ranged delay value
//
// input:
// string that contains a float range ( two vals )
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseDelay( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSpawnDelay.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseCount
// Reads in a ranged count value
//
// input:
// string that contains a float range ( two vals )
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseCount( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSpawnCount.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseElasticity
// Reads in a ranged elasticity value
//
// input:
// string that contains a float range ( two vals )
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseElasticity( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mElasticity.SetRange( min, max );
// We assume that if elasticity is set that we are using physics, but don't assume we are
// using a bounding box unless a min/max are explicitly set
// mFlags |= FX_APPLY_PHYSICS;
return true;
}
return false;
}
//------------------------------------------------------
// ParseOrigin1
// Reads in an origin field in vector format
//
// input:
// string that contains three float values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseOrigin1( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mOrigin1X.SetRange( min[0], max[0] );
mOrigin1Y.SetRange( min[1], max[1] );
mOrigin1Z.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseOrigin2
// Reads in an origin field in vector format
//
// input:
// string that contains three float values
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseOrigin2( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mOrigin2X.SetRange( min[0], max[0] );
mOrigin2Y.SetRange( min[1], max[1] );
mOrigin2Z.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRadius
// Reads in a ranged radius value
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRadius( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mRadius.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseHeight
// Reads in a ranged height value
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseHeight( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mHeight.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRotation
// Reads in a ranged rotation value
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRotation( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == qtrue )
{
mRotation.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRotationDelta
// Reads in a ranged rotationDelta value
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRotationDelta( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == qtrue )
{
mRotationDelta.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAngle
// Reads in a ranged angle field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAngle( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mAngle1.SetRange( min[0], max[0] );
mAngle2.SetRange( min[1], max[1] );
mAngle3.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAngleDelta
// Reads in a ranged angleDelta field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAngleDelta( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mAngle1Delta.SetRange( min[0], max[0] );
mAngle2Delta.SetRange( min[1], max[1] );
mAngle3Delta.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseVelocity
// Reads in a ranged velocity field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseVelocity( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mVelX.SetRange( min[0], max[0] );
mVelY.SetRange( min[1], max[1] );
mVelZ.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseFlags
// These are flags that are not specific to a group,
// rather, they are specific to the whole primitive.
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseFlags( const gsl::cstring_view& val )
{
// For a primitive, really you probably only have two or less flags set
std::array< gsl::cstring_view, 7 > flag;
const auto availableFlag = scanStrings( val, flag );
bool ok = true;
for( auto& cur : availableFlag )
{
static StringViewIMap< int > flagNames{
{ CSTRING_VIEW( "useModel" ), FX_ATTACHED_MODEL },
{ CSTRING_VIEW( "useBBox" ), FX_USE_BBOX },
{ CSTRING_VIEW( "usePhysics" ), FX_APPLY_PHYSICS },
{ CSTRING_VIEW( "expensivePhysics" ), FX_EXPENSIVE_PHYSICS },
{ CSTRING_VIEW( "impactKills" ), FX_KILL_ON_IMPACT },
{ CSTRING_VIEW( "impactFx" ), FX_IMPACT_RUNS_FX },
{ CSTRING_VIEW( "deathFx" ), FX_DEATH_RUNS_FX },
{ CSTRING_VIEW( "useAlpha" ), FX_USE_ALPHA },
{ CSTRING_VIEW( "emitFx" ), FX_EMIT_FX },
{ CSTRING_VIEW( "depthHack" ), FX_DEPTH_HACK },
{ CSTRING_VIEW( "setShaderTime" ), FX_SET_SHADER_TIME },
};
auto pos = flagNames.find( cur );
if( pos == flagNames.end() )
{ // we have badness going on, but continue on in case there are any valid fields in here
ok = false;
}
else
{
mFlags |= pos->second;
}
}
return ok;
}
//------------------------------------------------------
// ParseSpawnFlags
// These kinds of flags control how things spawn. They
// never get passed on to a primitive.
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSpawnFlags( const gsl::cstring_view& val )
{
std::array< gsl::cstring_view, 7 > flag;
// For a primitive, really you probably only have two or less flags set
const auto availableFlag = scanStrings( val, flag );
bool ok = true;
for( auto& cur : availableFlag )
{
static StringViewIMap< int > flagNames{
{ CSTRING_VIEW( "org2fromTrace" ), FX_ORG2_FROM_TRACE },
{ CSTRING_VIEW( "traceImpactFx" ), FX_TRACE_IMPACT_FX },
{ CSTRING_VIEW( "org2isOffset" ), FX_ORG2_IS_OFFSET },
{ CSTRING_VIEW( "cheapOrgCalc" ), FX_CHEAP_ORG_CALC },
{ CSTRING_VIEW( "cheapOrg2Calc" ), FX_CHEAP_ORG2_CALC },
{ CSTRING_VIEW( "absoluteVel" ), FX_VEL_IS_ABSOLUTE },
{ CSTRING_VIEW( "absoluteAccel" ), FX_ACCEL_IS_ABSOLUTE },
{ CSTRING_VIEW( "orgOnSphere" ), FX_ORG_ON_SPHERE },
{ CSTRING_VIEW( "orgOnCylinder" ), FX_ORG_ON_CYLINDER },
{ CSTRING_VIEW( "axisFromSphere" ), FX_AXIS_FROM_SPHERE },
{ CSTRING_VIEW( "randrotaroundfwd" ), FX_RAND_ROT_AROUND_FWD },
{ CSTRING_VIEW( "evenDistribution" ), FX_EVEN_DISTRIBUTION },
{ CSTRING_VIEW( "rgbComponentInterpolation" ), FX_RGB_COMPONENT_INTERP },
{ CSTRING_VIEW( "lessAttenuation" ), FX_SND_LESS_ATTENUATION },
};
auto pos = flagNames.find( cur );
if( pos == flagNames.end() )
{
ok = false;
}
else
{
mSpawnFlags |= pos->second;
}
}
return ok;
}
//------------------------------------------------------
// ParseAcceleration
// Reads in a ranged acceleration field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAcceleration( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mAccelX.SetRange( min[0], max[0] );
mAccelY.SetRange( min[1], max[1] );
mAccelZ.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseGravity
// Reads in a ranged gravity value
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseGravity( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mGravity.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseDensity
// Reads in a ranged density value. Density is only
// for emitters that are calling effects...it basically
// specifies how often the emitter should emit fx.
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseDensity( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mDensity.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseVariance
// Reads in a ranged variance value. Variance is only
// valid for emitters that are calling effects...
// it basically determines the amount of slop in the
// density calculations
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseVariance( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mVariance.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRGBStart
// Reads in a ranged rgbStart field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRGBStart( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mRedStart.SetRange( min[0], max[0] );
mGreenStart.SetRange( min[1], max[1] );
mBlueStart.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRGBEnd
// Reads in a ranged rgbEnd field in vector format
//
// input:
// string that contains one or two vectors
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRGBEnd( const gsl::cstring_view& val )
{
vec3_t min, max;
if ( ParseVector( val, min, max ) == true )
{
mRedEnd.SetRange( min[0], max[0] );
mGreenEnd.SetRange( min[1], max[1] );
mBlueEnd.SetRange( min[2], max[2] );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRGBParm
// Reads in a ranged rgbParm field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRGBParm( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mRGBParm.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseRGBFlags
// Reads in a set of rgbFlags in string format
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRGBFlags( const gsl::cstring_view& val )
{
int flags;
if ( ParseGroupFlags( val, flags ) == true )
{
// Convert our generic flag values into type specific ones
mFlags |= ( flags << FX_RGB_SHIFT );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAlphaStart
// Reads in a ranged alphaStart field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAlphaStart( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mAlphaStart.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAlphaEnd
// Reads in a ranged alphaEnd field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAlphaEnd( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mAlphaEnd.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAlphaParm
// Reads in a ranged alphaParm field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAlphaParm( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mAlphaParm.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseAlphaFlags
// Reads in a set of alphaFlags in string format
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAlphaFlags( const gsl::cstring_view& val )
{
int flags;
if ( ParseGroupFlags( val, flags ) == true )
{
// Convert our generic flag values into type specific ones
mFlags |= ( flags << FX_ALPHA_SHIFT );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSizeStart
// Reads in a ranged sizeStart field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSizeStart( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSizeStart.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSizeEnd
// Reads in a ranged sizeEnd field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSizeEnd( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSizeEnd.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSizeParm
// Reads in a ranged sizeParm field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSizeParm( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSizeParm.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSizeFlags
// Reads in a set of sizeFlags in string format
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSizeFlags( const gsl::cstring_view& val )
{
int flags;
if ( ParseGroupFlags( val, flags ) == true )
{
// Convert our generic flag values into type specific ones
mFlags |= ( flags << FX_SIZE_SHIFT );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSize2Start
// Reads in a ranged Size2Start field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize2Start( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSize2Start.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSize2End
// Reads in a ranged Size2End field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize2End( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSize2End.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSize2Parm
// Reads in a ranged Size2Parm field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize2Parm( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mSize2Parm.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseSize2Flags
// Reads in a set of Size2Flags in string format
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize2Flags( const gsl::cstring_view& val )
{
int flags;
if ( ParseGroupFlags( val, flags ) == true )
{
// Convert our generic flag values into type specific ones
mFlags |= ( flags << FX_SIZE2_SHIFT );
return true;
}
return false;
}
//------------------------------------------------------
// ParseLengthStart
// Reads in a ranged lengthStart field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLengthStart( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mLengthStart.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseLengthEnd
// Reads in a ranged lengthEnd field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLengthEnd( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mLengthEnd.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseLengthParm
// Reads in a ranged lengthParm field in float format
//
// input:
// string that contains one or two floats
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLengthParm( const gsl::cstring_view& val )
{
float min, max;
if ( ParseFloat( val, min, max ) == true )
{
mLengthParm.SetRange( min, max );
return true;
}
return false;
}
//------------------------------------------------------
// ParseLengthFlags
// Reads in a set of lengthFlags in string format
//
// input:
// string that contains the flag strings
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLengthFlags( const gsl::cstring_view& val )
{
int flags;
if ( ParseGroupFlags( val, flags ) == true )
{
// Convert our generic flag values into type specific ones
mFlags |= ( flags << FX_LENGTH_SHIFT );
return true;
}
return false;
}
//------------------------------------------------------
// ParseShaders
// Reads in a group of shaders and registers them
//
// input:
// Parse group that contains the list of shaders to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseShaders( const CGPProperty& grp )
{
bool any = false;
for( auto& value : grp.GetValues() )
{
if( !value.empty() )
{
any = true;
int handle = theFxHelper.RegisterShader( value );
mMediaHandles.AddHandle( handle );
}
}
if( !any )
{
// empty "list"
theFxHelper.Print( "CPrimitiveTemplate::ParseShaders called with an empty list!\n" );
return false;
}
return true;
}
//------------------------------------------------------
// ParseSounds
// Reads in a group of sounds and registers them
//
// input:
// Parse group that contains the list of sounds to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSounds( const CGPProperty& grp )
{
bool any = false;
for( auto& value : grp.GetValues() )
{
if( !value.empty() )
{
any = true;
int handle = theFxHelper.RegisterSound( value );
mMediaHandles.AddHandle( handle );
}
}
if( !any )
{
// empty "list"
theFxHelper.Print( "CPrimitiveTemplate::ParseSounds called with an empty list!\n" );
return false;
}
return true;
}
//------------------------------------------------------
// ParseModels
// Reads in a group of models and registers them
//
// input:
// Parse group that contains the list of models to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseModels( const CGPProperty& grp )
{
bool any = false;
for( auto& value : grp.GetValues() )
{
if( !value.empty() )
{
any = true;
int handle = theFxHelper.RegisterModel( value );
mMediaHandles.AddHandle( handle );
}
}
if( !any )
{
// empty "list"
theFxHelper.Print( "CPrimitiveTemplate::ParseModels called with an empty list!\n" );
return false;
}
mFlags |= FX_ATTACHED_MODEL;
return true;
}
static bool ParseFX( const CGPProperty& grp, CFxScheduler& scheduler, CMediaHandles& handles, SFxHelper& helper, int& flags, int successFlags, gsl::czstring loadError, gsl::czstring emptyError )
{
bool any = false;
for( auto& value : grp.GetValues() )
{
if( !value.empty() )
{
any = true;
// TODO: string_view parameter
int handle = scheduler.RegisterEffect( std::string( value.begin(), value.end() ).c_str() );
if( handle )
{
handles.AddHandle( handle );
flags |= successFlags;
}
else
{
helper.Print( "%s", loadError );
}
}
}
if( !any )
{
helper.Print( "%s", emptyError );
}
return any;
}
//------------------------------------------------------
// ParseImpactFxStrings
// Reads in a group of fx file names and registers them
//
// input:
// Parse group that contains the list of fx to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseImpactFxStrings( const CGPProperty& grp )
{
return ParseFX(
grp,
theFxScheduler, mImpactFxHandles, theFxHelper,
mFlags, FX_IMPACT_RUNS_FX | FX_APPLY_PHYSICS,
"FxTemplate: Impact effect file not found.\n",
"CPrimitiveTemplate::ParseImpactFxStrings called with an empty list!\n"
);
}
//------------------------------------------------------
// ParseDeathFxStrings
// Reads in a group of fx file names and registers them
//
// input:
// Parse group that contains the list of fx to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseDeathFxStrings( const CGPProperty& grp )
{
return ParseFX(
grp,
theFxScheduler, mDeathFxHandles, theFxHelper,
mFlags, FX_DEATH_RUNS_FX,
"FxTemplate: Death effect file not found.\n",
"CPrimitiveTemplate::ParseDeathFxStrings called with an empty list!\n"
);
}
//------------------------------------------------------
// ParseEmitterFxStrings
// Reads in a group of fx file names and registers them
//
// input:
// Parse group that contains the list of fx to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseEmitterFxStrings( const CGPProperty& grp )
{
return ParseFX(
grp,
theFxScheduler, mEmitterFxHandles, theFxHelper,
mFlags, FX_EMIT_FX,
"FxTemplate: Emitter effect file not found.\n",
"CPrimitiveTemplate::ParseEmitterFxStrings called with an empty list!\n"
);
}
//------------------------------------------------------
// ParsePlayFxStrings
// Reads in a group of fx file names and registers them
//
// input:
// Parse group that contains the list of fx to parse
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParsePlayFxStrings( const CGPProperty& grp )
{
return ParseFX(
grp,
theFxScheduler, mPlayFxHandles, theFxHelper,
mFlags, 0,
"FxTemplate: Effect file not found.\n",
"CPrimitiveTemplate::ParsePlayFxStrings called with an empty list!\n"
);
}
bool CPrimitiveTemplate::ParseGroup( const CGPGroup& grp, const StringViewIMap< ParseMethod >& parseMethods, gsl::czstring name )
{
for( auto& cur : grp.GetProperties() )
{
auto pos = parseMethods.find( cur.GetName() );
if( pos == parseMethods.end() )
{
theFxHelper.Print( "Unknown key parsing %s group!", name );
}
else
{
ParseMethod method = pos->second;
( this->*method )( cur.GetTopValue() );
}
}
return true;
}
//------------------------------------------------------
// ParseRGB
// Takes an RGB group and chomps out any pairs contained
// in it.
//
// input:
// the parse group to process
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseRGB( const CGPGroup& grp )
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseRGBStart },
{ CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseRGBEnd },
{ CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseRGBParm },
{ CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseRGBParm },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseRGBFlags },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseRGBFlags },
};
return ParseGroup( grp, parseMethods, "RGB" );
}
//------------------------------------------------------
// ParseAlpha
// Takes an alpha group and chomps out any pairs contained
// in it.
//
// input:
// the parse group to process
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseAlpha( const CGPGroup& grp )
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseAlphaStart },
{ CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseAlphaEnd },
{ CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseAlphaParm },
{ CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseAlphaParm },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseAlphaFlags },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseAlphaFlags },
};
return ParseGroup( grp, parseMethods, "Alpha" );
}
//------------------------------------------------------
// ParseSize
// Takes a size group and chomps out any pairs contained
// in it.
//
// input:
// the parse group to process
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize( const CGPGroup& grp )
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseSizeStart },
{ CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseSizeEnd },
{ CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseSizeParm },
{ CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseSizeParm },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseSizeFlags },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseSizeFlags },
};
return ParseGroup( grp, parseMethods, "Size" );
}
//------------------------------------------------------
// ParseSize2
// Takes a Size2 group and chomps out any pairs contained
// in it.
//
// input:
// the parse group to process
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseSize2( const CGPGroup& grp )
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseSize2Start },
{ CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseSize2End },
{ CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseSize2Parm },
{ CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseSize2Parm },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseSize2Flags },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseSize2Flags },
};
return ParseGroup( grp, parseMethods, "Size2" );
}
//------------------------------------------------------
// ParseLength
// Takes a length group and chomps out any pairs contained
// in it.
//
// input:
// the parse group to process
//
// return:
// success of parse operation.
//------------------------------------------------------
bool CPrimitiveTemplate::ParseLength( const CGPGroup& grp )
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseLengthStart },
{ CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseLengthEnd },
{ CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseLengthParm },
{ CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseLengthParm },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseLengthFlags },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseLengthFlags },
};
return ParseGroup( grp, parseMethods, "Length" );
}
// Parse a primitive, apply defaults first, grab any base level
// key pairs, then process any sub groups we may contain.
//------------------------------------------------------
bool CPrimitiveTemplate::ParsePrimitive( const CGPGroup& grp )
{
// Property
for( auto& prop : grp.GetProperties() )
{
// Single Value Parsing
{
static StringViewIMap< ParseMethod > parseMethods{
{ CSTRING_VIEW( "count" ), &CPrimitiveTemplate::ParseCount },
{ CSTRING_VIEW( "life" ), &CPrimitiveTemplate::ParseLife },
{ CSTRING_VIEW( "delay" ), &CPrimitiveTemplate::ParseDelay },
{ CSTRING_VIEW( "bounce" ), &CPrimitiveTemplate::ParseElasticity },
{ CSTRING_VIEW( "intensity" ), &CPrimitiveTemplate::ParseElasticity },
{ CSTRING_VIEW( "min" ), &CPrimitiveTemplate::ParseMin },
{ CSTRING_VIEW( "max" ), &CPrimitiveTemplate::ParseMax },
{ CSTRING_VIEW( "angle" ), &CPrimitiveTemplate::ParseAngle },
{ CSTRING_VIEW( "angles" ), &CPrimitiveTemplate::ParseAngle },
{ CSTRING_VIEW( "angleDelta" ), &CPrimitiveTemplate::ParseAngleDelta },
{ CSTRING_VIEW( "velocity" ), &CPrimitiveTemplate::ParseVelocity },
{ CSTRING_VIEW( "vel" ), &CPrimitiveTemplate::ParseVelocity },
{ CSTRING_VIEW( "acceleration" ), &CPrimitiveTemplate::ParseAcceleration },
{ CSTRING_VIEW( "accel" ), &CPrimitiveTemplate::ParseAcceleration },
{ CSTRING_VIEW( "gravity" ), &CPrimitiveTemplate::ParseGravity },
{ CSTRING_VIEW( "density" ), &CPrimitiveTemplate::ParseDensity },
{ CSTRING_VIEW( "variance" ), &CPrimitiveTemplate::ParseVariance },
{ CSTRING_VIEW( "origin" ), &CPrimitiveTemplate::ParseOrigin1 },
{ CSTRING_VIEW( "origin2" ), &CPrimitiveTemplate::ParseOrigin2 },
{ CSTRING_VIEW( "radius" ), &CPrimitiveTemplate::ParseRadius },
{ CSTRING_VIEW( "height" ), &CPrimitiveTemplate::ParseHeight },
{ CSTRING_VIEW( "rotation" ), &CPrimitiveTemplate::ParseRotation },
{ CSTRING_VIEW( "rotationDelta" ), &CPrimitiveTemplate::ParseRotationDelta },
{ CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseFlags },
{ CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseFlags },
{ CSTRING_VIEW( "spawnFlags" ), &CPrimitiveTemplate::ParseSpawnFlags },
{ CSTRING_VIEW( "spawnFlag" ), &CPrimitiveTemplate::ParseSpawnFlags },
};
auto pos = parseMethods.find( prop.GetName() );
if( pos != parseMethods.end() )
{
ParseMethod method = pos->second;
( this->*method )( prop.GetTopValue() );
continue;
}
}
// Property Parsing
{
using PropertyParseMethod = bool( CPrimitiveTemplate::* )( const CGPProperty& );
static StringViewIMap< PropertyParseMethod > parseMethods{
{ CSTRING_VIEW( "shaders" ), &CPrimitiveTemplate::ParseShaders },
{ CSTRING_VIEW( "shader" ), &CPrimitiveTemplate::ParseShaders },
{ CSTRING_VIEW( "models" ), &CPrimitiveTemplate::ParseModels },
{ CSTRING_VIEW( "model" ), &CPrimitiveTemplate::ParseModels },
{ CSTRING_VIEW( "sounds" ), &CPrimitiveTemplate::ParseSounds },
{ CSTRING_VIEW( "sound" ), &CPrimitiveTemplate::ParseSounds },
{ CSTRING_VIEW( "impactfx" ), &CPrimitiveTemplate::ParseImpactFxStrings },
{ CSTRING_VIEW( "deathfx" ), &CPrimitiveTemplate::ParseDeathFxStrings },
{ CSTRING_VIEW( "emitfx" ), &CPrimitiveTemplate::ParseEmitterFxStrings },
{ CSTRING_VIEW( "playfx" ), &CPrimitiveTemplate::ParsePlayFxStrings },
};
auto pos = parseMethods.find( prop.GetName() );
if( pos != parseMethods.end() )
{
PropertyParseMethod method = pos->second;
( this->*method )( prop );
continue;
}
}
// Special Cases
if( Q::stricmp( prop.GetName(), CSTRING_VIEW( "cullrange" ) ) == Q::Ordering::EQ )
{
mCullRange = Q::svtoi( prop.GetTopValue() );
mCullRange *= mCullRange; // Square
}
else if( Q::stricmp( prop.GetName(), CSTRING_VIEW( "name" ) ) == Q::Ordering::EQ )
{
if( !prop.GetTopValue().empty() )
{
// just stash the descriptive name of the primitive
std::size_t len = std::min< std::size_t >( prop.GetTopValue().size(), FX_MAX_PRIM_NAME - 1 );
auto begin = prop.GetTopValue().begin();
std::copy( begin, begin + len, &mName[ 0 ] );
mName[ len ] = '\0';
}
}
// Error
else
{
theFxHelper.Print( "Unknown key parsing an effect primitive!\n" );
}
}
for( auto& subGrp : grp.GetSubGroups() )
{
using GroupParseMethod = bool ( CPrimitiveTemplate::* )( const CGPGroup& );
static StringViewIMap< GroupParseMethod > parseMethods{
{ CSTRING_VIEW( "rgb" ), &CPrimitiveTemplate::ParseRGB },
{ CSTRING_VIEW( "alpha" ), &CPrimitiveTemplate::ParseAlpha },
{ CSTRING_VIEW( "size" ), &CPrimitiveTemplate::ParseSize },
{ CSTRING_VIEW( "width" ), &CPrimitiveTemplate::ParseSize },
{ CSTRING_VIEW( "size2" ), &CPrimitiveTemplate::ParseSize2 },
{ CSTRING_VIEW( "width2" ), &CPrimitiveTemplate::ParseSize2 },
{ CSTRING_VIEW( "length" ), &CPrimitiveTemplate::ParseLength },
{ CSTRING_VIEW( "height" ), &CPrimitiveTemplate::ParseLength },
};
auto pos = parseMethods.find( subGrp.GetName() );
if( pos == parseMethods.end() )
{
theFxHelper.Print( "Unknown group key parsing a particle!\n" );
}
else
{
GroupParseMethod method = pos->second;
( this->*method )( subGrp );
}
}
return true;
}