explosion.cpp

Engine/source/T3D/fx/explosion.cpp

More...

Public Defines

define

Public Variables

sgRandom (0xdeadbeef)

Public Functions

ConsoleDocClass(Explosion , "@brief The emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an explosion effect, with properties defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "<a href="/coding/class/classexplosiondata/">ExplosionData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "The object will initiate the explosion effects automatically after being " "added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>(GrenadeSubExplosion)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " offset=0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " emitter[0]=GrenadeExpSparkEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " lightStartRadius=4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightEndRadius=0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightStartColor=\"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>( GrenadeLauncherExplosion )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = GrenadeLauncherExplosionSound;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lifeTimeMS = 400; // Quick flash, short burn, and moderate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dispersal\n\n</a>" "   // Volume <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">particles\n</a>" "   particleEmitter = GrenadeExpFireEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleDensity = 75;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleRadius = 2.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Point <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">emission\n</a>" "   emitter[0] = GrenadeExpDustEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[1] = GrenadeExpSparksEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[2] = GrenadeExpSmokeEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Sub explosion <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects\n</a>" "   subExplosion[0] = GrenadeSubExplosion;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // <a href="/coding/class/classcamera/">Camera</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaking\n</a>" "   shakeCamera = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeFreq = \"10.0 11.0 9.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeAmp = \"15.0 15.0 15.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeDuration = 1.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeRadius = 20;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Exploding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">debris\n</a>" "   debris = GrenadeDebris;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMin = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMax = 60;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNum = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNumVariance = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocity = 25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocityVariance = 5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   lightStartRadius = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndRadius = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightNormalOffset = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function ServerPlayExplosion(%position, %datablock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play the given explosion on every <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   // The explosion will be transmitted as an event, not attached <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "   <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a>(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> = 0; %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> < ClientGroup.getCount(); %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>++)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      %client = ClientGroup.getObject(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function clientCmdPlayExplosion(%position, %effectDataBlock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play an explosion sent by the server. Make sure this function is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">defined\n</a>" "   // on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> (isObject(%effectDataBlock))\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classexplosion/">Explosion</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         position = %position;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         dataBlock = %effectDataBlock;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      };\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// schedule an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion\n</a>" "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )
ConsoleDocClass(ExplosionData , "@brief Defines the attributes of an Explosion: particleEmitters, debris , " "lighting and camera shake <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
DefineEngineFunction(calcExplosionCoverage , F32 , (Point3F pos, S32 id, U32 covMask) , "@brief Calculates how much an explosion effects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Use this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine how much damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> objects based on their " "distance from the explosion's center point, and whether the explosion is " "blocked by other <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" " @param pos Center position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @param <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> Id of the object of which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coverage.\n</a>" " @param covMask Mask of object types that may block the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @return Coverage <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> from 0(not affected by the explosion) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(fully affected)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" "% position)

Detailed Description

Public Defines

MaxLightRadius() 20

Public Variables

MRandomLCG sgRandom (0xdeadbeef)

Public Functions

ConsoleDocClass(Explosion , "@brief The emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an explosion effect, with properties defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "<a href="/coding/class/classexplosiondata/">ExplosionData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "The object will initiate the explosion effects automatically after being " "added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>(GrenadeSubExplosion)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " offset=0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " emitter[0]=GrenadeExpSparkEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " lightStartRadius=4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightEndRadius=0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightStartColor=\"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>( GrenadeLauncherExplosion )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = GrenadeLauncherExplosionSound;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lifeTimeMS = 400; // Quick flash, short burn, and moderate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dispersal\n\n</a>" "   // Volume <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">particles\n</a>" "   particleEmitter = GrenadeExpFireEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleDensity = 75;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleRadius = 2.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Point <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">emission\n</a>" "   emitter[0] = GrenadeExpDustEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[1] = GrenadeExpSparksEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[2] = GrenadeExpSmokeEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Sub explosion <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects\n</a>" "   subExplosion[0] = GrenadeSubExplosion;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // <a href="/coding/class/classcamera/">Camera</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaking\n</a>" "   shakeCamera = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeFreq = \"10.0 11.0 9.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeAmp = \"15.0 15.0 15.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeDuration = 1.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeRadius = 20;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Exploding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">debris\n</a>" "   debris = GrenadeDebris;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMin = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMax = 60;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNum = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNumVariance = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocity = 25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocityVariance = 5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   lightStartRadius = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndRadius = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightNormalOffset = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function ServerPlayExplosion(%position, %datablock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play the given explosion on every <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   // The explosion will be transmitted as an event, not attached <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "   <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a>(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> = 0; %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> < ClientGroup.getCount(); %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>++)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      %client = ClientGroup.getObject(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function clientCmdPlayExplosion(%position, %effectDataBlock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play an explosion sent by the server. Make sure this function is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">defined\n</a>" "   // on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> (isObject(%effectDataBlock))\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classexplosion/">Explosion</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         position = %position;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         dataBlock = %effectDataBlock;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      };\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// schedule an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion\n</a>" "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )

ConsoleDocClass(ExplosionData , "@brief Defines the attributes of an Explosion: particleEmitters, debris , " "lighting and camera shake <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )

DefineEngineFunction(calcExplosionCoverage , F32 , (Point3F pos, S32 id, U32 covMask) , "@brief Calculates how much an explosion effects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Use this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine how much damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> objects based on their " "distance from the explosion's center point, and whether the explosion is " "blocked by other <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" " @param pos Center position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @param <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> Id of the object of which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coverage.\n</a>" " @param covMask Mask of object types that may block the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @return Coverage <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> from 0(not affected by the explosion) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(fully affected)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" "% position)

IMPLEMENT_CO_DATABLOCK_V1(ExplosionData )

IMPLEMENT_CONOBJECT(Explosion )

   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//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "platform/platform.h"
  30#include "T3D/fx/explosion.h"
  31
  32#include "core/resourceManager.h"
  33#include "console/consoleTypes.h"
  34#include "console/typeValidators.h"
  35#include "sfx/sfxSystem.h"
  36#include "sfx/sfxTrack.h"
  37#include "sfx/sfxTypes.h"
  38#include "scene/sceneManager.h"
  39#include "scene/sceneRenderState.h"
  40#include "lighting/lightInfo.h"
  41#include "lighting/lightManager.h"
  42#include "core/stream/bitStream.h"
  43#include "sim/netConnection.h"
  44#include "ts/tsShape.h"
  45#include "ts/tsShapeInstance.h"
  46#include "math/mRandom.h"
  47#include "math/mathIO.h"
  48#include "math/mathUtils.h"
  49#include "T3D/debris.h"
  50#include "T3D/gameBase/gameConnection.h"
  51#include "T3D/fx/particleEmitter.h"
  52#include "T3D/fx/cameraFXMgr.h"
  53#include "T3D/debris.h"
  54#include "T3D/shapeBase.h"
  55#include "T3D/gameBase/gameProcess.h"
  56#include "renderInstance/renderPassManager.h"
  57#include "console/engineAPI.h"
  58
  59#include "sfx/sfxProfile.h"
  60
  61IMPLEMENT_CONOBJECT(Explosion);
  62
  63ConsoleDocClass( Explosion,
  64   "@brief The emitter for an explosion effect, with properties defined by a "
  65   "ExplosionData object.\n\n"
  66   "@ingroup FX\n"
  67   "The object will initiate the explosion effects automatically after being "
  68   "added to the simulation.\n"
  69   "@tsexample\n"
  70   "datablock ExplosionData( GrenadeSubExplosion )\n"
  71   "{\n"
  72   "   offset = 0.25;\n"
  73   "   emitter[0] = GrenadeExpSparkEmitter;\n\n"
  74   "   lightStartRadius = 4.0;\n"
  75   "   lightEndRadius = 0.0;\n"
  76   "   lightStartColor = \"0.9 0.7 0.7\";\n"
  77   "   lightEndColor = \"0.9 0.7 0.7\";\n"
  78   "   lightStartBrightness = 2.0;\n"
  79   "   lightEndBrightness = 0.0;\n"
  80   "};\n\n"
  81   "datablock ExplosionData( GrenadeLauncherExplosion )\n"
  82   "{\n"
  83   "   soundProfile = GrenadeLauncherExplosionSound;\n"
  84   "   lifeTimeMS = 400; // Quick flash, short burn, and moderate dispersal\n\n"
  85   "   // Volume particles\n"
  86   "   particleEmitter = GrenadeExpFireEmitter;\n"
  87   "   particleDensity = 75;\n"
  88   "   particleRadius = 2.25;\n\n"
  89   "   // Point emission\n"
  90   "   emitter[0] = GrenadeExpDustEmitter;\n"
  91   "   emitter[1] = GrenadeExpSparksEmitter;\n"
  92   "   emitter[2] = GrenadeExpSmokeEmitter;\n\n"
  93   "   // Sub explosion objects\n"
  94   "   subExplosion[0] = GrenadeSubExplosion;\n\n"
  95   "   // Camera Shaking\n"
  96   "   shakeCamera = true;\n"
  97   "   camShakeFreq = \"10.0 11.0 9.0\";\n"
  98   "   camShakeAmp = \"15.0 15.0 15.0\";\n"
  99   "   camShakeDuration = 1.5;\n"
 100   "   camShakeRadius = 20;\n\n"
 101   "   // Exploding debris\n"
 102   "   debris = GrenadeDebris;\n"
 103   "   debrisThetaMin = 10;\n"
 104   "   debrisThetaMax = 60;\n"
 105   "   debrisNum = 4;\n"
 106   "   debrisNumVariance = 2;\n"
 107   "   debrisVelocity = 25;\n"
 108   "   debrisVelocityVariance = 5;\n\n"
 109   "   lightStartRadius = 4.0;\n"
 110   "   lightEndRadius = 0.0;\n"
 111   "   lightStartColor = \"1.0 1.0 1.0\";\n"
 112   "   lightEndColor = \"1.0 1.0 1.0\";\n"
 113   "   lightStartBrightness = 4.0;\n"
 114   "   lightEndBrightness = 0.0;\n"
 115   "   lightNormalOffset = 2.0;\n"
 116   "};\n\n"
 117   "function ServerPlayExplosion(%position, %datablock)\n"
 118   "{\n"
 119   "   // Play the given explosion on every client.\n"
 120   "   // The explosion will be transmitted as an event, not attached to any object.\n"
 121   "   for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n"
 122   "   {\n"
 123   "      %client = ClientGroup.getObject(%idx);\n"
 124   "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\n"
 125   "   }\n"
 126   "}\n\n"
 127   "function clientCmdPlayExplosion(%position, %effectDataBlock)\n"
 128   "{\n"
 129   "   // Play an explosion sent by the server. Make sure this function is defined\n"
 130   "   // on the client.\n"
 131   "   if (isObject(%effectDataBlock))\n"
 132   "   {\n"
 133   "      new Explosion()\n"
 134   "      {\n"
 135   "         position = %position;\n"
 136   "         dataBlock = %effectDataBlock;\n"
 137   "      };\n"
 138   "   }\n"
 139   "}\n\n"
 140   "// schedule an explosion\n"
 141   "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\n"
 142   "@endtsexample"
 143);
 144
 145#define MaxLightRadius 20
 146
 147MRandomLCG sgRandom(0xdeadbeef);
 148
 149//WLE - Vince - The defaults are bad, the whole point of calling this function\
 150//is to determine the explosion coverage on a object.  Why would you want them
 151//To call this with a null for the ID?  In fact, it just returns a 1f if
 152//it can't find the object.  Seems useless to me.  Cause how can I apply
 153//damage to a object that doesn't exist?
 154
 155//I could possible see a use with passing in a null covMask, but even that
 156//sounds flaky because it will be 100 percent if your saying not to take
 157//any thing in consideration for coverage.  So I'm removing these defaults they are just bad.
 158
 159DefineEngineFunction(calcExplosionCoverage, F32, (Point3F pos, S32 id, U32 covMask),,
 160   "@brief Calculates how much an explosion effects a specific object.\n\n"
 161   "Use this to determine how much damage to apply to objects based on their "
 162   "distance from the explosion's center point, and whether the explosion is "
 163   "blocked by other objects.\n\n"
 164   "@param pos Center position of the explosion.\n"
 165   "@param id Id of the object of which to check coverage.\n"
 166   "@param covMask Mask of object types that may block the explosion.\n"
 167   "@return Coverage value from 0 (not affected by the explosion) to 1 (fully affected)\n\n"
 168   "@tsexample\n"
 169   "// Get the position of the explosion.\n"
 170   "%position = %explosion.getPosition();\n\n"
 171   "// Set a list of TypeMasks (defined in gameFunctioncs.cpp), seperated by the | character.\n"
 172   "%TypeMasks = $TypeMasks::StaticObjectType | $TypeMasks::ItemObjectType\n\n"
 173   "// Acquire the damage value from 0.0f - 1.0f.\n"
 174   "%coverage = calcExplosionCoverage( %position, %sceneObject, %TypeMasks );\n\n"
 175   "// Apply damage to object\n" 
 176   "%sceneObject.applyDamage( %coverage * 20 );\n"
 177   "@endtsexample\n"
 178   "@ingroup FX")
 179{
 180   Point3F center;
 181
 182   SceneObject* sceneObject = NULL;
 183   if (Sim::findObject(id, sceneObject) == false) {
 184      Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: couldn't find object: %d", id);
 185      return 1.0f;
 186   }
 187   if (sceneObject->isClientObject() || sceneObject->getContainer() == NULL) {
 188      Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: object is on the client, or not in the container system");
 189      return 1.0f;
 190   }
 191
 192   sceneObject->getObjBox().getCenter(&center);
 193   center.convolve(sceneObject->getScale());
 194   sceneObject->getTransform().mulP(center);
 195
 196   RayInfo rayInfo;
 197   sceneObject->disableCollision();
 198   if (sceneObject->getContainer()->castRay(pos, center, covMask, &rayInfo) == true) {
 199      // Try casting up and then out
 200      if (sceneObject->getContainer()->castRay(pos, pos + Point3F(0.0f, 0.0f, 1.0f), covMask, &rayInfo) == false)
 201      {
 202         if (sceneObject->getContainer()->castRay(pos + Point3F(0.0f, 0.0f, 1.0f), center, covMask, &rayInfo) == false)
 203         {
 204            sceneObject->enableCollision();
 205            return 1.0f;
 206         }
 207      }
 208
 209      sceneObject->enableCollision();
 210      return 0.0f;
 211   } else {
 212      sceneObject->enableCollision();
 213      return 1.0f;
 214   }
 215}
 216
 217//----------------------------------------------------------------------------
 218//
 219IMPLEMENT_CO_DATABLOCK_V1(ExplosionData);
 220
 221ConsoleDocClass( ExplosionData,
 222   "@brief Defines the attributes of an Explosion: particleEmitters, debris, "
 223   "lighting and camera shake effects.\n"
 224   "@ingroup FX\n"
 225);
 226
 227ExplosionData::ExplosionData()
 228{
 229   dtsFileName  = NULL;
 230   particleDensity = 10;
 231   particleRadius = 1.0f;
 232
 233   faceViewer   = false;
 234
 235   soundProfile      = NULL;
 236   particleEmitter   = NULL;
 237   particleEmitterId = 0;
 238
 239   explosionScale.set(1.0f, 1.0f, 1.0f);
 240   playSpeed = 1.0f;
 241
 242   explosionShape = NULL;
 243   explosionAnimation = -1;
 244
 245   dMemset( emitterList, 0, sizeof( emitterList ) );
 246   dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
 247   dMemset( debrisList, 0, sizeof( debrisList ) );
 248   dMemset( debrisIDList, 0, sizeof( debrisIDList ) );
 249
 250   debrisThetaMin = 0.0f;
 251   debrisThetaMax = 90.0f;
 252   debrisPhiMin = 0.0f;
 253   debrisPhiMax = 360.0f;
 254   debrisNum = 1;
 255   debrisNumVariance = 0;
 256   debrisVelocity = 2.0f;
 257   debrisVelocityVariance = 0.0f;
 258
 259   dMemset( explosionList, 0, sizeof( explosionList ) );
 260   dMemset( explosionIDList, 0, sizeof( explosionIDList ) );
 261
 262   delayMS = 0;
 263   delayVariance = 0;
 264   lifetimeMS = 1000;
 265   lifetimeVariance = 0;
 266   offset = 0.0f;
 267
 268   shakeCamera = false;
 269   camShakeFreq.set( 10.0f, 10.0f, 10.0f );
 270   camShakeAmp.set( 1.0f, 1.0f, 1.0f );
 271   camShakeDuration = 1.5f;
 272   camShakeRadius = 10.0f;
 273   camShakeFalloff = 10.0f;
 274
 275   for( U32 i=0; i<EC_NUM_TIME_KEYS; i++ )
 276   {
 277      times[i] = 1.0f;
 278   }
 279   times[0] = 0.0f;
 280
 281   for( U32 j=0; j<EC_NUM_TIME_KEYS; j++ )
 282   {
 283      sizes[j].set( 1.0f, 1.0f, 1.0f );
 284   }
 285
 286   //
 287   lightStartRadius = lightEndRadius = 0.0f;
 288   lightStartColor.set(1.0f,1.0f,1.0f);
 289   lightEndColor.set(1.0f,1.0f,1.0f);
 290   lightStartBrightness = 1.0f;
 291   lightEndBrightness = 1.0f;
 292   lightNormalOffset = 0.1f;
 293}
 294
 295//#define TRACK_EXPLOSION_DATA_CLONES
 296
 297#ifdef TRACK_EXPLOSION_DATA_CLONES
 298static int explosion_data_clones = 0;
 299#endif
 300
 301ExplosionData::ExplosionData(const ExplosionData& other, bool temp_clone) : GameBaseData(other, temp_clone)
 302{
 303#ifdef TRACK_EXPLOSION_DATA_CLONES
 304   explosion_data_clones++;
 305   if (explosion_data_clones == 1)
 306      Con::errorf("ExplosionData -- Clones are on the loose!");
 307#endif
 308
 309   dtsFileName = other.dtsFileName;
 310   faceViewer = other.faceViewer;
 311   particleDensity = other.particleDensity;
 312   particleRadius = other.particleRadius;
 313   soundProfile = other.soundProfile;
 314   particleEmitter = other.particleEmitter;
 315   particleEmitterId = other.particleEmitterId; // -- for pack/unpack of particleEmitter ptr 
 316   explosionScale = other.explosionScale;
 317   playSpeed = other.playSpeed;
 318   explosionShape = other.explosionShape; // -- TSShape loaded using dtsFileName
 319   explosionAnimation = other.explosionAnimation; // -- from explosionShape sequence "ambient"
 320   dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
 321   dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
 322   dMemcpy( debrisList, other.debrisList, sizeof( debrisList ) );
 323   dMemcpy( debrisIDList, other.debrisIDList, sizeof( debrisIDList ) ); // -- for pack/unpack of debrisList ptrs 
 324   debrisThetaMin = other.debrisThetaMin;
 325   debrisThetaMax = other.debrisThetaMax;
 326   debrisPhiMin = other.debrisPhiMin;
 327   debrisPhiMax = other.debrisPhiMax;
 328   debrisNum = other.debrisNum;
 329   debrisNumVariance = other.debrisNumVariance;
 330   debrisVelocity = other.debrisVelocity;
 331   debrisVelocityVariance = other.debrisVelocityVariance;
 332   dMemcpy( explosionList, other.explosionList, sizeof( explosionList ) );
 333   dMemcpy( explosionIDList, other.explosionIDList, sizeof( explosionIDList ) ); // -- for pack/unpack of explosionList ptrs
 334   delayMS = other.delayMS;
 335   delayVariance = other.delayVariance;
 336   lifetimeMS = other.lifetimeMS;
 337   lifetimeVariance = other.lifetimeVariance;
 338   offset = other.offset;
 339   dMemcpy( sizes, other.times, sizeof( sizes ) );
 340   dMemcpy( times, other.times, sizeof( times ) );
 341   shakeCamera = other.shakeCamera;
 342   camShakeFreq = other.camShakeFreq;
 343   camShakeAmp = other.camShakeAmp;
 344   camShakeDuration = other.camShakeDuration;
 345   camShakeRadius = other.camShakeRadius;
 346   camShakeFalloff = other.camShakeFalloff;
 347   lightStartRadius = other.lightStartRadius;
 348   lightEndRadius = other.lightEndRadius;
 349   lightStartColor = other.lightStartColor;
 350   lightEndColor = other.lightEndColor;
 351   lightStartBrightness = other.lightStartBrightness;
 352   lightEndBrightness = other.lightEndBrightness;
 353   lightNormalOffset = other.lightNormalOffset;
 354   // Note - Explosion calls mDataBlock->getName() in warning messages but
 355   //   that should be safe.
 356}
 357
 358ExplosionData::~ExplosionData()
 359{
 360   if (!isTempClone())
 361      return;
 362
 363   if (soundProfile && soundProfile->isTempClone())
 364   {
 365      delete soundProfile;
 366      soundProfile = 0;
 367   }
 368
 369   // particleEmitter, emitterList[*], debrisList[*], explosionList[*] will delete themselves
 370
 371#ifdef TRACK_EXPLOSION_DATA_CLONES
 372   if (explosion_data_clones > 0)
 373   {
 374      explosion_data_clones--;
 375      if (explosion_data_clones == 0)
 376         Con::errorf("ExplosionData -- Clones eliminated!");
 377   }
 378   else
 379      Con::errorf("ExplosionData -- Too many clones deleted!");
 380#endif
 381}
 382
 383ExplosionData* ExplosionData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
 384{
 385   if (!owner || getSubstitutionCount() == 0)
 386      return this;
 387
 388   ExplosionData* sub_explosion_db = new ExplosionData(*this, true);
 389   performSubstitutions(sub_explosion_db, owner, index);
 390
 391   return sub_explosion_db;
 392}
 393
 394void ExplosionData::initPersistFields()
 395{
 396   addField( "explosionShape", TypeShapeFilename, Offset(dtsFileName, ExplosionData),
 397      "@brief Optional DTS or DAE shape to place at the center of the explosion.\n\n"
 398      "The <i>ambient</i> animation of this model will be played automatically at "
 399      "the start of the explosion." );
 400   addField( "explosionScale", TypePoint3F, Offset(explosionScale, ExplosionData),
 401      "\"X Y Z\" scale factor applied to the explosionShape model at the start "
 402      "of the explosion." );
 403   addField( "playSpeed", TypeF32, Offset(playSpeed, ExplosionData),
 404      "Time scale at which to play the explosionShape <i>ambient</i> sequence." );
 405   addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, ExplosionData),
 406      "Non-looping sound effect that will be played at the start of the explosion." );
 407   addField( "faceViewer", TypeBool, Offset(faceViewer, ExplosionData),
 408      "Controls whether the visual effects of the explosion always face the camera." );
 409
 410   addField( "particleEmitter", TYPEID< ParticleEmitterData >(), Offset(particleEmitter, ExplosionData),
 411      "@brief Emitter used to generate a cloud of particles at the start of the explosion.\n\n"
 412      "Explosions can generate two different particle effects. The first is a "
 413      "single burst of particles at the start of the explosion emitted in a "
 414      "spherical cloud using particleEmitter.\n\n"
 415      "The second effect spawns the list of ParticleEmitters given by the emitter[] "
 416      "field. These emitters generate particles in the normal way throughout the "
 417      "lifetime of the explosion." );
 418   addField( "particleDensity", TypeS32, Offset(particleDensity, ExplosionData),
 419      "@brief Density of the particle cloud created at the start of the explosion.\n\n"
 420      "@see particleEmitter" );
 421   addField( "particleRadius", TypeF32, Offset(particleRadius, ExplosionData),
 422      "@brief Radial distance from the explosion center at which cloud particles "
 423      "are emitted.\n\n"
 424      "@see particleEmitter" );
 425   addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, ExplosionData), EC_NUM_EMITTERS,
 426      "@brief List of additional ParticleEmitterData objects to spawn with this "
 427      "explosion.\n\n"
 428      "@see particleEmitter" );
 429
 430   addField( "debris", TYPEID< DebrisData >(), Offset(debrisList, ExplosionData), EC_NUM_DEBRIS_TYPES,
 431      "List of DebrisData objects to spawn with this explosion." );
 432   addField( "debrisThetaMin", TypeF32, Offset(debrisThetaMin, ExplosionData),
 433      "Minimum angle, from the horizontal plane, to eject debris from." );
 434   addField( "debrisThetaMax", TypeF32, Offset(debrisThetaMax, ExplosionData),
 435      "Maximum angle, from the horizontal plane, to eject debris from." );
 436   addField( "debrisPhiMin", TypeF32, Offset(debrisPhiMin, ExplosionData),
 437      "Minimum reference angle, from the vertical plane, to eject debris from." );
 438   addField( "debrisPhiMax", TypeF32, Offset(debrisPhiMax, ExplosionData),
 439      "Maximum reference angle, from the vertical plane, to eject debris from." );
 440   addField( "debrisNum", TypeS32, Offset(debrisNum, ExplosionData),
 441      "Number of debris objects to create." );
 442   addField( "debrisNumVariance", TypeS32, Offset(debrisNumVariance, ExplosionData),
 443      "Variance in the number of debris objects to create (must be from 0 - debrisNum)." );
 444   addField( "debrisVelocity", TypeF32, Offset(debrisVelocity, ExplosionData),
 445      "Velocity to toss debris at." );
 446   addField( "debrisVelocityVariance", TypeF32, Offset(debrisVelocityVariance, ExplosionData),
 447      "Variance in the debris initial velocity (must be >= 0)." );
 448
 449   addField( "subExplosion", TYPEID< ExplosionData >(), Offset(explosionList, ExplosionData), EC_MAX_SUB_EXPLOSIONS,
 450      "List of additional ExplosionData objects to create at the start of the "
 451      "explosion." );
 452
 453   addField( "delayMS", TypeS32, Offset(delayMS, ExplosionData),
 454      "Amount of time, in milliseconds, to delay the start of the explosion effect "
 455      "from the creation of the Explosion object." );
 456   addField( "delayVariance", TypeS32, Offset(delayVariance, ExplosionData),
 457      "Variance, in milliseconds, of delayMS." );
 458   addField( "lifetimeMS", TypeS32, Offset(lifetimeMS, ExplosionData),
 459      "@brief Lifetime, in milliseconds, of the Explosion object.\n\n"
 460      "@note If explosionShape is defined and contains an <i>ambient</i> animation, "
 461      "this field is ignored, and the playSpeed scaled duration of the animation "
 462      "is used instead." );
 463   addField( "lifetimeVariance", TypeS32, Offset(lifetimeVariance, ExplosionData),
 464      "Variance, in milliseconds, of the lifetimeMS of the Explosion object.\n" );
 465   addField( "offset", TypeF32, Offset(offset, ExplosionData),
 466      "@brief Offset distance (in a random direction) of the center of the explosion "
 467      "from the Explosion object position.\n\n"
 468      "Most often used to create some variance in position for subExplosion effects." );
 469
 470   addField( "times", TypeF32, Offset(times, ExplosionData), EC_NUM_TIME_KEYS,
 471      "@brief Time keyframes used to scale the explosionShape model.\n\n"
 472      "Values should be in increasing order from 0.0 - 1.0, and correspond to "
 473      "the life of the Explosion where 0 is the beginning and 1 is the end of "
 474      "the explosion lifetime.\n"
 475      "@see lifetimeMS" );
 476   addField( "sizes", TypePoint3F, Offset(sizes, ExplosionData), EC_NUM_TIME_KEYS,
 477      "@brief \"X Y Z\" size keyframes used to scale the explosionShape model.\n\n"
 478      "The explosionShape (if defined) will be scaled using the times/sizes "
 479      "keyframes over the lifetime of the explosion.\n"
 480      "@see lifetimeMS" );
 481
 482   addField( "shakeCamera", TypeBool, Offset(shakeCamera, ExplosionData),
 483      "Controls whether the camera shakes during this explosion." );
 484   addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ExplosionData),
 485      "Frequency of camera shaking, defined in the \"X Y Z\" axes." );
 486   addField( "camShakeAmp", TypePoint3F, Offset(camShakeAmp, ExplosionData),
 487      "@brief Amplitude of camera shaking, defined in the \"X Y Z\" axes.\n\n"
 488      "Set any value to 0 to disable shaking in that axis." );
 489   addField( "camShakeDuration", TypeF32, Offset(camShakeDuration, ExplosionData),
 490      "Duration (in seconds) to shake the camera." );
 491   addField( "camShakeRadius", TypeF32, Offset(camShakeRadius, ExplosionData),
 492      "Radial distance that a camera's position must be within relative to the "
 493      "center of the explosion to be shaken." );
 494   addField( "camShakeFalloff", TypeF32, Offset(camShakeFalloff, ExplosionData),
 495      "Falloff value for the camera shake." );
 496
 497   addField( "lightStartRadius", TypeF32, Offset(lightStartRadius, ExplosionData),
 498      "@brief Initial radius of the PointLight created by this explosion.\n\n"
 499      "Radius is linearly interpolated from lightStartRadius to lightEndRadius "
 500      "over the lifetime of the explosion.\n"
 501      "@see lifetimeMS" );
 502   addField( "lightEndRadius", TypeF32, Offset(lightEndRadius, ExplosionData),
 503      "@brief Final radius of the PointLight created by this explosion.\n\n"
 504      "@see lightStartRadius" );
 505   addField( "lightStartColor", TypeColorF, Offset(lightStartColor, ExplosionData),
 506      "@brief Initial color of the PointLight created by this explosion.\n\n"
 507      "Color is linearly interpolated from lightStartColor to lightEndColor "
 508      "over the lifetime of the explosion.\n"
 509      "@see lifetimeMS" );
 510   addField( "lightEndColor", TypeColorF, Offset(lightEndColor, ExplosionData),
 511      "@brief Final color of the PointLight created by this explosion.\n\n"
 512      "@see lightStartColor" );
 513   addField( "lightStartBrightness", TypeF32, Offset(lightStartBrightness, ExplosionData),
 514      "@brief Initial brightness of the PointLight created by this explosion.\n\n"
 515      "Brightness is linearly interpolated from lightStartBrightness to "
 516      "lightEndBrightness over the lifetime of the explosion.\n"
 517      "@see lifetimeMS" );
 518   addField("lightEndBrightness", TypeF32, Offset(lightEndBrightness, ExplosionData),
 519      "@brief Final brightness of the PointLight created by this explosion.\n\n"
 520      "@see lightStartBrightness" );
 521   addField( "lightNormalOffset", TypeF32, Offset(lightNormalOffset, ExplosionData),
 522      "Distance (in the explosion normal direction) of the PointLight position "
 523      "from the explosion center." );
 524
 525   // disallow some field substitutions
 526   onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK
 527   onlyKeepClearSubstitutions("emitter");
 528   onlyKeepClearSubstitutions("particleEmitter");
 529   onlyKeepClearSubstitutions("soundProfile");
 530   onlyKeepClearSubstitutions("subExplosion");
 531   Parent::initPersistFields();
 532}
 533
 534bool ExplosionData::onAdd()
 535{
 536   if (Parent::onAdd() == false)
 537      return false;
 538
 539   if (explosionScale.x < 0.01f || explosionScale.y < 0.01f || explosionScale.z < 0.01f)
 540   {
 541      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s)::onAdd: ExplosionScale components must be >= 0.01", getName());
 542      explosionScale.x = explosionScale.x < 0.01f ? 0.01f : explosionScale.x;
 543      explosionScale.y = explosionScale.y < 0.01f ? 0.01f : explosionScale.y;
 544      explosionScale.z = explosionScale.z < 0.01f ? 0.01f : explosionScale.z;
 545   }
 546
 547   if (debrisThetaMin < 0.0f)
 548   {
 549      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin < 0.0", getName());
 550      debrisThetaMin = 0.0f;
 551   }
 552   if (debrisThetaMax > 180.0f)
 553   {
 554      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMax > 180.0", getName());
 555      debrisThetaMax = 180.0f;
 556   }
 557   if (debrisThetaMin > debrisThetaMax) {
 558      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin > debrisThetaMax", getName());
 559      debrisThetaMin = debrisThetaMax;
 560   }
 561   if (debrisPhiMin < 0.0f)
 562   {
 563      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin < 0.0", getName());
 564      debrisPhiMin = 0.0f;
 565   }
 566   if (debrisPhiMax > 360.0f)
 567   {
 568      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMax > 360.0", getName());
 569      debrisPhiMax = 360.0f;
 570   }
 571   if (debrisPhiMin > debrisPhiMax) {
 572      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin > debrisPhiMax", getName());
 573      debrisPhiMin = debrisPhiMax;
 574   }
 575   if (debrisNum > 1000) {
 576      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNum > 1000", getName());
 577      debrisNum = 1000;
 578   }
 579   if (debrisNumVariance > 1000) {
 580      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNumVariance > 1000", getName());
 581      debrisNumVariance = 1000;
 582   }
 583   if (debrisVelocity < 0.1f)
 584   {
 585      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocity < 0.1", getName());
 586      debrisVelocity = 0.1f;
 587   }
 588   if (debrisVelocityVariance > 1000) {
 589      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocityVariance > 1000", getName());
 590      debrisVelocityVariance = 1000;
 591   }
 592   if (playSpeed < 0.05f)
 593   {
 594      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) playSpeed < 0.05", getName());
 595      playSpeed = 0.05f;
 596   }
 597   if (lifetimeMS < 1) {
 598      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeMS < 1", getName());
 599      lifetimeMS = 1;
 600   }
 601   if (lifetimeVariance > lifetimeMS) {
 602      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeVariance > lifetimeMS", getName());
 603      lifetimeVariance = lifetimeMS;
 604   }
 605   if (delayMS < 0) {
 606      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayMS < 0", getName());
 607      delayMS = 0;
 608   }
 609   if (delayVariance > delayMS) {
 610      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayVariance > delayMS", getName());
 611      delayVariance = delayMS;
 612   }
 613   if (offset < 0.0f)
 614   {
 615      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) offset < 0.0", getName());
 616      offset = 0.0f;
 617   }
 618
 619   S32 i;
 620   for( i=0; i<EC_NUM_DEBRIS_TYPES; i++ )
 621   {
 622      if( !debrisList[i] && debrisIDList[i] != 0 )
 623      {
 624         if( !Sim::findObject( debrisIDList[i], debrisList[i] ) )
 625         {
 626            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(debris): 0x%x", debrisIDList[i] );
 627         }
 628      }
 629   }
 630
 631   for( i=0; i<EC_NUM_EMITTERS; i++ )
 632   {
 633      if( !emitterList[i] && emitterIDList[i] != 0 )
 634      {
 635         if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
 636         {
 637            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
 638         }
 639      }
 640   }
 641
 642   for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
 643   {
 644      if( !explosionList[k] && explosionIDList[k] != 0 )
 645      {
 646         if( Sim::findObject( explosionIDList[k], explosionList[k] ) == false)
 647         {
 648            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(explosion): 0x%x", explosionIDList[k] );
 649         }
 650      }
 651   }
 652
 653   return true;
 654}
 655
 656void ExplosionData::packData(BitStream* stream)
 657{
 658   Parent::packData(stream);
 659
 660   stream->writeString(dtsFileName);
 661
 662   sfxWrite( stream, soundProfile );
 663   if (stream->writeFlag(particleEmitter))
 664      stream->writeRangedU32(particleEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
 665
 666   stream->writeInt(particleDensity, 14);
 667   stream->write(particleRadius);
 668   stream->writeFlag(faceViewer);
 669   if(stream->writeFlag(explosionScale.x != 1 || explosionScale.y != 1 || explosionScale.z != 1))
 670   {
 671      stream->writeInt((S32)(explosionScale.x * 100), 16);
 672      stream->writeInt((S32)(explosionScale.y * 100), 16);
 673      stream->writeInt((S32)(explosionScale.z * 100), 16);
 674   }
 675   stream->writeInt((S32)(playSpeed * 20), 14);
 676   stream->writeRangedU32((U32)debrisThetaMin, 0, 180);
 677   stream->writeRangedU32((U32)debrisThetaMax, 0, 180);
 678   stream->writeRangedU32((U32)debrisPhiMin, 0, 360);
 679   stream->writeRangedU32((U32)debrisPhiMax, 0, 360);
 680   stream->writeRangedU32((U32)debrisNum, 0, 1000);
 681   stream->writeRangedU32(debrisNumVariance, 0, 1000);
 682   stream->writeInt((S32)(debrisVelocity * 10), 14);
 683   stream->writeRangedU32((U32)(debrisVelocityVariance * 10), 0, 10000);
 684   stream->writeInt(delayMS >> 5, 16);
 685   stream->writeInt(delayVariance >> 5, 16);
 686   stream->writeInt(lifetimeMS >> 5, 16);
 687   stream->writeInt(lifetimeVariance >> 5, 16);
 688   stream->write(offset);
 689
 690   stream->writeFlag( shakeCamera );
 691   stream->write(camShakeFreq.x);
 692   stream->write(camShakeFreq.y);
 693   stream->write(camShakeFreq.z);
 694   stream->write(camShakeAmp.x);
 695   stream->write(camShakeAmp.y);
 696   stream->write(camShakeAmp.z);
 697   stream->write(camShakeDuration);
 698   stream->write(camShakeRadius);
 699   stream->write(camShakeFalloff);
 700
 701   for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
 702   {
 703      if( stream->writeFlag( debrisList[j] ) )
 704      {
 705         stream->writeRangedU32( debrisList[j]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 706      }
 707   }
 708
 709   S32 i;
 710   for( i=0; i<EC_NUM_EMITTERS; i++ )
 711   {
 712      if( stream->writeFlag( emitterList[i] != NULL ) )
 713      {
 714         stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 715      }
 716   }
 717
 718   for( i=0; i<EC_MAX_SUB_EXPLOSIONS; i++ )
 719   {
 720      if( stream->writeFlag( explosionList[i] != NULL ) )
 721      {
 722         stream->writeRangedU32( explosionList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 723      }
 724   }
 725   U32 count;
 726   for(count = 0; count < EC_NUM_TIME_KEYS; count++)
 727      if(times[count] >= 1)
 728         break;
 729   count++;
 730   if(count > EC_NUM_TIME_KEYS)
 731      count = EC_NUM_TIME_KEYS;
 732
 733   stream->writeRangedU32(count, 0, EC_NUM_TIME_KEYS);
 734
 735   for( i=0; i<count; i++ )
 736      stream->writeFloat( times[i], 8 );
 737
 738   for( i=0; i<count; i++ )
 739   {
 740      stream->writeRangedU32((U32)(sizes[i].x * 100), 0, 16000);
 741      stream->writeRangedU32((U32)(sizes[i].y * 100), 0, 16000);
 742      stream->writeRangedU32((U32)(sizes[i].z * 100), 0, 16000);
 743   }
 744
 745   // Dynamic light info
 746   stream->writeFloat(lightStartRadius/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 747   stream->writeFloat(lightEndRadius/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 748   stream->writeFloat(lightStartColor.red,7);
 749   stream->writeFloat(lightStartColor.green,7);
 750   stream->writeFloat(lightStartColor.blue,7);
 751   stream->writeFloat(lightEndColor.red,7);
 752   stream->writeFloat(lightEndColor.green,7);
 753   stream->writeFloat(lightEndColor.blue,7);
 754   stream->writeFloat(lightStartBrightness/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 755   stream->writeFloat(lightEndBrightness/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 756   stream->write(lightNormalOffset);
 757}
 758
 759void ExplosionData::unpackData(BitStream* stream)
 760{
 761   Parent::unpackData(stream);
 762
 763   dtsFileName = stream->readSTString();
 764
 765   sfxRead( stream, &soundProfile );
 766
 767   if (stream->readFlag())
 768      particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 769   else
 770      particleEmitterId = 0;
 771
 772   particleDensity = stream->readInt(14);
 773   stream->read(&particleRadius);
 774   faceViewer = stream->readFlag();
 775   if(stream->readFlag())
 776   {
 777      explosionScale.x = stream->readInt(16) / 100.0f;
 778      explosionScale.y = stream->readInt(16) / 100.0f;
 779      explosionScale.z = stream->readInt(16) / 100.0f;
 780   }
 781   else
 782      explosionScale.set(1,1,1);
 783   playSpeed = stream->readInt(14) / 20.0f;
 784   debrisThetaMin = stream->readRangedU32(0, 180);
 785   debrisThetaMax = stream->readRangedU32(0, 180);
 786   debrisPhiMin = stream->readRangedU32(0, 360);
 787   debrisPhiMax = stream->readRangedU32(0, 360);
 788   debrisNum = stream->readRangedU32(0, 1000);
 789   debrisNumVariance = stream->readRangedU32(0, 1000);
 790
 791   debrisVelocity = stream->readInt(14) / 10.0f;
 792   debrisVelocityVariance = stream->readRangedU32(0, 10000) / 10.0f;
 793   delayMS = stream->readInt(16) << 5;
 794   delayVariance = stream->readInt(16) << 5;
 795   lifetimeMS = stream->readInt(16) << 5;
 796   lifetimeVariance = stream->readInt(16) << 5;
 797
 798   stream->read(&offset);
 799
 800   shakeCamera = stream->readFlag();
 801   stream->read(&camShakeFreq.x);
 802   stream->read(&camShakeFreq.y);
 803   stream->read(&camShakeFreq.z);
 804   stream->read(&camShakeAmp.x);
 805   stream->read(&camShakeAmp.y);
 806   stream->read(&camShakeAmp.z);
 807   stream->read(&camShakeDuration);
 808   stream->read(&camShakeRadius);
 809   stream->read(&camShakeFalloff);
 810
 811
 812   for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
 813   {
 814      if( stream->readFlag() )
 815      {
 816         debrisIDList[j] = (S32) stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 817      }
 818   }
 819
 820   U32 i;
 821   for( i=0; i<EC_NUM_EMITTERS; i++ )
 822   {
 823      if( stream->readFlag() )
 824      {
 825         emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 826      }
 827   }
 828
 829   for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
 830   {
 831      if( stream->readFlag() )
 832      {
 833         explosionIDList[k] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 834      }
 835   }
 836
 837   U32 count = stream->readRangedU32(0, EC_NUM_TIME_KEYS);
 838
 839   for( i=0; i<count; i++ )
 840      times[i] = stream->readFloat(8);
 841
 842   for( i=0; i<count; i++ )
 843   {
 844      sizes[i].x = stream->readRangedU32(0, 16000) / 100.0f;
 845      sizes[i].y = stream->readRangedU32(0, 16000) / 100.0f;
 846      sizes[i].z = stream->readRangedU32(0, 16000) / 100.0f;
 847   }
 848
 849   //
 850   lightStartRadius = stream->readFloat(8) * MaxLightRadius;
 851   lightEndRadius = stream->readFloat(8) * MaxLightRadius;
 852   lightStartColor.red = stream->readFloat(7);
 853   lightStartColor.green = stream->readFloat(7);
 854   lightStartColor.blue = stream->readFloat(7);
 855   lightEndColor.red = stream->readFloat(7);
 856   lightEndColor.green = stream->readFloat(7);
 857   lightEndColor.blue = stream->readFloat(7);
 858   lightStartBrightness = stream->readFloat(8) * MaxLightRadius;
 859   lightEndBrightness = stream->readFloat(8) * MaxLightRadius;
 860   stream->read( &lightNormalOffset );
 861}
 862
 863bool ExplosionData::preload(bool server, String &errorStr)
 864{
 865   if (Parent::preload(server, errorStr) == false)
 866      return false;
 867      
 868   if( !server )
 869   {
 870      String sfxErrorStr;
 871      if( !sfxResolve( &soundProfile, sfxErrorStr ) )
 872         Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", sfxErrorStr.c_str());
 873      if (!particleEmitter && particleEmitterId != 0)
 874         if (Sim::findObject(particleEmitterId, particleEmitter) == false)
 875            Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock");
 876   }
 877
 878   if (dtsFileName && dtsFileName[0]) {
 879      explosionShape = ResourceManager::get().load(dtsFileName);
 880      if (!bool(explosionShape)) {
 881         errorStr = String::ToString("ExplosionData: Couldn't load shape \"%s\"", dtsFileName);
 882         return false;
 883      }
 884
 885      // Resolve animations
 886      explosionAnimation = explosionShape->findSequence("ambient");
 887
 888      // Preload textures with a dummy instance...
 889      TSShapeInstance* pDummy = new TSShapeInstance(explosionShape, !server);
 890      delete pDummy;
 891
 892   } else {
 893      explosionShape     = NULL;
 894      explosionAnimation = -1;
 895   }
 896
 897   return true;
 898}
 899
 900
 901//--------------------------------------------------------------------------
 902//--------------------------------------
 903//
 904Explosion::Explosion()
 905   : mDataBlock( NULL )
 906{
 907   mTypeMask |= ExplosionObjectType | LightObjectType;
 908
 909   mExplosionInstance = NULL;
 910   mExplosionThread   = NULL;
 911
 912   dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
 913   mMainEmitter = NULL;
 914
 915   mFade = 1;
 916   mDelayMS = 0;
 917   mCurrMS = 0;
 918   mEndingMS = 1000;
 919   mActive = false;
 920   mCollideType = 0;
 921
 922   mInitialNormal.set( 0.0f, 0.0f, 1.0f );
 923   mRandAngle = sgRandom.randF( 0.0f, 1.0f ) * M_PI_F * 2.0f;
 924   mLight = LIGHTMGR->createLightInfo();
 925
 926   mNetFlags.set( IsGhost );
 927   ss_object = 0;
 928   ss_index = 0;
 929   mDataBlock = 0;
 930   soundProfile_clone = 0;
 931   mRandomVal = 0;
 932}
 933
 934Explosion::~Explosion()
 935{
 936   if( mExplosionInstance )
 937   {
 938      delete mExplosionInstance;
 939      mExplosionInstance = NULL;
 940      mExplosionThread   = NULL;
 941   }
 942   
 943   SAFE_DELETE(mLight);
 944   
 945   if (soundProfile_clone)
 946   { 
 947      delete soundProfile_clone;
 948      soundProfile_clone = 0;
 949   }
 950
 951   if (mDataBlock && mDataBlock->isTempClone())
 952   { 
 953      delete mDataBlock;
 954      mDataBlock = 0;
 955   }
 956}
 957
 958
 959void Explosion::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
 960{
 961   setPosition(point);
 962   mInitialNormal   = normal;
 963   mFade            = fade;
 964}
 965
 966//--------------------------------------------------------------------------
 967void Explosion::initPersistFields()
 968{
 969   Parent::initPersistFields();
 970   addField("initialNormal", TypePoint3F, Offset(mInitialNormal, Explosion), "Initial starting Normal.");
 971   //
 972}
 973
 974//--------------------------------------------------------------------------
 975bool Explosion::onAdd()
 976{
 977   // first check if we have a server connection, if we dont then this is on the server
 978   //  and we should exit, then check if the parent fails to add the object
 979   GameConnection *conn = GameConnection::getConnectionToServer();
 980   if ( !conn || !Parent::onAdd() )
 981      return false;
 982
 983   if( !mDataBlock )
 984   {
 985      Con::errorf("Explosion::onAdd - Fail - No datablok");
 986      return false;
 987   }
 988
 989   mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
 990   mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
 991
 992   if( mFabs( mDataBlock->offset ) > 0.001f )
 993   {
 994      MatrixF axisOrient = MathUtils::createOrientFromDir( mInitialNormal );
 995
 996      MatrixF trans = getTransform();
 997      Point3F randVec;
 998      randVec.x = sgRandom.randF( -1.0f, 1.0f );
 999      randVec.y = sgRandom.randF( 0.0f, 1.0f );
1000      randVec.z = sgRandom.randF( -1.0f, 1.0f );
1001      randVec.normalize();
1002      randVec *= mDataBlock->offset;
1003      axisOrient.mulV( randVec );
1004      trans.setPosition( trans.getPosition() + randVec );
1005      setTransform( trans );
1006   }
1007
1008   // shake camera
1009   if( mDataBlock->shakeCamera )
1010   {
1011      // first check if explosion is near player
1012      GameConnection* connection = GameConnection::getConnectionToServer();
1013      ShapeBase *obj = dynamic_cast<ShapeBase*>(connection->getControlObject());
1014
1015      bool applyShake = true;
1016
1017      if( obj )
1018      {
1019         ShapeBase* cObj = obj;
1020         while((cObj = cObj->getControlObject()) != 0)
1021         {
1022            if(cObj->useObjsEyePoint())
1023            {
1024               applyShake = false;
1025               break;
1026            }
1027         }
1028      }
1029
1030
1031      if( applyShake && obj )
1032      {
1033         VectorF diff = obj->getPosition() - getPosition();
1034         F32 dist = diff.len();
1035         if( dist < mDataBlock->camShakeRadius )
1036         {
1037            CameraShake *camShake = new CameraShake;
1038            camShake->setDuration( mDataBlock->camShakeDuration );
1039            camShake->setFrequency( mDataBlock->camShakeFreq );
1040
1041            F32 falloff =  dist / mDataBlock->camShakeRadius;
1042            falloff = 1.0f + falloff * 10.0f;
1043            falloff = 1.0f / (falloff * falloff);
1044
1045            VectorF shakeAmp = mDataBlock->camShakeAmp * falloff;
1046            camShake->setAmplitude( shakeAmp );
1047            camShake->setFalloff( mDataBlock->camShakeFalloff );
1048            camShake->init();
1049            gCamFXMgr.addFX( camShake );
1050         }
1051      }
1052   }
1053
1054
1055   if( mDelayMS == 0 )
1056   {
1057      if( !explode() )
1058      {
1059         return false;
1060      }
1061   }
1062
1063   gClientSceneGraph->addObjectToScene(this);
1064
1065   removeFromProcessList();
1066   ClientProcessList::get()->addObject(this);
1067
1068   mRandomVal = sgRandom.randF();
1069
1070   NetConnection* pNC = NetConnection::getConnectionToServer();
1071   AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
1072   pNC->addObject(this);
1073
1074   // Initialize the light structure and register as a dynamic light
1075   if (mDataBlock->lightStartRadius != 0.0f || mDataBlock->lightEndRadius)
1076   {
1077      mLight->setType( LightInfo::Point );
1078      mLight->setRange( mDataBlock->lightStartRadius );
1079      mLight->setColor( mDataBlock->lightStartColor );
1080   }
1081
1082   return true;
1083}
1084
1085void Explosion::onRemove()
1086{
1087   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
1088   {
1089      if( mEmitterList[i] )
1090      {
1091         mEmitterList[i]->deleteWhenEmpty();
1092         mEmitterList[i] = NULL;
1093      }
1094   }
1095
1096   if( mMainEmitter )
1097   {
1098      mMainEmitter->deleteWhenEmpty();
1099      mMainEmitter = NULL;
1100   }
1101
1102   removeFromScene();
1103
1104   Parent::onRemove();
1105}
1106
1107
1108bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload )
1109{
1110   mDataBlock = dynamic_cast<ExplosionData*>( dptr );
1111   if (!mDataBlock || !Parent::onNewDataBlock( dptr, reload ))
1112      return false;
1113
1114   if (mDataBlock->isTempClone())
1115      return true;
1116   scriptOnNewDataBlock();
1117   return true;
1118}
1119
1120
1121//--------------------------------------------------------------------------
1122void Explosion::prepRenderImage( SceneRenderState* state )
1123{
1124   prepBatchRender( state );
1125}
1126
1127void Explosion::setCurrentScale()
1128{
1129   F32 t = F32(mCurrMS) / F32(mEndingMS);
1130
1131   for( U32 i = 1; i < ExplosionData::EC_NUM_TIME_KEYS; i++ )
1132   {
1133      if( mDataBlock->times[i] >= t )
1134      {
1135         F32 firstPart =   t - mDataBlock->times[i-1];
1136         F32 total     =   mDataBlock->times[i] -
1137                           mDataBlock->times[i-1];
1138
1139         firstPart /= total;
1140
1141         mObjScale =      (mDataBlock->sizes[i-1] * (1.0f - firstPart)) +
1142                          (mDataBlock->sizes[i]   * firstPart);
1143
1144         return;
1145      }
1146   }
1147
1148}
1149
1150//--------------------------------------------------------------------------
1151// Make the explosion face the viewer (if desired)
1152//--------------------------------------------------------------------------
1153void Explosion::prepModelView(SceneRenderState* state)
1154{
1155   MatrixF rotMatrix( true );
1156   Point3F targetVector;
1157
1158   if( mDataBlock->faceViewer )
1159   {
1160      targetVector = getPosition() - state->getCameraPosition();
1161      targetVector.normalize();
1162
1163      // rotate explosion each time so it's a little different
1164      rotMatrix.set( EulerF( 0.0f, mRandAngle, 0.0f ) );
1165   }
1166   else
1167   {
1168      targetVector = mInitialNormal;
1169   }
1170
1171   MatrixF explOrient = MathUtils::createOrientFromDir( targetVector );
1172   explOrient.mul( rotMatrix );
1173   explOrient.setPosition( getPosition() );
1174
1175   setCurrentScale();
1176   explOrient.scale( mObjScale );
1177   GFX->setWorldMatrix( explOrient );
1178}
1179
1180//--------------------------------------------------------------------------
1181// Render object
1182//--------------------------------------------------------------------------
1183void Explosion::prepBatchRender(SceneRenderState* state)
1184{
1185   if ( !mExplosionInstance )
1186      return;
1187
1188   MatrixF proj = GFX->getProjectionMatrix();
1189   RectI viewport = GFX->getViewport();
1190
1191   // Set up our TS render state here.
1192   TSRenderState rdata;
1193   rdata.setSceneState( state );
1194
1195   // We might have some forward lit materials
1196   // so pass down a query to gather lights.
1197   LightQuery query;
1198   query.init( getWorldSphere() );
1199   rdata.setLightQuery( &query );
1200
1201   // render mesh
1202   GFX->pushWorldMatrix();
1203
1204   prepModelView( state );
1205
1206   mExplosionInstance->animate();
1207   mExplosionInstance->render( rdata );
1208
1209   GFX->popWorldMatrix();
1210   GFX->setProjectionMatrix( proj );
1211   GFX->setViewport( viewport );
1212}
1213
1214void Explosion::submitLights( LightManager *lm, bool staticLighting )
1215{
1216   if ( staticLighting )
1217      return;
1218
1219   // Update the light's info and add it to the scene, the light will
1220   // only be visible for this current frame.
1221   mLight->setPosition( getRenderTransform().getPosition() + mInitialNormal * mDataBlock->lightNormalOffset );
1222   F32 t = F32(mCurrMS) / F32(mEndingMS);
1223   mLight->setRange( mDataBlock->lightStartRadius +
1224      (mDataBlock->lightEndRadius - mDataBlock->lightStartRadius) * t );
1225   mLight->setColor( mDataBlock->lightStartColor +
1226      (mDataBlock->lightEndColor - mDataBlock->lightStartColor) * t );
1227   mLight->setBrightness( mDataBlock->lightStartBrightness +
1228      (mDataBlock->lightEndBrightness - mDataBlock->lightStartBrightness) * t );
1229
1230   lm->registerGlobalLight( mLight, this );
1231}
1232
1233
1234//--------------------------------------------------------------------------
1235void Explosion::processTick(const Move*)
1236{
1237   mCurrMS += TickMs;
1238
1239   if( mCurrMS >= mEndingMS )
1240   {
1241         deleteObject();
1242         return;
1243   }
1244         
1245   if( (mCurrMS > mDelayMS) && !mActive )
1246      explode();
1247}
1248
1249void Explosion::advanceTime(F32 dt)
1250{
1251   if (dt == 0.0f)
1252      return;
1253
1254   GameConnection* conn = GameConnection::getConnectionToServer();
1255   if(!conn)
1256      return;
1257
1258   updateEmitters( dt );
1259
1260   if( mExplosionInstance )
1261      mExplosionInstance->advanceTime(dt, mExplosionThread);
1262}
1263
1264//----------------------------------------------------------------------------
1265// Update emitters
1266//----------------------------------------------------------------------------
1267void Explosion::updateEmitters( F32 dt )
1268{
1269   Point3F pos = getPosition();
1270
1271   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
1272   {
1273      if( mEmitterList[i] )
1274      {
1275         mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0f, 0.0f, 0.0f ), (U32)(dt * 1000));
1276      }
1277   }
1278
1279}
1280
1281//----------------------------------------------------------------------------
1282// Launch Debris
1283//----------------------------------------------------------------------------
1284void Explosion::launchDebris( Point3F &axis )
1285{
1286   GameConnection* conn = GameConnection::getConnectionToServer();
1287   if(!conn)
1288      return;
1289
1290   bool hasDebris = false;
1291   for( S32 j=0; j<ExplosionData::EC_NUM_DEBRIS_TYPES; j++ )
1292   {
1293      if( mDataBlock->debrisList[j] )
1294      {
1295         hasDebris = true;
1296         break;
1297      }
1298   }
1299   if( !hasDebris )
1300   {
1301      return;
1302   }
1303
1304   Point3F axisx;
1305   if (mFabs(axis.z) < 0.999f)
1306      mCross(axis, Point3F(0.0f, 0.0f, 1.0f), &axisx);
1307   else
1308      mCross(axis, Point3F(0.0f, 1.0f, 0.0f), &axisx);
1309   axisx.normalize();
1310
1311   Point3F pos( 0.0f, 0.0f, 0.5f );
1312   pos += getPosition();
1313
1314
1315   U32 numDebris = mDataBlock->debrisNum + sgRandom.randI( -mDataBlock->debrisNumVariance, mDataBlock->debrisNumVariance );
1316
1317   for( S32 i=0; i<numDebris; i++ )
1318   {
1319
1320      Point3F launchDir = MathUtils::randomDir( axis, mDataBlock->debrisThetaMin, mDataBlock->debrisThetaMax,
1321                                                mDataBlock->debrisPhiMin, mDataBlock->debrisPhiMax );
1322
1323      F32 debrisVel = mDataBlock->debrisVelocity + mDataBlock->debrisVelocityVariance * sgRandom.randF( -1.0f, 1.0f );
1324
1325      launchDir *= debrisVel;
1326
1327      Debris *debris = new Debris;
1328      debris->setSubstitutionData(ss_object, ss_index);
1329      debris->setDataBlock(mDataBlock->debrisList[0]->cloneAndPerformSubstitutions(ss_object, ss_index));
1330      debris->setTransform( getTransform() );
1331      debris->init( pos, launchDir );
1332
1333      if( !debris->registerObject() )
1334      {
1335         Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
1336         delete debris;
1337         debris = NULL;
1338      }
1339   }
1340}
1341
1342//----------------------------------------------------------------------------
1343// Spawn sub explosions
1344//----------------------------------------------------------------------------
1345void Explosion::spawnSubExplosions()
1346{
1347   GameConnection* conn = GameConnection::getConnectionToServer();
1348   if(!conn)
1349      return;
1350
1351   for( S32 i=0; i<ExplosionData::EC_MAX_SUB_EXPLOSIONS; i++ )
1352   {
1353      if( mDataBlock->explosionList[i] )
1354      {
1355         MatrixF trans = getTransform();
1356         Explosion* pExplosion = new Explosion;
1357         pExplosion->setSubstitutionData(ss_object, ss_index);
1358         pExplosion->setDataBlock(mDataBlock->explosionList[i]->cloneAndPerformSubstitutions(ss_object, ss_index));
1359         pExplosion->setTransform( trans );
1360         pExplosion->setInitialState( trans.getPosition(), mInitialNormal, 1);
1361         if (!pExplosion->registerObject())
1362            delete pExplosion;
1363      }
1364   }
1365}
1366
1367//----------------------------------------------------------------------------
1368// Explode
1369//----------------------------------------------------------------------------
1370bool Explosion::explode()
1371{
1372   mActive = true;
1373
1374   GameConnection* conn = GameConnection::getConnectionToServer();
1375   if(!conn)
1376      return false;
1377
1378   launchDebris( mInitialNormal );
1379   spawnSubExplosions();
1380
1381   if (bool(mDataBlock->explosionShape) && mDataBlock->explosionAnimation != -1) {
1382      mExplosionInstance = new TSShapeInstance(mDataBlock->explosionShape, true);
1383
1384      mExplosionThread   = mExplosionInstance->addThread();
1385      mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0);
1386      mExplosionInstance->setTimeScale(mExplosionThread, mDataBlock->playSpeed);
1387
1388      mCurrMS   = 0;
1389      mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f);
1390
1391      mObjScale.convolve(mDataBlock->explosionScale);
1392      mObjBox = mDataBlock->explosionShape->mBounds;
1393      resetWorldBox();
1394   }
1395
1396   SFXProfile* sound_prof = dynamic_cast<SFXProfile*>(mDataBlock->soundProfile);
1397   if (sound_prof)
1398   {
1399      soundProfile_clone = sound_prof->cloneAndPerformSubstitutions(ss_object, ss_index);
1400      SFX->playOnce( soundProfile_clone, &getTransform() );
1401      if (!soundProfile_clone->isTempClone())
1402         soundProfile_clone = 0;
1403   }
1404
1405   if (mDataBlock->particleEmitter) {
1406      mMainEmitter = new ParticleEmitter;
1407      mMainEmitter->setDataBlock(mDataBlock->particleEmitter->cloneAndPerformSubstitutions(ss_object, ss_index));
1408      mMainEmitter->registerObject();
1409
1410      mMainEmitter->emitParticles(getPosition(), mInitialNormal, mDataBlock->particleRadius,
1411         Point3F::Zero, U32(mDataBlock->particleDensity * mFade));
1412   }
1413
1414   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
1415   {
1416      if( mDataBlock->emitterList[i] != NULL )
1417      {
1418         ParticleEmitter * pEmitter = new ParticleEmitter;
1419         pEmitter->setDataBlock(mDataBlock->emitterList[i]->cloneAndPerformSubstitutions(ss_object, ss_index));
1420         if( !pEmitter->registerObject() )
1421         {
1422            Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
1423            SAFE_DELETE(pEmitter);
1424         }
1425         mEmitterList[i] = pEmitter;
1426      }
1427   }
1428
1429   return true;
1430}
1431
1432