particle.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. /**
  2. \brief Legacy way of using particles in the game
  3. * \note They work okay when just needing to play a particle once every once in a while
  4. * But are extremely wasteful when it comes to playing multiple Particles at the same time
  5. */
  6. class Particle : ParticleBase
  7. {
  8. /** \name Generic data
  9. Generic data for the Particle
  10. */
  11. //@{
  12. //! ID from ParticleList if assigned
  13. protected int m_ParticleID;
  14. //! Approx. remaining lifetime of particle
  15. protected float m_Lifetime;
  16. //! Whether this particle repeats
  17. protected bool m_IsRepeat;
  18. //! Whether this particle is queued for deletion
  19. private bool m_MarkedForDeletion;
  20. //@}
  21. /** \name Wiggle API
  22. Data for the wiggle API
  23. */
  24. //@{
  25. //! Used for Wiggle API, to signal that wiggle API is currently doing work
  26. bool m_WiggleProcessing;
  27. //! Used for Wiggle API, to restore after unparenting
  28. bool m_ForceOrientationRelativeToWorld;
  29. //! Used for Wiggle API, to restore after unparenting
  30. vector m_DefaultOri;
  31. //! Used for Wiggle API, to restore after unparenting
  32. vector m_DefaultPos;
  33. //! Used for Wiggle API, to restore after unparenting
  34. vector m_DefaultWorldOri;
  35. //! Used for Wiggle API, to restore after unparenting
  36. vector m_DefaultWorldPos;
  37. //! Used for Wiggle API, Wiggle room [-m_MaxOriWiggle, m_MaxOriWiggle]
  38. float m_MaxOriWiggle;
  39. //! Used for Wiggle API, Interval for wiggling [0, m_MaxOriInterval[
  40. float m_MaxOriInterval;
  41. //! Used for Wiggle API, calls the Wiggle functionality
  42. ref Timer m_RandomizeOri;
  43. //@}
  44. //! Parent Object the Particle is child of
  45. protected Object m_ParentObject;
  46. //! The child object which contains the actual particle
  47. protected Object m_ParticleEffect;
  48. //! DEPRECATED
  49. protected int m_PreviousFrame;
  50. //! DEPRECATED
  51. private vector m_GlobalPosPreviousFrame;
  52. //! DEPRECATED
  53. static private const int MAX_EMITORS = 30;
  54. //! ctor
  55. void Particle()
  56. {
  57. ParticleInit();
  58. }
  59. //! Purely here so that it can be emptied in ParticleSource
  60. protected void ParticleInit()
  61. {
  62. SetFlags(EntityFlags.VISIBLE, true);
  63. SetEventMask(EntityEvent.INIT);
  64. SetEventMask(EntityEvent.FRAME);
  65. }
  66. /** \name Create a particle (static)
  67. You can create a particle either at some position, or create it as a child on some object.
  68. */
  69. //@{
  70. /**
  71. \brief Creates a particle emitter and attaches it on the given object
  72. \param particle_id \p int Particle ID registered in ParticleList
  73. \param parent_obj \p Object Instance on which this particle will be attached
  74. \param local_pos \p vector Attachment position local to the parent (Optional)
  75. \param local_ori \p vector Orientation local to the parent (Pitch, Yawn, Roll in degrees) (Optional)
  76. \param force_world_rotation \p bool Forces particle's orientation to rotate relative to the world and not with the object (Optional)
  77. \return \p Particle Created particle instance
  78. */
  79. static Particle CreateOnObject( int particle_id, Object parent_obj, vector local_pos = "0 0 0", vector local_ori = "0 0 0", bool force_world_rotation = false )
  80. {
  81. if (!parent_obj)
  82. Error("ERROR when creating a particle! Parameter parent_obj is NULL!");
  83. vector global_pos = parent_obj.GetPosition();
  84. Particle p = CreateInWorld(particle_id, global_pos, Vector(0,0,0), force_world_rotation);
  85. p.AddAsChild(parent_obj, local_pos, local_ori, force_world_rotation);
  86. p.m_DefaultOri = local_ori;
  87. return p;
  88. }
  89. /**
  90. \brief Legacy function for backwards compatibility
  91. */
  92. static Particle Create( int particle_id, Object parent_obj, vector local_pos = "0 0 0", vector local_ori = "0 0 0" )
  93. {
  94. return CreateOnObject( particle_id, parent_obj, local_pos, local_ori);
  95. }
  96. /**
  97. \brief Creates a particle emitter on the given position
  98. \param particle_id \p int Particle ID registered in ParticleList
  99. \param global_pos \p Vector Position where the particel will be created
  100. \param global_ori \p vector Orientation (Pitch, Yawn, Roll in degrees) (Optional)
  101. \param force_world_rotation \p bool Forces particle's orientation to rotate relative to the world and not with the object (Optional)
  102. \return \p Particle Created particle instance
  103. */
  104. static Particle CreateInWorld( int particle_id, vector global_pos, vector global_ori = "0 0 0", bool force_world_rotation = false )
  105. {
  106. Particle p = Particle.Cast( GetGame().CreateObjectEx("Particle", global_pos, ECE_LOCAL) );
  107. p.SetSource(particle_id);
  108. p.SetOrientation(global_ori);
  109. p.m_ForceOrientationRelativeToWorld = force_world_rotation;
  110. return p;
  111. }
  112. /**
  113. \brief Legacy function for backwards compatibility with 1.01 and below
  114. */
  115. static Particle Create( int particle_id, vector global_pos, vector global_ori = "0 0 0" )
  116. {
  117. return CreateInWorld( particle_id, global_pos, global_ori );
  118. }
  119. //@}
  120. /** \name Static play on creation
  121. You can use the following Play(...) functions to create and activate a particle in 1 line of your script.
  122. */
  123. //@{
  124. /**
  125. \brief Creates a particle emitter, attaches it on the given object and activates it
  126. \param particle_id \p int Particle ID registered in ParticleList
  127. \param parent_obj \p Object Instance on which this particle will be attached
  128. \param local_pos \p vector Attachment position local to the parent (Optional)
  129. \param local_ori \p vector Orientation local to the parent (Pitch, Yaw, Roll in degrees) (Optional)
  130. \param force_world_rotation \p bool Forces particle's orientation to rotate relative to the world and not with the object (Optional)
  131. \return \p Particle Created particle instance
  132. */
  133. static Particle PlayOnObject( int particle_id, Object parent_obj, vector local_pos = "0 0 0", vector local_ori = "0 0 0", bool force_world_rotation = false )
  134. {
  135. Particle p = CreateOnObject(particle_id, parent_obj, local_pos, local_ori, force_world_rotation);
  136. p.PlayParticle();
  137. return p;
  138. }
  139. /**
  140. \brief Legacy function for backwards compatibility with 1.01 and below
  141. */
  142. static Particle Play( int particle_id, Object parent_obj, vector local_pos = "0 0 0", vector local_ori = "0 0 0" )
  143. {
  144. return PlayOnObject( particle_id, parent_obj, local_pos, local_ori);
  145. }
  146. /**
  147. \brief Creates a particle emitter on the given position and activates it
  148. \param particle_id \p int Particle ID registered in ParticleList
  149. \param global_pos \p Vector Position where the particel will be created
  150. \return \p Particle Created particle instance
  151. */
  152. static Particle PlayInWorld( int particle_id, vector global_pos)
  153. {
  154. Particle p = CreateInWorld(particle_id, global_pos);
  155. p.PlayParticle();
  156. return p;
  157. }
  158. /**
  159. \brief Legacy function for backwards compatibility with 1.01 and below
  160. */
  161. static Particle Play( int particle_id, vector global_pos)
  162. {
  163. return PlayInWorld( particle_id, global_pos);
  164. }
  165. //@}
  166. /** \name Playback
  167. Methods regarding playing/stopping of particle
  168. */
  169. //@{
  170. /**
  171. \brief Method to tell the particle to start playing
  172. \param particle_id \p int Particle ID registered in ParticleList to start playing
  173. */
  174. override void PlayParticle(int particle_id = -1)
  175. {
  176. PlayParticleEx(particle_id, 0);
  177. }
  178. /**
  179. \brief Method to tell the particle to start playing
  180. \note The parameter to set the ID will only work when the particle is not already playing
  181. \param particle_id \p int Particle ID registered in ParticleList to start playing
  182. \param flags \p int Flags to pass to the playing (None on this level)
  183. \return \p bool Whether the particle successfully started
  184. */
  185. override bool PlayParticleEx(int particle_id = -1, int flags = 0)
  186. {
  187. if ( particle_id > -1 )
  188. {
  189. SetSource(particle_id);
  190. }
  191. OnParticleStart();
  192. UpdateState();
  193. return true;
  194. }
  195. /**
  196. \brief Legacy function for backwards compatibility with 1.01 and below
  197. \param particle_id \p int Particle ID registered in ParticleList to start playing
  198. */
  199. void Play(int particle_id = -1)
  200. {
  201. PlayParticle(particle_id);
  202. }
  203. /**
  204. \brief Method to tell the particle to stop playing
  205. \note No flags available for Particle
  206. \note Emitors are automatically removed later when its particle count is 0
  207. \param flags \p int Flags to pass to the stopping (None on this level)
  208. \return \p bool Whether the particle successfully stopped
  209. */
  210. override bool StopParticle(int flags = 0)
  211. {
  212. OnParticleStop();
  213. // Without the following we might get an error when a particle parent is despawned client-side.
  214. Object parent = Object.Cast( GetParent() );
  215. if ( parent && !ToDelete())
  216. {
  217. vector world_pos = GetPosition();
  218. parent.RemoveChild(this);
  219. SetPosition(world_pos);
  220. }
  221. UpdateState();
  222. return true;
  223. }
  224. /**
  225. \brief Legacy function for backwards compatibility with 1.14 and below
  226. */
  227. void Stop()
  228. {
  229. StopParticle();
  230. }
  231. //@}
  232. /** \name Properties and state
  233. Obtain information or set properties regarding the state of the Particle
  234. */
  235. //@{
  236. /**
  237. \brief Sets particle id
  238. \note Does not work at runtime, particle object needs to be destroyed and then Particle needs to play again
  239. \param particle_id \p int Particle ID registered in ParticleList to start playing
  240. */
  241. void SetSource(int particle_id)
  242. {
  243. m_ParticleID = particle_id;
  244. }
  245. /**
  246. \brief Gets particle id
  247. \note This is not necessarily the CURRENT particle
  248. * As one can use SetSource while the Particle is still playing
  249. * But that will not change the particle before Particle is played again
  250. \return \p int The last set Particle ID registered in ParticleList
  251. */
  252. int GetParticleID()
  253. {
  254. return m_ParticleID;
  255. }
  256. /**
  257. \brief Returns direct particle effect entity which is usually handled by this class 'Particle' if there is one
  258. \note Is a child of this Particle
  259. \return \p Object The Object with the particle component or null
  260. */
  261. Object GetDirectParticleEffect()
  262. {
  263. return m_ParticleEffect;
  264. }
  265. /**
  266. \brief Returns the parent of this Particle if there is one
  267. \return \p Object The registered parent or null
  268. */
  269. Object GetParticleParent()
  270. {
  271. return m_ParentObject;
  272. }
  273. /**
  274. \brief Returns if there is any particle active
  275. \return \p bool Whether there is any particle active
  276. */
  277. bool HasActiveParticle()
  278. {
  279. if (m_ParticleEffect)
  280. {
  281. return ParticleHasActive(m_ParticleEffect);
  282. }
  283. return false;
  284. }
  285. /**
  286. \brief Returns the total count of active particles in all emitors
  287. \note Internally does a sum, HasActiveParticle is better for a quick check
  288. \return \p int Total count of active particles
  289. */
  290. int GetParticleCount()
  291. {
  292. if (m_ParticleEffect)
  293. {
  294. return ParticleGetCount(m_ParticleEffect);
  295. }
  296. return 0;
  297. }
  298. /**
  299. \brief Returns whether there is a repeating particle
  300. \return \p bool whether there is a repeating particle
  301. */
  302. bool IsRepeat()
  303. {
  304. if (m_ParticleEffect)
  305. {
  306. bool repeat = false;
  307. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  308. for (int i = 0; i < emitors; ++i)
  309. {
  310. GetParticleParm(m_ParticleEffect, i, EmitorParam.REPEAT, repeat);
  311. if (repeat)
  312. {
  313. return true;
  314. }
  315. }
  316. }
  317. return false;
  318. }
  319. /**
  320. \brief Returns the approx. max lifetime
  321. \return \p float The largest lifetime sum among the emitors
  322. */
  323. float GetMaxLifetime()
  324. {
  325. float lifetime_return = 0;
  326. if (m_ParticleEffect)
  327. {
  328. float lifetime_min = 0;
  329. float lifetime_random = 0;
  330. float effect_time = 0;
  331. float lifetime_sum = 0;
  332. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  333. for (int i = 0; i < emitors; ++i)
  334. {
  335. GetParticleParm(m_ParticleEffect, i, EmitorParam.LIFETIME, lifetime_min);
  336. GetParticleParm(m_ParticleEffect, i, EmitorParam.LIFETIME_RND, lifetime_random);
  337. GetParticleParm(m_ParticleEffect, i, EmitorParam.EFFECT_TIME, effect_time);
  338. lifetime_sum = lifetime_min + lifetime_random + effect_time;
  339. if ( lifetime_sum > lifetime_return )
  340. {
  341. lifetime_return = lifetime_sum;
  342. }
  343. }
  344. }
  345. return lifetime_return;
  346. }
  347. //@}
  348. /** \name Misc Particle specific helpers
  349. Functionality specific for Particle
  350. */
  351. //@{
  352. /**
  353. \brief Creates/Destroys ParticleEffect child according to current state
  354. \note Is called from Play/Stop methods
  355. */
  356. protected void UpdateState()
  357. {
  358. if ( m_IsPlaying == false && m_ParticleEffect)
  359. {
  360. DestroyParticleEffect();
  361. }
  362. else if ( m_IsPlaying == true && m_ParticleEffect == null )
  363. {
  364. CreateParticleEffect();
  365. }
  366. }
  367. /**
  368. \brief Creates ParticleEffect child, called from UpdateState
  369. */
  370. private void CreateParticleEffect()
  371. {
  372. if ( !GetGame().IsServer() || !GetGame().IsMultiplayer() )
  373. {
  374. string fullPath = ParticleList.GetParticleFullPath(m_ParticleID);
  375. if (fullPath == "")
  376. {
  377. ErrorEx("Could not play Particle as there is no valid particle id assigned.");
  378. m_IsPlaying = false;
  379. return;
  380. }
  381. if ( m_ParticleEffect == null )
  382. {
  383. m_ParticleEffect = GetGame().CreateObjectEx("#particlesourceenf", vector.Zero, ECE_LOCAL); // particle source must be lowercase!
  384. }
  385. AddChild(m_ParticleEffect, -1, m_ForceOrientationRelativeToWorld);
  386. vobject vobj = GetObject( fullPath );
  387. m_ParticleEffect.SetObject(vobj, "");
  388. ReleaseObject(vobj);
  389. m_IsRepeat = IsRepeat();
  390. m_Lifetime = GetMaxLifetime();
  391. }
  392. }
  393. /**
  394. \brief Destroys ParticleEffect child, called from UpdateState
  395. \note Does not destroy it immediately
  396. * As it simply nulls the lifetime
  397. * Then it will be cleaned up by EOnFrame eventually
  398. */
  399. private void DestroyParticleEffect()
  400. {
  401. if ( m_ParticleEffect && GetGame() )
  402. {
  403. SetParameter(-1, EmitorParam.LIFETIME, 0);
  404. SetParameter(-1, EmitorParam.LIFETIME_RND, 0);
  405. SetParameter(-1, EmitorParam.REPEAT, 0);
  406. m_IsRepeat = false;
  407. }
  408. }
  409. /**
  410. \brief OnFrame update event decrementing the stored approx. lifetime and checking for deletion
  411. */
  412. override void EOnFrame(IEntity other, float timeSlice)
  413. {
  414. m_Lifetime -= timeSlice;
  415. OnCheckAutoDelete();
  416. }
  417. /**
  418. \brief Creates ParticleEffect child, called from UpdateState
  419. */
  420. void OnCheckAutoDelete()
  421. {
  422. if (m_Lifetime <= 0)
  423. {
  424. if (!m_MarkedForDeletion)
  425. {
  426. m_IsRepeat = IsRepeat(); // It is possible that the REPEAT flag was changed during lifetime, so it needs to be checked again.
  427. if ( m_IsRepeat )
  428. {
  429. m_Lifetime = GetMaxLifetime();
  430. }
  431. else
  432. {
  433. OnParticleStop();
  434. if ( GetParticleCount() == 0 )
  435. {
  436. m_MarkedForDeletion = true;
  437. OnToDelete();
  438. OnParticleEnd();
  439. }
  440. }
  441. }
  442. else
  443. {
  444. if ( m_MarkedForDeletion )
  445. {
  446. if (m_ParticleEffect)
  447. {
  448. m_ParticleEffect.Delete();
  449. m_ParticleEffect = null;
  450. }
  451. Delete();
  452. }
  453. }
  454. }
  455. }
  456. /**
  457. \brief Called before deletion from OnCheckAutoDelete
  458. */
  459. private void OnToDelete()
  460. {
  461. }
  462. //@}
  463. /** \name Misc
  464. Various helpers
  465. */
  466. //@{
  467. /**
  468. \brief Attaches this particle onto some object. If null value is provided then the particle will be detached from the current parent.
  469. \note Due to the members being filled in, AddChild/RemoveChild cannot be used with Particle when using Wiggle
  470. \param parent \p Object Parent onto which this particle will be attached
  471. \param local_pos \p vector Attachment position local to the parent (optional)
  472. \param local_ori \p vector Orientation local to the parent (Pitch, Yawn, Roll in degrees) (Optional)
  473. \param force_rotation_to_world \p bool Force rotation to be in WS (Optional)
  474. */
  475. void AddAsChild(Object parent, vector local_pos = "0 0 0", vector local_ori = "0 0 0", bool force_rotation_to_world = false)
  476. {
  477. if (ToDelete())
  478. return;
  479. if (parent)
  480. {
  481. // AddAsChild method is sometimes called from a timer.
  482. // Due to that it is necesarry to use ToDelete() here to check if the parent object is flagged for deletion or not on client,
  483. // because sometimes this code is executed before the parent's destructor from where this would normally be handled.
  484. if (!parent.ToDelete())
  485. {
  486. SetPosition(local_pos);
  487. SetOrientation(local_ori);
  488. m_ParentObject = parent;
  489. m_DefaultPos = local_pos;
  490. m_ForceOrientationRelativeToWorld = force_rotation_to_world;
  491. if (m_ParticleEffect)
  492. AddChild(m_ParticleEffect, -1, m_ForceOrientationRelativeToWorld);
  493. parent.AddChild(this, -1, false);
  494. }
  495. }
  496. else
  497. {
  498. if (m_ParentObject && !m_ParentObject.ToDelete())
  499. {
  500. m_ParentObject.RemoveChild(this, true);
  501. m_ParentObject = null;
  502. }
  503. }
  504. }
  505. //@}
  506. /** \name Parameter API
  507. Helpful methods for getting or setting parameters
  508. */
  509. //@{
  510. /**
  511. \brief Set the value of a parameter of all emitors in the particle
  512. \param parameter \p int The parameter to apply the new value to (enum EmitorParam)
  513. \param value \p float The value to apply
  514. */
  515. void SetParticleParam(int parameter_id, float value )
  516. {
  517. if (!m_ParticleEffect)
  518. return;
  519. SetParticleParm(m_ParticleEffect, -1, parameter_id, value);
  520. }
  521. /**
  522. \brief Set the value of a parameter of an emitor in the particle
  523. \param emitter \p int The emitter to apply the new value to, -1 for all emitter
  524. \param parameter \p int The parameter to apply the new value to (enum EmitorParam)
  525. \param value \p float The value to apply
  526. */
  527. void SetParameter(int emitter, int parameter, float value)
  528. {
  529. if (!m_ParticleEffect)
  530. return;
  531. SetParticleParm(m_ParticleEffect, emitter, parameter, value);
  532. }
  533. /**
  534. \brief Get the value of a parameter of an emitor in the particle
  535. \param emitter \p int The emitor to get the value from
  536. \param parameter \p int The parameter to get the value from (enum EmitorParam)
  537. \param value \p float The value
  538. */
  539. void GetParameter(int emitter, int parameter, out float value)
  540. {
  541. if (!m_ParticleEffect)
  542. return;
  543. GetParticleParm(m_ParticleEffect, emitter, parameter, value);
  544. }
  545. /**
  546. \brief Get the value of a parameter of an emitor in the particle
  547. \param emitter \p int The emitor to get the value from
  548. \param parameter \p int The parameter to get the value from (enum EmitorParam)
  549. \return \p float The value
  550. */
  551. float GetParameterEx(int emitter, int parameter)
  552. {
  553. if (!m_ParticleEffect)
  554. return 0;
  555. float value;
  556. GetParticleParm(m_ParticleEffect, emitter, parameter, value);
  557. return value;
  558. }
  559. float GetParameterOriginal(int emitter, int parameter)
  560. {
  561. if (!m_ParticleEffect)
  562. return 0;
  563. float value;
  564. GetParticleParmOriginal(m_ParticleEffect, emitter, parameter, value);
  565. return value;
  566. }
  567. /**
  568. \brief Scales the given parameter on all emitors relatively to their ORIGINAL value.
  569. \param parameter_id \p int The parameter to adjust (enum EmitorParam)
  570. \param coef \p float The multiplier to apply
  571. */
  572. void ScaleParticleParamFromOriginal(int parameter_id, float coef )
  573. {
  574. if (!m_ParticleEffect)
  575. return;
  576. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  577. for (int i = 0; i < emitors; ++i)
  578. {
  579. float value;
  580. GetParticleParmOriginal(m_ParticleEffect, i, parameter_id, value);
  581. SetParticleParm(m_ParticleEffect, i, parameter_id, value * coef);
  582. }
  583. }
  584. /**
  585. \brief Scales the given parameter on all emitors relatively to their CURRENT value.
  586. \param parameter_id \p int The parameter to adjust (enum EmitorParam)
  587. \param coef \p float The multiplier to apply
  588. */
  589. void ScaleParticleParam(int parameter_id, float coef )
  590. {
  591. if (!m_ParticleEffect)
  592. return;
  593. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  594. for (int i = 0; i < emitors; ++i)
  595. {
  596. float value;
  597. GetParticleParm(m_ParticleEffect, i, parameter_id, value);
  598. SetParticleParm(m_ParticleEffect, i, parameter_id, value * coef);
  599. }
  600. }
  601. /**
  602. \brief Increments the value of the given parameter relatively from the ORIGINAL value.
  603. \note It's a simple sum, so negative value decrements
  604. \param parameter_id \p int The parameter to adjust (enum EmitorParam)
  605. \param value \p float The value to sum
  606. */
  607. void IncrementParticleParamFromOriginal(int parameter_id, float value )
  608. {
  609. if (!m_ParticleEffect)
  610. return;
  611. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  612. for (int i = 0; i < emitors; ++i)
  613. {
  614. float param;
  615. GetParticleParmOriginal(m_ParticleEffect, i, parameter_id, param);
  616. SetParticleParm(m_ParticleEffect, i, parameter_id, param + value);
  617. }
  618. }
  619. /**
  620. \brief Increments the value of the given parameter relatively from the CURRENT value.
  621. \note It's a simple sum, so negative value decrements
  622. \param parameter_id \p int The parameter to adjust (enum EmitorParam)
  623. \param value \p float The value to sum
  624. */
  625. void IncrementParticleParam(int parameter_id, float value )
  626. {
  627. if (!m_ParticleEffect)
  628. return;
  629. int emitors = GetParticleEmitorCount(m_ParticleEffect);
  630. for (int i = 0; i < emitors; ++i)
  631. {
  632. float param;
  633. GetParticleParm(m_ParticleEffect, i, parameter_id, param);
  634. SetParticleParm(m_ParticleEffect, i, parameter_id, param + value);
  635. }
  636. }
  637. //@}
  638. /** \name Wiggle API
  639. Settings to make the Particle wiggle
  640. */
  641. //@{
  642. /**
  643. \brief Checks if particle is currently wiggling
  644. */
  645. bool IsWiggling()
  646. {
  647. return m_RandomizeOri && m_RandomizeOri.IsRunning();
  648. }
  649. /**
  650. \brief Makes the particle change direction by random_angle every random_interval seconds.
  651. \note This does not actually work on Particle with no parent, it should on ParticleSource
  652. \note Calling SetWiggle(0,0) will effectively stop all wiggle functionality
  653. \param random_angle \p float Will be the range [-random_angle, random_angle[ to wiggle between
  654. \param random_interval \p float Will be the time range [0, random_interval] to wiggle next time
  655. */
  656. void SetWiggle(float random_angle, float random_interval)
  657. {
  658. if ( random_angle != 0 || random_interval != 0 )
  659. {
  660. m_MaxOriWiggle = random_angle;
  661. m_MaxOriInterval = random_interval;
  662. if ( !m_RandomizeOri )
  663. m_RandomizeOri = new Timer( CALL_CATEGORY_GAMEPLAY );
  664. if ( !m_RandomizeOri.IsRunning() ) // Makes sure the timer is NOT running already
  665. m_RandomizeOri.Run( Math.RandomFloat(0, m_MaxOriInterval) , this, "RandomizeOrientation", null, false);
  666. }
  667. else
  668. {
  669. StopWiggle();
  670. }
  671. }
  672. /**
  673. \brief Stops randomized wiggle
  674. */
  675. void StopWiggle()
  676. {
  677. if ( m_RandomizeOri )
  678. {
  679. m_RandomizeOri.Stop();
  680. }
  681. m_MaxOriWiggle = 0;
  682. m_MaxOriInterval = 0;
  683. }
  684. /**
  685. \brief Randomizes a new orientation and applies it
  686. */
  687. void RandomizeOrientation()
  688. {
  689. m_WiggleProcessing = true;
  690. if (m_ParentObject)
  691. {
  692. if ( !m_RandomizeOri.IsRunning() )
  693. {
  694. m_RandomizeOri.Run( Math.RandomFloat(0, m_MaxOriInterval) , this, "RandomizeOrientation", NULL, false);
  695. }
  696. Object old_parent = m_ParentObject;
  697. AddAsChild( null );
  698. AddAsChild( old_parent, m_DefaultPos, m_DefaultOri + RandWiggleVector() );
  699. }
  700. m_WiggleProcessing = false;
  701. }
  702. /**
  703. \brief Helper to get a randomized wiggle vector
  704. */
  705. protected vector RandWiggleVector()
  706. {
  707. return Vector( RandWiggleFloat(), RandWiggleFloat(), RandWiggleFloat() );
  708. }
  709. /**
  710. \brief Helper to get a randomized wiggle float value
  711. */
  712. protected float RandWiggleFloat()
  713. {
  714. return Math.RandomFloatInclusive(-m_MaxOriWiggle, m_MaxOriWiggle);
  715. }
  716. //@}
  717. }