| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** 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/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: FXList.cpp ///////////////////////////////////////////////////////////////////////////////
- // Author: Steven Johnson, December 2001
- // Desc: FXList descriptions
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "GameClient/FXList.h"
- #include "Common/DrawModule.h"
- #include "Common/GameAudio.h"
- #include "Common/INI.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/RandomValue.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/TerrainLogic.h"
- #include "GameClient/Display.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/ParticleSys.h"
- #include "GameLogic/PartitionManager.h"
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- FXListStore *TheFXListStore = NULL; ///< the FXList store definition
- //-------------------------------------------------------------------------------------------------
- static void adjustVector(Coord3D *vec, const Matrix3D* mtx)
- {
- if (mtx)
- {
- Vector3 vectmp;
- vectmp.X = vec->x;
- vectmp.Y = vec->y;
- vectmp.Z = vec->z;
- vectmp = mtx->Rotate_Vector(vectmp);
- vec->x = vectmp.X;
- vec->y = vectmp.Y;
- vec->z = vectmp.Z;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- void FXNugget::doFXObj(const Object* primary, const Object* secondary) const
- {
- const Coord3D* p = primary ? primary->getPosition() : NULL;
- const Matrix3D* mtx = primary ? primary->getTransformMatrix() : NULL;
- const Real speed = 0.0f; // yes, that's right -- NOT the object's speed.
- const Coord3D* s = secondary ? secondary->getPosition() : NULL;
- doFXPos(p, mtx, speed, s);
- }
- //-------------------------------------------------------------------------------------------------
- class SoundFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SoundFXNugget, "SoundFXNugget")
-
- public:
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
- {
- AudioEventRTS sound(m_soundName);
-
- if (primary)
- {
- sound.setPosition(primary);
- }
- TheAudio->addAudioEvent(&sound);
- }
- virtual void doFXObj(const Object* primary, const Object* secondary = NULL) const
- {
- AudioEventRTS sound(m_soundName);
- if (primary)
- {
- sound.setPlayerIndex(primary->getControllingPlayer()->getPlayerIndex());
- sound.setPosition(primary->getPosition());
- }
- TheAudio->addAudioEvent(&sound);
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Name", INI::parseAsciiString, NULL, offsetof( SoundFXNugget, m_soundName ) },
- { 0, 0, 0, 0 }
- };
- SoundFXNugget* nugget = newInstance(SoundFXNugget);
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- private:
- AsciiString m_soundName;
- };
- EMPTY_DTOR(SoundFXNugget)
- //-------------------------------------------------------------------------------------------------
- static Real calcDist(const Coord3D& src, const Coord3D& dst)
- {
- Real dx = dst.x - src.x;
- Real dy = dst.y - src.y;
- Real dz = dst.z - src.z;
- return sqrt(dx*dx + dy*dy + dz*dz);
- }
- //-------------------------------------------------------------------------------------------------
- class TracerFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TracerFXNugget, "TracerFXNugget")
- public:
- TracerFXNugget()
- {
- m_tracerName.set("GenericTracer");
- m_boneName.clear();
- m_speed = 0.0f; // means "use passed-in speed"
- m_decayAt = 1.0f;
- m_length = 10.0f;
- m_width = 1.0f;
- m_color.red = m_color.green = m_color.blue = 1.0f;
- m_probability = 1.0f;
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* primaryMtx, const Real primarySpeed, const Coord3D *secondary, const Real /*overrideRadius*/ ) const
- {
- if (m_probability <= GameClientRandomValueReal(0, 1))
- return;
- if (primary && secondary)
- {
- Drawable *tracer = TheThingFactory->newDrawable(TheThingFactory->findTemplate(m_tracerName));
- if(!tracer)
- return;
-
- //Kris -- Redid this section Sept 18, 2002
- //Calculate tracer orientations to face from primary to secondary position. This
- //should be the direction that the projectile is being fired towards. It doesn't make
- //sense that the old stuff made use of the muzzle fx bone orientation (because it's a
- //subobject). It had other problems because of elevation variations the tracers would
- //stay on the ground.
- //tracer->setTransformMatrix(primaryMtx);
- Matrix3D tracerMtx;
- Vector3 pos( primary->x, primary->y, primary->z );
- Vector3 dir( secondary->x - primary->x, secondary->y - primary->y, secondary->z - primary->z );
- dir.Normalize(); //This is fantastically crucial for calling buildTransformMatrix!!!!!
- tracerMtx.buildTransformMatrix( pos, dir );
- tracer->setTransformMatrix( &tracerMtx );
- tracer->setPosition(primary);
- Real speed = m_speed;
- if (speed == 0.0f)
- {
- speed = primarySpeed;
- }
- TracerDrawInterface* tdi = NULL;
- for (DrawModule** d = tracer->getDrawModules(); *d; ++d)
- {
- if ((tdi = (*d)->getTracerDrawInterface()) != NULL)
- {
- tdi->setTracerParms(speed, m_length, m_width, m_color, 1.0f);
- }
- }
- // estimate how long it will take us to get to the destination
- Real dist = calcDist(*primary, *secondary) - m_length;
- Real frames = (dist >= 0.0f && speed >= 0.0f) ? (dist / speed) : 1;
- Int framesAdjusted = REAL_TO_INT_CEIL(frames * m_decayAt);
- tracer->setExpirationDate(TheGameLogic->getFrame() + framesAdjusted);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "TracerName", INI::parseAsciiString, NULL, offsetof( TracerFXNugget, m_tracerName ) },
- { "BoneName", INI::parseAsciiString, NULL, offsetof( TracerFXNugget, m_boneName ) },
- { "Speed", INI::parseVelocityReal, NULL, offsetof( TracerFXNugget, m_speed ) },
- { "DecayAt", INI::parseReal, NULL, offsetof( TracerFXNugget, m_decayAt ) },
- { "Length", INI::parseReal, NULL, offsetof( TracerFXNugget, m_length ) },
- { "Width", INI::parseReal, NULL, offsetof( TracerFXNugget, m_width ) },
- { "Color", INI::parseRGBColor, NULL, offsetof( TracerFXNugget, m_color ) },
- { "Probability", INI::parseReal, NULL, offsetof( TracerFXNugget, m_probability ) },
- { 0, 0, 0, 0 }
- };
- TracerFXNugget* nugget = newInstance( TracerFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- private:
- AsciiString m_tracerName;
- AsciiString m_boneName;
- Real m_speed;
- Real m_decayAt;
- Real m_length;
- Real m_width;
- RGBColor m_color;
- Real m_probability;
- };
- EMPTY_DTOR(TracerFXNugget)
- //-------------------------------------------------------------------------------------------------
- class RayEffectFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(RayEffectFXNugget, "RayEffectFXNugget")
- public:
- RayEffectFXNugget()
- {
- m_templateName.clear();
- m_primaryOffset.x = m_primaryOffset.y = m_primaryOffset.z = 0;
- m_secondaryOffset.x = m_secondaryOffset.y = m_secondaryOffset.z = 0;
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * secondary, const Real /*overrideRadius*/ ) const
- {
- const ThingTemplate* tmpl = TheThingFactory->findTemplate(m_templateName);
- DEBUG_ASSERTCRASH(tmpl, ("RayEffect %s not found\n",m_templateName.str()));
- if (primary && secondary && tmpl)
- {
- Coord3D sourcePos = *primary;
- sourcePos.x += m_primaryOffset.x;
- sourcePos.y += m_primaryOffset.y;
- sourcePos.z += m_primaryOffset.z;
- Coord3D targetPos = *secondary;
- targetPos.x += m_secondaryOffset.x;
- targetPos.y += m_secondaryOffset.y;
- targetPos.z += m_secondaryOffset.z;
- TheGameClient->createRayEffectByTemplate(&sourcePos, &targetPos, tmpl);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary AND secondary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Name", INI::parseAsciiString, NULL, offsetof( RayEffectFXNugget, m_templateName ) },
- { "PrimaryOffset", INI::parseCoord3D, NULL, offsetof( RayEffectFXNugget, m_primaryOffset ) },
- { "SecondaryOffset", INI::parseCoord3D, NULL, offsetof( RayEffectFXNugget, m_secondaryOffset ) },
- { 0, 0, 0, 0 }
- };
- RayEffectFXNugget* nugget = newInstance( RayEffectFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- private:
- AsciiString m_templateName;
- Coord3D m_primaryOffset;
- Coord3D m_secondaryOffset;
- };
- EMPTY_DTOR(RayEffectFXNugget)
- //-------------------------------------------------------------------------------------------------
- class LightPulseFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(LightPulseFXNugget, "LightPulseFXNugget")
- public:
- LightPulseFXNugget() : m_radius(0), m_increaseFrames(0), m_decreaseFrames(0), m_boundingCirclePct(0)
- {
- m_color.red = m_color.green = m_color.blue = 0;
- }
- virtual void doFXObj(const Object* primary, const Object* /*secondary*/) const
- {
- if (primary)
- {
- Real radius = m_radius;
- if (m_boundingCirclePct > 0)
- radius = (primary->getGeometryInfo().getBoundingCircleRadius() * m_boundingCirclePct);
- TheDisplay->createLightPulse(primary->getPosition(), &m_color, 1, radius, m_increaseFrames, m_decreaseFrames);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
- {
- if (primary)
- {
- TheDisplay->createLightPulse(primary, &m_color, 1, m_radius, m_increaseFrames, m_decreaseFrames);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Color", INI::parseRGBColor, NULL, offsetof( LightPulseFXNugget, m_color ) },
- { "Radius", INI::parseReal, NULL, offsetof( LightPulseFXNugget, m_radius ) },
- { "RadiusAsPercentOfObjectSize", INI::parsePercentToReal, NULL, offsetof( LightPulseFXNugget, m_boundingCirclePct ) },
- { "IncreaseTime", INI::parseDurationUnsignedInt, NULL, offsetof( LightPulseFXNugget, m_increaseFrames ) },
- { "DecreaseTime", INI::parseDurationUnsignedInt, NULL, offsetof( LightPulseFXNugget, m_decreaseFrames ) },
- { 0, 0, 0, 0 }
- };
- LightPulseFXNugget* nugget = newInstance( LightPulseFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- private:
- RGBColor m_color;
- Real m_radius;
- Real m_boundingCirclePct;
- UnsignedInt m_increaseFrames;
- UnsignedInt m_decreaseFrames;
- };
- EMPTY_DTOR(LightPulseFXNugget)
- //-------------------------------------------------------------------------------------------------
- class ViewShakeFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ViewShakeFXNugget, "ViewShakeFXNugget")
- public:
- ViewShakeFXNugget() : m_shake(View::SHAKE_NORMAL)
- {
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
- {
- if (primary)
- {
- if (TheTacticalView)
- TheTacticalView->shake(primary, m_shake);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Type", parseShakeType, NULL, offsetof( ViewShakeFXNugget, m_shake ) },
- { 0, 0, 0, 0 }
- };
- ViewShakeFXNugget* nugget = newInstance( ViewShakeFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- protected:
- static void parseShakeType( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- static const LookupListRec shakeTypeNames[] =
- {
- { "SUBTLE", View::SHAKE_SUBTLE },
- { "NORMAL", View::SHAKE_NORMAL },
- { "STRONG", View::SHAKE_STRONG },
- { "SEVERE", View::SHAKE_SEVERE },
- { "CINE_EXTREME", View::SHAKE_CINE_EXTREME },
- { "CINE_INSANE", View::SHAKE_CINE_INSANE },
- { 0, 0 }
- };
- *(Int *)store = INI::scanLookupList(ini->getNextToken(), shakeTypeNames);
- }
- private:
- View::CameraShakeType m_shake;
- };
- EMPTY_DTOR(ViewShakeFXNugget)
- //-------------------------------------------------------------------------------------------------
- class TerrainScorchFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TerrainScorchFXNugget, "TerrainScorchFXNugget")
- public:
- TerrainScorchFXNugget() : m_scorch(-1), m_radius(0)
- {
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
- {
- if (primary)
- {
- Int scorch = m_scorch;
- if (scorch < 0)
- {
- scorch = GameClientRandomValue( SCORCH_1, SCORCH_4 );
- }
- TheGameClient->addScorch(primary, m_radius, (Scorches)scorch);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Type", parseScorchType, NULL, offsetof( TerrainScorchFXNugget, m_scorch ) },
- { "Radius", INI::parseReal, NULL, offsetof( TerrainScorchFXNugget, m_radius ) },
- { 0, 0, 0, 0 }
- };
- TerrainScorchFXNugget* nugget = newInstance( TerrainScorchFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- protected:
- static void parseScorchType( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- static const LookupListRec scorchTypeNames[] =
- {
- { "SCORCH_1", SCORCH_1 },
- { "SCORCH_2", SCORCH_2 },
- { "SCORCH_3", SCORCH_3 },
- { "SCORCH_4", SCORCH_4 },
- { "SHADOW_SCORCH", SHADOW_SCORCH },
- { "RANDOM", -1 },
- { 0, 0 }
- };
- *(Int *)store = INI::scanLookupList(ini->getNextToken(), scorchTypeNames);
- }
- private:
- Int m_scorch;
- Real m_radius;
- };
- EMPTY_DTOR(TerrainScorchFXNugget)
- //-------------------------------------------------------------------------------------------------
- class ParticleSystemFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ParticleSystemFXNugget, "ParticleSystemFXNugget")
- public:
- ParticleSystemFXNugget()
- {
- m_name.clear();
- m_count = 1;
- m_radius.setRange(0, 0, GameClientRandomVariable::CONSTANT);
- m_height.setRange(0, 0, GameClientRandomVariable::CONSTANT);
- // -1 means "don't mess with it, just accept the particle-system's defaults"
- m_delay.setRange(-1, -1, GameClientRandomVariable::CONSTANT);
- m_offset.x = m_offset.y = m_offset.z = 0;
- m_orientToObject = false;
- m_attachToObject = false;
- m_createAtGroundHeight = FALSE;
- m_useCallersRadius = FALSE;
- m_rotateX = m_rotateY = m_rotateZ = 0;
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* primaryMtx, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real overrideRadius ) const
- {
- if (primary)
- {
- reallyDoFX(primary, primaryMtx, NULL, overrideRadius);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- virtual void doFXObj(const Object* primary, const Object* secondary) const
- {
- if (primary)
- {
- if (m_ricochet && secondary)
- {
- // HERE WE MUST BUILD A MATRIX WHICH WILL ORIENT THE NEW PARTICLE SYSTEM TO FACE AWAY FROM THE SECONDARY OBJECT
- // THE RESULT SHOULD LOOK LIKE THE DIRECTION OF THE "ATTACK" IS CARRIED THROUGH LIKE A RICOCHET
- Real deltaX = primary->getPosition()->x - secondary->getPosition()->x;
- Real deltaY = primary->getPosition()->y - secondary->getPosition()->y;
- Real aimingAngle = atan2(deltaY, deltaX);
- Matrix3D aimingMatrix(1);
- aimingMatrix.Rotate_Z( aimingAngle );
- reallyDoFX(primary->getPosition(), &aimingMatrix, primary, 0.0f);
- }
- else
- // if we have an object, then adjust the offset and direction by the object's transformation
- // matrix, so that (say) an offset of +10 in the z axis "follows" the orientation of the object.
- reallyDoFX(primary->getPosition(), primary->getTransformMatrix(), primary, 0.0f);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Name", INI::parseAsciiString, NULL, offsetof( ParticleSystemFXNugget, m_name ) },
- { "Count", INI::parseInt, NULL, offsetof( ParticleSystemFXNugget, m_count ) },
- { "Offset", INI::parseCoord3D, NULL, offsetof( ParticleSystemFXNugget, m_offset ) },
- { "Radius", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemFXNugget, m_radius ) },
- { "Height", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemFXNugget, m_height ) },
- { "InitialDelay", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemFXNugget, m_delay ) },
- { "RotateX", INI::parseAngleReal, NULL, offsetof( ParticleSystemFXNugget, m_rotateX ) },
- { "RotateY", INI::parseAngleReal, NULL, offsetof( ParticleSystemFXNugget, m_rotateY ) },
- { "RotateZ", INI::parseAngleReal, NULL, offsetof( ParticleSystemFXNugget, m_rotateZ ) },
- { "OrientToObject", INI::parseBool, NULL, offsetof( ParticleSystemFXNugget, m_orientToObject ) },
- { "Ricochet", INI::parseBool, NULL, offsetof( ParticleSystemFXNugget, m_ricochet ) },
- { "AttachToObject", INI::parseBool, NULL, offsetof( ParticleSystemFXNugget, m_attachToObject ) },
- { "CreateAtGroundHeight", INI::parseBool, NULL, offsetof( ParticleSystemFXNugget, m_createAtGroundHeight ) },
- { "UseCallersRadius", INI::parseBool, NULL, offsetof( ParticleSystemFXNugget, m_useCallersRadius ) },
- { 0, 0, 0, 0 }
- };
- ParticleSystemFXNugget* nugget = newInstance( ParticleSystemFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- protected:
- void reallyDoFX(const Coord3D *primary, const Matrix3D* mtx, const Object* thingToAttachTo, Real overrideRadius ) const
- {
- Coord3D offset = m_offset;
- if (mtx)
- {
- adjustVector(&offset, mtx);
- }
- const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate(m_name);
- DEBUG_ASSERTCRASH(tmp, ("ParticleSystem %s not found\n",m_name.str()));
- if (tmp)
- {
- for (Int i = 0; i < m_count; i++ )
- {
- ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
- if (sys)
- {
- Coord3D newPos;
- Real radius = m_radius.getValue();
- Real angle = GameClientRandomValueReal( 0.0f, 2.0f * PI );
- newPos.x = primary->x + offset.x + radius * cos(angle);
- newPos.y = primary->y + offset.y + radius * sin(angle);
- if( m_createAtGroundHeight && TheTerrainLogic )
- {
- //old way:
- //newPos.z = TheTerrainLogic->getGrsoundHeight( newPos.x, newPos.y ) + 1;// The plus one prevents scissoring with terrain
-
- //new way: now we allow bridges in the GroundHeight.
- PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(&newPos);
- newPos.z = TheTerrainLogic->getLayerHeight( newPos.x, newPos.y, layer );
- }
- else
- newPos.z = primary->z + offset.z + m_height.getValue();
- if (m_orientToObject && mtx)
- {
- sys->setLocalTransform(mtx);
- }
- if (m_rotateX != 0.0f)
- sys->rotateLocalTransformX(m_rotateX);
- if (m_rotateY != 0.0f)
- sys->rotateLocalTransformY(m_rotateY);
- if (m_rotateZ != 0.0f)
- sys->rotateLocalTransformZ(m_rotateZ);
- if (m_attachToObject && thingToAttachTo)
- sys->attachToObject(thingToAttachTo);
- else
- sys->setPosition( &newPos );
- Real delayInMsec = m_delay.getValue();
- if (delayInMsec >= 0.0f)
- {
- UnsignedInt delayInFrames = REAL_TO_INT_CEIL(ConvertDurationFromMsecsToFrames(delayInMsec));
- sys->setInitialDelay(delayInFrames);
- }
- if( m_useCallersRadius && overrideRadius )
- {
- ParticleSystemInfo::EmissionVolumeType type = sys->getEmisionVolumeType();
- if( type == ParticleSystemInfo::EmissionVolumeType::SPHERE )
- sys->setEmissionVolumeSphereRadius( overrideRadius );
- else if( type == ParticleSystemInfo::EmissionVolumeType::CYLINDER )
- sys->setEmissionVolumeCylinderRadius( overrideRadius );
- }
- }
- }
- }
- }
- private:
- AsciiString m_name;
- Int m_count;
- Coord3D m_offset;
- GameClientRandomVariable m_radius;
- GameClientRandomVariable m_height;
- GameClientRandomVariable m_delay;
- Real m_rotateX, m_rotateY, m_rotateZ;
- Bool m_orientToObject;
- Bool m_attachToObject;
- Bool m_createAtGroundHeight;
- Bool m_useCallersRadius;
- Bool m_ricochet;
- };
- EMPTY_DTOR(ParticleSystemFXNugget)
- //-------------------------------------------------------------------------------------------------
- class FXListAtBonePosFXNugget : public FXNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(FXListAtBonePosFXNugget, "FXListAtBonePosFXNugget")
- public:
- FXListAtBonePosFXNugget()
- {
- m_fx = NULL;
- m_boneName.clear();
- m_orientToBone = true;
- }
- virtual void doFXPos(const Coord3D *primary, const Matrix3D* primaryMtx, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
- {
- DEBUG_CRASH(("You must use the object form for this effect"));
- }
- virtual void doFXObj(const Object* primary, const Object* /*secondary*/) const
- {
- if (primary)
- {
- // first, try the unadorned name.
- doFxAtBones(primary, 0);
- // then, try the 01,02,03...etc names.
- doFxAtBones(primary, 1);
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "FX", INI::parseFXList, NULL, offsetof( FXListAtBonePosFXNugget, m_fx ) },
- { "BoneName", INI::parseAsciiString, NULL, offsetof( FXListAtBonePosFXNugget, m_boneName ) },
- { "OrientToBone", INI::parseBool, NULL, offsetof( FXListAtBonePosFXNugget, m_orientToBone ) },
- { 0, 0, 0, 0 }
- };
- FXListAtBonePosFXNugget* nugget = newInstance( FXListAtBonePosFXNugget );
- ini->initFromINI(nugget, myFieldParse);
- ((FXList*)instance)->addFXNugget(nugget);
- }
- protected:
- void doFxAtBones(const Object* obj, Int start) const
- {
- Coord3D bonePos[MAX_BONE_POINTS];
- Matrix3D boneMtx[MAX_BONE_POINTS];
- Drawable* draw = obj->getDrawable();
- if (draw)
- {
- // yes, BONEPOS_CURRENT_CLIENT_ONLY -- this is client-only, so should be safe to do.
- Int count = draw->getCurrentClientBonePositions(m_boneName.str(), start, bonePos, boneMtx, MAX_BONE_POINTS);
- for (Int i = 0; i < count; ++i)
- {
- Coord3D p;
- Matrix3D m;
- obj->convertBonePosToWorldPos(&bonePos[i], &boneMtx[i], &p, &m);
- FXList::doFXPos(m_fx, &p, &m, 0.0f, NULL, 0.0f);
- }
- }
- }
- private:
- enum { MAX_BONE_POINTS = 40 };
- const FXList* m_fx;
- AsciiString m_boneName;
- Bool m_orientToBone;
- };
- EMPTY_DTOR(FXListAtBonePosFXNugget)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static const FieldParse TheFXListFieldParse[] =
- {
- { "Sound", SoundFXNugget::parse, 0, 0},
- { "RayEffect", RayEffectFXNugget::parse, 0, 0},
- { "Tracer", TracerFXNugget::parse, 0, 0},
- { "LightPulse", LightPulseFXNugget::parse, 0, 0},
- { "ViewShake", ViewShakeFXNugget::parse, 0, 0},
- { "TerrainScorch", TerrainScorchFXNugget::parse, 0, 0},
- { "ParticleSystem", ParticleSystemFXNugget::parse, 0, 0},
- { "FXListAtBonePos", FXListAtBonePosFXNugget::parse, 0, 0},
- { NULL, NULL, 0, 0 } // keep this last
- };
- //-------------------------------------------------------------------------------------------------
- FXList::FXList()
- {
- }
- //-------------------------------------------------------------------------------------------------
- FXList::~FXList()
- {
- clear();
- }
- //-------------------------------------------------------------------------------------------------
- void FXList::clear()
- {
- for (FXNuggetList::iterator it = m_nuggets.begin(); it != m_nuggets.end(); ++it)
- {
- if (*it)
- (*it)->deleteInstance();
- }
- m_nuggets.clear();
- }
- //-------------------------------------------------------------------------------------------------
- void FXList::doFXPos(const Coord3D *primary, const Matrix3D* primaryMtx, const Real primarySpeed, const Coord3D *secondary, const Real overrideRadius ) const
- {
- if (ThePartitionManager->getShroudStatusForPlayer(ThePlayerList->getLocalPlayer()->getPlayerIndex(), primary) != CELLSHROUD_CLEAR)
- return;
- for (FXNuggetList::const_iterator it = m_nuggets.begin(); it != m_nuggets.end(); ++it)
- {
- (*it)->doFXPos(primary, primaryMtx, primarySpeed, secondary, overrideRadius);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void FXList::doFXObj(const Object* primary, const Object* secondary) const
- {
- if (primary && primary->getShroudedStatus(ThePlayerList->getLocalPlayer()->getPlayerIndex()) > OBJECTSHROUD_PARTIAL_CLEAR)
- return; //the primary object is fogged or shrouded so don't bother with the effect.
- for (FXNuggetList::const_iterator it = m_nuggets.begin(); it != m_nuggets.end(); ++it)
- {
- // HERE THE PRIMARY IS THE GUY RECEIVING THE FX, AND SECONDARY MIGHT BE THE GUY DEALING IT
- (*it)->doFXObj(primary, secondary);
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- FXListStore::FXListStore()
- {
- }
- //-------------------------------------------------------------------------------------------------
- FXListStore::~FXListStore()
- {
- m_fxmap.clear();
- }
- //-------------------------------------------------------------------------------------------------
- const FXList *FXListStore::findFXList(const char* name) const
- {
- if (stricmp(name, "None") == 0)
- return NULL;
- FXListMap::const_iterator it = m_fxmap.find(NAMEKEY(name));
- if (it != m_fxmap.end())
- {
- return &(*it).second;
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- /*static */ void FXListStore::parseFXListDefinition(INI *ini)
- {
- // read the FXList name
- const char *c = ini->getNextToken();
- NameKeyType key = TheNameKeyGenerator->nameToKey(c);
- FXList& fxl = TheFXListStore->m_fxmap[key];
- fxl.clear();
- ini->initFromINI(&fxl, TheFXListFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void INI::parseFXListDefinition(INI *ini)
- {
- FXListStore::parseFXListDefinition(ini);
- }
|