splash.cpp

Engine/source/T3D/fx/splash.cpp

More...

Public Functions

ConsoleDocClass(Splash , "@brief Manages the ring used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsplash/">Splash</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effect.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
ConsoleDocClass(SplashData , "@brief Acts as the physical point in space in white <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsplash/">Splash</a> is created <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(Splash , "@brief Manages the ring used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsplash/">Splash</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effect.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )

ConsoleDocClass(SplashData , "@brief Acts as the physical point in space in white <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsplash/">Splash</a> is created <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(SplashData )

IMPLEMENT_CO_NETOBJECT_V1(Splash )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "T3D/fx/splash.h"
 25
 26#include "console/consoleTypes.h"
 27#include "gfx/primBuilder.h"
 28#include "gfx/gfxDrawUtil.h"
 29#include "sfx/sfxSystem.h"
 30#include "sfx/sfxProfile.h"
 31#include "scene/sceneManager.h"
 32#include "scene/sceneRenderState.h"
 33#include "core/stream/bitStream.h"
 34#include "math/mathIO.h"
 35#include "T3D/fx/explosion.h"
 36#include "T3D/fx/particle.h"
 37#include "T3D/fx/particleEmitter.h"
 38#include "T3D/fx/particleEmitterNode.h"
 39#include "T3D/gameBase/gameProcess.h"
 40#include "sim/netConnection.h"
 41#include "renderInstance/renderPassManager.h"
 42#include "console/engineAPI.h"
 43
 44namespace
 45{
 46
 47MRandomLCG sgRandom(0xdeadbeef);
 48
 49} // namespace {}
 50
 51//----------------------------------------------------------------------------
 52
 53IMPLEMENT_CO_DATABLOCK_V1(SplashData);
 54IMPLEMENT_CO_NETOBJECT_V1(Splash);
 55
 56ConsoleDocClass( SplashData,
 57   "@brief Acts as the physical point in space in white a Splash is created from.\n"
 58   "@ingroup FX\n"
 59);
 60
 61ConsoleDocClass( Splash,
 62   "@brief Manages the ring used for a Splash effect.\n"
 63   "@ingroup FX\n"
 64);
 65
 66//--------------------------------------------------------------------------
 67// Splash Data
 68//--------------------------------------------------------------------------
 69SplashData::SplashData()
 70{
 71   soundProfile      = NULL;
 72   soundProfileId    = 0;
 73
 74   scale.set(1, 1, 1);
 75
 76   dMemset( emitterList, 0, sizeof( emitterList ) );
 77   dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
 78
 79   delayMS = 0;
 80   delayVariance = 0;
 81   lifetimeMS = 1000;
 82   lifetimeVariance = 0;
 83   width = 4.0;
 84   numSegments = 10;
 85   velocity = 5.0;
 86   height = 0.0;
 87   acceleration = 0.0;
 88   texWrap = 1.0;
 89   texFactor = 3.0;
 90   ejectionFreq = 5;
 91   ejectionAngle = 45.0;
 92   ringLifetime = 1.0;
 93   startRadius = 0.5;
 94   explosion = NULL;
 95   explosionId = 0;
 96
 97   dMemset( textureName, 0, sizeof( textureName ) );
 98
 99   U32 i;
100   for( i=0; i<NUM_TIME_KEYS; i++ )
101      times[i] = 1.0;
102
103   times[0] = 0.0;
104
105   for( i=0; i<NUM_TIME_KEYS; i++ )
106      colors[i].set( 1.0, 1.0, 1.0, 1.0 );
107
108}
109
110//--------------------------------------------------------------------------
111// Init fields
112//--------------------------------------------------------------------------
113   void SplashData::initPersistFields()
114{
115   addField("soundProfile",      TYPEID< SFXProfile >(),       Offset(soundProfile,       SplashData), "SFXProfile effect to play.\n");
116   addField("scale",             TypePoint3F,                  Offset(scale,              SplashData), "The scale of this splashing effect, defined as the F32 points X, Y, Z.\n");
117   addField("emitter",           TYPEID< ParticleEmitterData >(),   Offset(emitterList,        SplashData), NUM_EMITTERS, "List of particle emitters to create at the point of this Splash effect.\n");
118   addField("delayMS",           TypeS32,                      Offset(delayMS,            SplashData), "Time to delay, in milliseconds, before actually starting this effect.\n");
119   addField("delayVariance",     TypeS32,                      Offset(delayVariance,      SplashData), "Time variance for delayMS.\n");
120   addField("lifetimeMS",        TypeS32,                      Offset(lifetimeMS,         SplashData), "Lifetime for this effect, in milliseconds.\n");
121   addField("lifetimeVariance",  TypeS32,                      Offset(lifetimeVariance,   SplashData), "Time variance for lifetimeMS.\n");
122   addField("width",             TypeF32,                      Offset(width,              SplashData), "Width for the X and Y coordinates to create this effect within.");
123   addField("numSegments",       TypeS32,                      Offset(numSegments,        SplashData), "Number of ejection points in the splash ring.\n");
124   addField("velocity",          TypeF32,                      Offset(velocity,           SplashData), "Velocity for the splash effect to travel.\n");
125   addField("height",            TypeF32,                      Offset(height,             SplashData), "Height for the splash to reach.\n");
126   addField("acceleration",      TypeF32,                      Offset(acceleration,       SplashData), "Constant acceleration value to place upon the splash effect.\n");
127   addField("times",             TypeF32,                      Offset(times,              SplashData), NUM_TIME_KEYS, "Times to transition through the splash effect. Up to 4 allowed. Values are 0.0 - 1.0, and corrispond to the life of the particle where 0 is first created and 1 is end of lifespace.\n" );
128   addField("colors",            TypeColorF,                   Offset(colors,             SplashData), NUM_TIME_KEYS, "Color values to set the splash effect, rgba. Up to 4 allowed. Will transition through colors based on values set in the times value. Example: colors[0] = \"0.6 1.0 1.0 0.5\".\n" );
129   addField("texture",           TypeFilename,                 Offset(textureName,        SplashData), NUM_TEX, "Imagemap file to use as the texture for the splash effect.\n");
130   addField("texWrap",           TypeF32,                      Offset(texWrap,            SplashData), "Amount to wrap the texture around the splash ring, 0.0f - 1.0f.\n");
131   addField("texFactor",         TypeF32,                      Offset(texFactor,          SplashData), "Factor in which to apply the texture to the splash ring, 0.0f - 1.0f.\n");
132   addField("ejectionFreq",      TypeF32,                      Offset(ejectionFreq,       SplashData), "Frequency in which to emit splash rings.\n");
133   addField("ejectionAngle",     TypeF32,                      Offset(ejectionAngle,      SplashData), "Rotational angle to create a splash ring.\n");
134   addField("ringLifetime",      TypeF32,                      Offset(ringLifetime,       SplashData), "Lifetime, in milliseconds, for a splash ring.\n");
135   addField("startRadius",       TypeF32,                      Offset(startRadius,        SplashData), "Starting radius size of a splash ring.\n");
136   addField("explosion",         TYPEID< ExplosionData >(),    Offset(explosion,          SplashData), "ExplosionData object to create at the creation position of this splash effect.\n");
137
138   Parent::initPersistFields();
139}
140
141//--------------------------------------------------------------------------
142// On add - verify data settings
143//--------------------------------------------------------------------------
144bool SplashData::onAdd()
145{
146   if (Parent::onAdd() == false)
147      return false;
148
149   return true;
150}
151
152//--------------------------------------------------------------------------
153// Pack data
154//--------------------------------------------------------------------------
155void SplashData::packData(BitStream* stream)
156{
157   Parent::packData(stream);
158
159   mathWrite(*stream, scale);
160   stream->write(delayMS);
161   stream->write(delayVariance);
162   stream->write(lifetimeMS);
163   stream->write(lifetimeVariance);
164   stream->write(width);
165   stream->write(numSegments);
166   stream->write(velocity);
167   stream->write(height);
168   stream->write(acceleration);
169   stream->write(texWrap);
170   stream->write(texFactor);
171   stream->write(ejectionFreq);
172   stream->write(ejectionAngle);
173   stream->write(ringLifetime);
174   stream->write(startRadius);
175
176   if( stream->writeFlag( explosion ) )
177   {
178      stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
179   }
180
181   S32 i;
182   for( i=0; i<NUM_EMITTERS; i++ )
183   {
184      if( stream->writeFlag( emitterList[i] != NULL ) )
185      {
186         stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
187      }
188   }
189
190   for( i=0; i<NUM_TIME_KEYS; i++ )
191   {
192      stream->write( colors[i] );
193   }
194
195   for( i=0; i<NUM_TIME_KEYS; i++ )
196   {
197      stream->write( times[i] );
198   }
199
200   for( i=0; i<NUM_TEX; i++ )
201   {
202      stream->writeString(textureName[i]);
203   }
204}
205
206//--------------------------------------------------------------------------
207// Unpack data
208//--------------------------------------------------------------------------
209void SplashData::unpackData(BitStream* stream)
210{
211   Parent::unpackData(stream);
212
213   mathRead(*stream, &scale);
214   stream->read(&delayMS);
215   stream->read(&delayVariance);
216   stream->read(&lifetimeMS);
217   stream->read(&lifetimeVariance);
218   stream->read(&width);
219   stream->read(&numSegments);
220   stream->read(&velocity);
221   stream->read(&height);
222   stream->read(&acceleration);
223   stream->read(&texWrap);
224   stream->read(&texFactor);
225   stream->read(&ejectionFreq);
226   stream->read(&ejectionAngle);
227   stream->read(&ringLifetime);
228   stream->read(&startRadius);
229
230   if( stream->readFlag() )
231   {
232      explosionId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
233   }
234
235   U32 i;
236   for( i=0; i<NUM_EMITTERS; i++ )
237   {
238      if( stream->readFlag() )
239      {
240         emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
241      }
242   }
243
244   for( i=0; i<NUM_TIME_KEYS; i++ )
245   {
246      stream->read( &colors[i] );
247   }
248
249   for( i=0; i<NUM_TIME_KEYS; i++ )
250   {
251      stream->read( &times[i] );
252   }
253
254   for( i=0; i<NUM_TEX; i++ )
255   {
256      textureName[i] = stream->readSTString();
257   }
258}
259
260//--------------------------------------------------------------------------
261// Preload data - load resources
262//--------------------------------------------------------------------------
263bool SplashData::preload(bool server, String &errorStr)
264{
265   if (Parent::preload(server, errorStr) == false)
266      return false;
267
268   if (!server)
269   {
270      S32 i;
271      for( i=0; i<NUM_EMITTERS; i++ )
272      {
273         if( !emitterList[i] && emitterIDList[i] != 0 )
274         {
275            if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
276            {
277               Con::errorf( ConsoleLogEntry::General, "SplashData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
278            }
279         }
280      }
281
282      for( i=0; i<NUM_TEX; i++ )
283      {
284         if (textureName[i] && textureName[i][0])
285         {
286            textureHandle[i] = GFXTexHandle(textureName[i], &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle[%d] (line %d)", __FUNCTION__, i, __LINE__) );
287         }
288      }
289   }
290
291   if( !explosion && explosionId != 0 )
292   {
293      if( !Sim::findObject(explosionId, explosion) )
294      {
295         Con::errorf(ConsoleLogEntry::General, "SplashData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId);
296      }
297   }
298
299   return true;
300}
301
302
303//--------------------------------------------------------------------------
304// Splash
305//--------------------------------------------------------------------------
306Splash::Splash()
307   : mDataBlock( NULL )
308{
309   dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
310
311   mDelayMS = 0;
312   mCurrMS = 0;
313   mRandAngle = 0;
314   mEndingMS = 1000;
315   mActive = false;
316   mRadius = 0.0;
317   mVelocity = 1.0;
318   mHeight = 0.0;
319   mTimeSinceLastRing = 0.0;
320   mDead = false;
321   mElapsedTime = 0.0;
322
323   mInitialNormal.set( 0.0, 0.0, 1.0 );
324   mFade = 0;
325   mFog = 0;
326   // Only allocated client side.
327   mNetFlags.set( IsGhost );
328}
329
330//--------------------------------------------------------------------------
331// Destructor
332//--------------------------------------------------------------------------
333Splash::~Splash()
334{
335}
336
337//--------------------------------------------------------------------------
338// Set initial state
339//--------------------------------------------------------------------------
340void Splash::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
341{
342   mInitialPosition = point;
343   mInitialNormal   = normal;
344   mFade            = fade;
345   mFog             = 0.0f;
346}
347
348
349//--------------------------------------------------------------------------
350// OnAdd
351//--------------------------------------------------------------------------
352bool Splash::onAdd()
353{
354   // first check if we have a server connection, if we dont then this is on the server
355   //  and we should exit, then check if the parent fails to add the object
356   NetConnection* conn = NetConnection::getConnectionToServer();
357   if(!conn || !Parent::onAdd())
358      return false;
359
360   if( !mDataBlock )
361   {
362      Con::errorf("Splash::onAdd - Fail - No datablock");
363      return false;
364   }
365
366   mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
367   mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
368
369   mVelocity = mDataBlock->velocity;
370   mHeight = mDataBlock->height;
371   mTimeSinceLastRing = 1.0 / mDataBlock->ejectionFreq;
372
373   for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
374   {
375      if( mDataBlock->emitterList[i] != NULL )
376      {
377         ParticleEmitter * pEmitter = new ParticleEmitter;
378         pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false );
379         if( !pEmitter->registerObject() )
380         {
381            Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
382            delete pEmitter;
383            pEmitter = NULL;
384         }
385         mEmitterList[i] = pEmitter;
386      }
387   }
388
389   spawnExplosion();
390
391   mObjBox.minExtents = Point3F( -1, -1, -1 );
392   mObjBox.maxExtents = Point3F(  1,  1,  1 );
393   resetWorldBox();
394
395   gClientSceneGraph->addObjectToScene(this);
396
397   removeFromProcessList();
398   ClientProcessList::get()->addObject(this);
399
400   conn->addObject(this);
401
402   return true;
403}
404
405//--------------------------------------------------------------------------
406// OnRemove
407//--------------------------------------------------------------------------
408void Splash::onRemove()
409{
410   for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
411   {
412      if( mEmitterList[i] )
413      {
414         mEmitterList[i]->deleteWhenEmpty();
415         mEmitterList[i] = NULL;
416      }
417   }
418
419   ringList.clear();
420
421   removeFromScene();
422
423   Parent::onRemove();
424}
425
426
427//--------------------------------------------------------------------------
428// On New Data Block
429//--------------------------------------------------------------------------
430bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload )
431{
432   mDataBlock = dynamic_cast<SplashData*>(dptr);
433   if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
434      return false;
435
436   scriptOnNewDataBlock();
437   return true;
438}
439
440
441//--------------------------------------------------------------------------
442// Process tick
443//--------------------------------------------------------------------------
444void Splash::processTick(const Move*)
445{
446   mCurrMS += TickMs;
447
448   if( isServerObject() )
449   {
450      if( mCurrMS >= mEndingMS )
451      {
452         mDead = true;
453         if( mCurrMS >= (mEndingMS + mDataBlock->ringLifetime * 1000) )
454         {
455            deleteObject();
456         }
457      }
458   }
459   else
460   {
461      if( mCurrMS >= mEndingMS )
462      {
463         mDead = true;
464      }
465   }
466}
467
468//--------------------------------------------------------------------------
469// Advance time
470//--------------------------------------------------------------------------
471void Splash::advanceTime(F32 dt)
472{
473   if (dt == 0.0)
474      return;
475
476   mElapsedTime += dt;
477
478   updateColor();
479   updateWave( dt );
480   updateEmitters( dt );
481   updateRings( dt );
482
483   if( !mDead )
484   {
485      emitRings( dt );
486   }
487}
488
489//----------------------------------------------------------------------------
490// Update emitters
491//----------------------------------------------------------------------------
492void Splash::updateEmitters( F32 dt )
493{
494   Point3F pos = getPosition();
495
496   for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
497   {
498      if( mEmitterList[i] )
499      {
500         mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (S32) (dt * 1000) );
501      }
502   }
503
504}
505
506//----------------------------------------------------------------------------
507// Update wave
508//----------------------------------------------------------------------------
509void Splash::updateWave( F32 dt )
510{
511   mVelocity += mDataBlock->acceleration * dt;
512   mRadius += mVelocity * dt;
513
514}
515
516//----------------------------------------------------------------------------
517// Update color
518//----------------------------------------------------------------------------
519void Splash::updateColor()
520{
521   for(SplashRingList::Iterator ring = ringList.begin(); ring != ringList.end(); ++ring)
522   {
523      F32 t = F32(ring->elapsedTime) / F32(ring->lifetime);
524
525      for( U32 i = 1; i < SplashData::NUM_TIME_KEYS; i++ )
526      {
527         if( mDataBlock->times[i] >= t )
528         {
529            F32 firstPart =   t - mDataBlock->times[i-1];
530            F32 total     =   (mDataBlock->times[i] -
531                               mDataBlock->times[i-1]);
532
533            firstPart /= total;
534
535            ring->color.interpolate( mDataBlock->colors[i-1],
536                                     mDataBlock->colors[i],
537                                     firstPart);
538            break;
539         }
540      }
541   }
542}
543
544//----------------------------------------------------------------------------
545// Create ring
546//----------------------------------------------------------------------------
547SplashRing Splash::createRing()
548{
549   SplashRing ring;
550   U32 numPoints = mDataBlock->numSegments + 1;
551
552   Point3F ejectionAxis( 0.0, 0.0, 1.0 );
553
554   Point3F axisx;
555   if (mFabs(ejectionAxis.z) < 0.999f)
556      mCross(ejectionAxis, Point3F(0, 0, 1), &axisx);
557   else
558      mCross(ejectionAxis, Point3F(0, 1, 0), &axisx);
559   axisx.normalize();
560
561   for( U32 i=0; i<numPoints; i++ )
562   {
563      F32 t = F32(i) / F32(numPoints);
564
565      AngAxisF thetaRot( axisx, mDataBlock->ejectionAngle * (M_PI / 180.0));
566      AngAxisF phiRot(   ejectionAxis, t * (M_PI * 2.0));
567
568      Point3F pointAxis = ejectionAxis;
569
570      MatrixF temp;
571      thetaRot.setMatrix(&temp);
572      temp.mulP(pointAxis);
573      phiRot.setMatrix(&temp);
574      temp.mulP(pointAxis);
575
576      Point3F startOffset = axisx;
577      temp.mulV( startOffset );
578      startOffset *= mDataBlock->startRadius;
579
580      SplashRingPoint point;
581      point.position = getPosition() + startOffset;
582      point.velocity = pointAxis * mDataBlock->velocity;
583
584      ring.points.push_back( point );
585   }
586
587   ring.color = mDataBlock->colors[0];
588   ring.lifetime = mDataBlock->ringLifetime;
589   ring.elapsedTime = 0.0;
590   ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 );
591
592   return ring;
593}
594
595//----------------------------------------------------------------------------
596// Emit rings
597//----------------------------------------------------------------------------
598void Splash::emitRings( F32 dt )
599{
600   mTimeSinceLastRing += dt;
601
602   S32 numNewRings = (S32) (mTimeSinceLastRing * F32(mDataBlock->ejectionFreq));
603
604   mTimeSinceLastRing -= numNewRings / mDataBlock->ejectionFreq;
605
606   for( S32 i=numNewRings-1; i>=0; i-- )
607   {
608      F32 t = F32(i) / F32(numNewRings);
609      t *= dt;
610      t += mTimeSinceLastRing;
611
612      SplashRing ring = createRing();
613      updateRing( ring, t );
614
615      ringList.pushBack( ring );
616   }
617}
618
619//----------------------------------------------------------------------------
620// Update rings
621//----------------------------------------------------------------------------
622void Splash::updateRings( F32 dt )
623{
624   SplashRingList::Iterator ring;
625   for(SplashRingList::Iterator i = ringList.begin(); i != ringList.end(); /*Trickiness*/)
626   {
627      ring = i++;
628      ring->elapsedTime += dt;
629
630      if( !ring->isActive() )
631      {
632         ringList.erase( ring );
633      }
634      else
635      {
636         updateRing( *ring, dt );
637      }
638   }
639}
640
641//----------------------------------------------------------------------------
642// Update ring
643//----------------------------------------------------------------------------
644void Splash::updateRing( SplashRing& ring, F32 dt )
645{
646   for( U32 i=0; i<ring.points.size(); i++ )
647   {
648      if( mDead )
649      {
650         Point3F vel = ring.points[i].velocity;
651         vel.normalize();
652         vel *= mDataBlock->acceleration;
653         ring.points[i].velocity += vel * dt;
654      }
655
656      ring.points[i].velocity += Point3F( 0.0f, 0.0f, -9.8f ) * dt;
657      ring.points[i].position += ring.points[i].velocity * dt;
658   }
659}
660
661//----------------------------------------------------------------------------
662// Explode
663//----------------------------------------------------------------------------
664void Splash::spawnExplosion()
665{
666   if( !mDataBlock->explosion ) return;
667
668   Explosion* pExplosion = new Explosion;
669   pExplosion->onNewDataBlock(mDataBlock->explosion, false);
670
671   MatrixF trans = getTransform();
672   trans.setPosition( getPosition() );
673
674   pExplosion->setTransform( trans );
675   pExplosion->setInitialState( trans.getPosition(), VectorF(0,0,1), 1);
676   if (!pExplosion->registerObject())
677      delete pExplosion;
678}
679
680//--------------------------------------------------------------------------
681// packUpdate
682//--------------------------------------------------------------------------
683U32 Splash::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
684{
685   U32 retMask = Parent::packUpdate(con, mask, stream);
686
687   if( stream->writeFlag(mask & GameBase::InitialUpdateMask) )
688   {
689      mathWrite(*stream, mInitialPosition);
690   }
691
692   return retMask;
693}
694
695//--------------------------------------------------------------------------
696// unpackUpdate
697//--------------------------------------------------------------------------
698void Splash::unpackUpdate(NetConnection* con, BitStream* stream)
699{
700   Parent::unpackUpdate(con, stream);
701
702   if( stream->readFlag() )
703   {
704      mathRead(*stream, &mInitialPosition);
705      setPosition( mInitialPosition );
706   }
707}
708