proximityMine.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "core/stream/bitStream.h"
  24. #include "console/console.h"
  25. #include "console/consoleTypes.h"
  26. #include "console/engineAPI.h"
  27. #include "sim/netConnection.h"
  28. #include "math/mMath.h"
  29. #include "math/mathIO.h"
  30. #include "math/mathUtils.h"
  31. #include "gfx/gfxTransformSaver.h"
  32. #include "gfx/gfxDrawUtil.h"
  33. #include "sfx/sfxTrack.h"
  34. #include "sfx/sfxTypes.h"
  35. #include "sfx/sfxSystem.h"
  36. #include "ts/tsShapeInstance.h"
  37. #include "T3D/fx/explosion.h"
  38. #include "T3D/proximityMine.h"
  39. #include "T3D/physics/physicsBody.h"
  40. const U32 sTriggerCollisionMask = ( VehicleObjectType | PlayerObjectType );
  41. static S32 gAutoDeleteTicks = 16; // Provides about half a second for all clients to be updated
  42. // before a mine is deleted on the server.
  43. //----------------------------------------------------------------------------
  44. IMPLEMENT_CO_DATABLOCK_V1( ProximityMineData );
  45. ConsoleDocClass( ProximityMineData,
  46. "@brief Stores common properties for a ProximityMine.\n\n"
  47. "@see ProximityMine\n"
  48. "@ingroup gameObjects\n"
  49. );
  50. IMPLEMENT_CALLBACK( ProximityMineData, onTriggered, void, ( ProximityMine* obj, SceneObject *target ),( obj, target ),
  51. "Callback invoked when an object triggers the ProximityMine.\n\n"
  52. "@param obj The ProximityMine object\n"
  53. "@param target The object that triggered the mine\n"
  54. "@note This callback is only invoked on the server.\n"
  55. "@see ProximityMine\n"
  56. );
  57. IMPLEMENT_CALLBACK( ProximityMineData, onExplode, void, ( ProximityMine* obj, Point3F pos ),( obj, pos ),
  58. "Callback invoked when a ProximityMine is about to explode.\n\n"
  59. "@param obj The ProximityMine object\n"
  60. "@param pos The position of the mine explosion\n"
  61. "@note This callback is only invoked on the server.\n"
  62. "@see ProximityMine\n"
  63. );
  64. ProximityMineData::ProximityMineData()
  65. : armingDelay( 0 ),
  66. armingSequence( -1 ),
  67. armingSound( NULL ),
  68. triggerRadius( 5.0f ),
  69. triggerSpeed( 1.0f ),
  70. autoTriggerDelay( 0 ),
  71. triggerOnOwner( false ),
  72. triggerDelay( 0 ),
  73. triggerSequence( -1 ),
  74. triggerSound( NULL ),
  75. explosionOffset( 0.05f )
  76. {
  77. }
  78. void ProximityMineData::initPersistFields()
  79. {
  80. addGroup( "Arming" );
  81. addField( "armingDelay", TypeF32, Offset(armingDelay, ProximityMineData),
  82. "Delay (in seconds) from when the mine is placed to when it becomes active." );
  83. addField( "armingSound", TypeSFXTrackName, Offset(armingSound, ProximityMineData),
  84. "Sound to play when the mine is armed (starts at the same time as "
  85. "the <i>armed</i> sequence if defined)." );
  86. endGroup( "Arming" );
  87. addGroup( "Triggering" );
  88. addField( "autoTriggerDelay", TypeF32, Offset(autoTriggerDelay, ProximityMineData),
  89. "@brief Delay (in seconds) from arming until the mine automatically "
  90. "triggers and explodes, even if no object has entered the trigger area.\n\n"
  91. "Set to 0 to disable." );
  92. addField( "triggerOnOwner", TypeBool, Offset(triggerOnOwner, ProximityMineData),
  93. "@brief Controls whether the mine can be triggered by the object that owns it.\n\n"
  94. "For example, a player could deploy mines that are only dangerous to other "
  95. "players and not himself." );
  96. addField( "triggerRadius", TypeF32, Offset(triggerRadius, ProximityMineData),
  97. "Distance at which an activated mine will detect other objects and explode." );
  98. addField( "triggerSpeed", TypeF32, Offset(triggerSpeed, ProximityMineData),
  99. "Speed above which moving objects within the trigger radius will trigger the mine" );
  100. addField( "triggerDelay", TypeF32, Offset(triggerDelay, ProximityMineData),
  101. "Delay (in seconds) from when the mine is triggered until it explodes." );
  102. addField( "triggerSound", TypeSFXTrackName, Offset(triggerSound, ProximityMineData),
  103. "Sound to play when the mine is triggered (starts at the same time as "
  104. "the <i>triggered</i> sequence if defined)." );
  105. endGroup( "Triggering" );
  106. addGroup( "Explosion" );
  107. addField( "explosionOffset", TypeF32, Offset(explosionOffset, ProximityMineData),
  108. "@brief Offset from the mine's origin where the explosion emanates from."
  109. "Sometimes a thrown mine may be slightly sunk into the ground. This can be just "
  110. "enough to cause the explosion to occur under the ground, especially on flat "
  111. "ground, which can end up blocking the explosion. This offset along the mine's "
  112. "'up' normal allows you to raise the explosion origin to a better height.");
  113. endGroup( "Explosion" );
  114. Parent::initPersistFields();
  115. }
  116. bool ProximityMineData::preload( bool server, String& errorStr )
  117. {
  118. if ( Parent::preload( server, errorStr ) == false )
  119. return false;
  120. if ( !server )
  121. {
  122. // Resolve sounds
  123. String sfxErrorStr;
  124. if( !sfxResolve( &armingSound, sfxErrorStr ) )
  125. Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() );
  126. if( !sfxResolve( &triggerSound, sfxErrorStr ) )
  127. Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() );
  128. }
  129. if ( mShape )
  130. {
  131. // Lookup animation sequences
  132. armingSequence = mShape->findSequence( "armed" );
  133. triggerSequence = mShape->findSequence( "triggered" );
  134. }
  135. return true;
  136. }
  137. void ProximityMineData::packData( BitStream* stream )
  138. {
  139. Parent::packData( stream );
  140. stream->write( armingDelay );
  141. sfxWrite( stream, armingSound );
  142. stream->write( autoTriggerDelay );
  143. stream->writeFlag( triggerOnOwner );
  144. stream->write( triggerRadius );
  145. stream->write( triggerSpeed );
  146. stream->write( triggerDelay );
  147. sfxWrite( stream, triggerSound );
  148. }
  149. void ProximityMineData::unpackData( BitStream* stream )
  150. {
  151. Parent::unpackData(stream);
  152. stream->read( &armingDelay );
  153. sfxRead( stream, &armingSound );
  154. stream->read( &autoTriggerDelay );
  155. triggerOnOwner = stream->readFlag();
  156. stream->read( &triggerRadius );
  157. stream->read( &triggerSpeed );
  158. stream->read( &triggerDelay );
  159. sfxRead( stream, &triggerSound );
  160. }
  161. //----------------------------------------------------------------------------
  162. IMPLEMENT_CO_NETOBJECT_V1( ProximityMine );
  163. ConsoleDocClass( ProximityMine,
  164. "@brief A simple proximity mine.\n\n"
  165. "Proximity mines can be deployed using the world editor or thrown by an "
  166. "in-game object. Once armed, any Player or Vehicle object that moves within "
  167. "the mine's trigger area will cause it to explode.\n\n"
  168. "Internally, the ProximityMine object transitions through the following states:\n"
  169. "<ol>\n"
  170. " <li><b>Thrown</b>: Mine has been thrown, but has not yet attached to a surface</li>\n"
  171. " <li><b>Deployed</b>: Mine has attached to a surface but is not yet armed. Start "
  172. "playing the #armingSound and <i>armed</i> sequence.</li>\n"
  173. " <li><b>Armed</b>: Mine is armed and will trigger if a Vehicle or Player object moves "
  174. "within the trigger area.</li>\n"
  175. " <li><b>Triggered</b>: Mine has been triggered and will explode soon. Invoke the "
  176. "onTriggered callback, and start playing the #triggerSound and <i>triggered</i> "
  177. "sequence.</li>\n"
  178. " <li><b>Exploded</b>: Mine has exploded and will be deleted on the server shortly. "
  179. "Invoke the onExplode callback on the server and generate the explosion effects "
  180. "on the client.</li>\n"
  181. "</ol>\n\n"
  182. "@note Proximity mines with the #static field set to true will start in the "
  183. "<b>Armed</b> state. Use this for mines placed with the World Editor.\n\n"
  184. "The shape used for the mine may optionally define the following sequences:\n"
  185. "<dl>\n"
  186. " <dt>armed</dt><dd>Sequence to play when the mine is deployed, but before "
  187. "it becomes active and triggerable (#armingDelay should be set appropriately).</dd>\n"
  188. " <dt>triggered</dt><dd>Sequence to play when the mine is triggered, just "
  189. "before it explodes (#triggerDelay should be set appropriately).<dd>\n"
  190. "</dl>\n\n"
  191. "@tsexample\n"
  192. "datablock ProximityMineData( SimpleMine )\n"
  193. "{\n"
  194. " // ShapeBaseData fields\n"
  195. " category = \"Weapon\";\n"
  196. " shapeFile = \"art/shapes/weapons/misc/proximityMine.dts\";\n\n"
  197. " // ItemData fields\n"
  198. " sticky = true;\n\n"
  199. " // ProximityMineData fields\n"
  200. " armingDelay = 0.5;\n"
  201. " armingSound = MineArmedSound;\n\n"
  202. " autoTriggerDelay = 0;\n"
  203. " triggerOnOwner = true;\n"
  204. " triggerRadius = 5.0;\n"
  205. " triggerSpeed = 1.0;\n"
  206. " triggerDelay = 0.5;\n"
  207. " triggerSound = MineTriggeredSound;\n"
  208. " explosion = RocketLauncherExplosion;\n\n"
  209. " // dynamic fields\n"
  210. " pickUpName = \"Proximity Mines\";\n"
  211. " maxInventory = 20;\n\n"
  212. " damageType = \"MineDamage\"; // type of damage applied to objects in radius\n"
  213. " radiusDamage = 30; // amount of damage to apply to objects in radius\n"
  214. " damageRadius = 8; // search radius to damage objects when exploding\n"
  215. " areaImpulse = 2000; // magnitude of impulse to apply to objects in radius\n"
  216. "};\n\n"
  217. "function ProximityMineData::onTriggered( %this, %obj, %target )\n"
  218. "{\n"
  219. " echo( %this.name SPC \"triggered by \" @ %target.getClassName() );\n"
  220. "}\n\n"
  221. "function ProximityMineData::onExplode( %this, %obj, %position )\n"
  222. "{\n"
  223. " // Damage objects within the mine's damage radius\n"
  224. " if ( %this.damageRadius > 0 )\n"
  225. " radiusDamage( %obj.sourceObject, %position, %this.damageRadius, %this.radiusDamage, %this.damageType, %this.areaImpulse );\n"
  226. "}\n\n"
  227. "function ProximityMineData::damage( %this, %obj, %position, %source, %amount, %damageType )\n"
  228. "{\n"
  229. " // Explode if any damage is applied to the mine\n"
  230. " %obj.schedule(50 + getRandom(50), explode);\n"
  231. "}\n\n"
  232. "%obj = new ProximityMine()\n"
  233. "{\n"
  234. " dataBlock = SimpleMine;\n"
  235. "};\n"
  236. "@endtsexample\n\n"
  237. "@see ProximityMineData\n"
  238. "@ingroup gameObjects\n"
  239. );
  240. ProximityMine::ProximityMine()
  241. {
  242. mTypeMask |= StaticShapeObjectType;
  243. mDataBlock = 0;
  244. mStickyCollisionPos.zero();
  245. mOwner = NULL;
  246. mState = Thrown;
  247. mStateTimeout = 0;
  248. mAnimThread = NULL;
  249. // For the Item class
  250. mSubclassItemHandlesScene = true;
  251. }
  252. ProximityMine::~ProximityMine()
  253. {
  254. }
  255. //----------------------------------------------------------------------------
  256. void ProximityMine::consoleInit()
  257. {
  258. Parent::consoleInit();
  259. Con::addVariable("$ProxMine::autoDeleteTicks", TypeS32, &gAutoDeleteTicks,
  260. "@brief Number of ticks until an exploded mine is deleted on the server.\n\n"
  261. "After a mine has exploded it remains in the server's scene graph for a time "
  262. "to allow its exploded state to be passed along to each client. This variable "
  263. "controls how long a mine remains before it is deleted. Any client that has not "
  264. "received the exploded state by then (perhaps due to lag) will not see any "
  265. "explosion produced by the mine.\n\n"
  266. "@ingroup GameObjects");
  267. }
  268. //----------------------------------------------------------------------------
  269. bool ProximityMine::onAdd()
  270. {
  271. if ( !Parent::onAdd() || !mDataBlock )
  272. return false;
  273. addToScene();
  274. if (isServerObject())
  275. scriptOnAdd();
  276. if ( mStatic )
  277. {
  278. // static mines are armed immediately
  279. mState = Deployed;
  280. mStateTimeout = 0;
  281. }
  282. return true;
  283. }
  284. bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload )
  285. {
  286. mDataBlock = dynamic_cast<ProximityMineData*>( dptr );
  287. if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
  288. return false;
  289. scriptOnNewDataBlock();
  290. return true;
  291. }
  292. void ProximityMine::onRemove()
  293. {
  294. scriptOnRemove();
  295. removeFromScene();
  296. Parent::onRemove();
  297. }
  298. //----------------------------------------------------------------------------
  299. void ProximityMine::setTransform( const MatrixF& mat )
  300. {
  301. ShapeBase::setTransform( mat ); // Skip Item::setTransform as it restricts rotation to the Z axis
  302. if ( !mStatic )
  303. {
  304. mAtRest = false;
  305. mAtRestCounter = 0;
  306. }
  307. if ( mPhysicsRep )
  308. mPhysicsRep->setTransform( getTransform() );
  309. setMaskBits( Item::RotationMask | Item::PositionMask | Item::NoWarpMask );
  310. }
  311. void ProximityMine::setDeployedPos( const Point3F& pos, const Point3F& normal )
  312. {
  313. // Align to deployed surface normal
  314. MatrixF mat( true );
  315. MathUtils::getMatrixFromUpVector( normal, &mat );
  316. mat.setPosition( pos + normal * mObjBox.minExtents.z );
  317. delta.pos = pos;
  318. delta.posVec.set(0, 0, 0);
  319. ShapeBase::setTransform( mat );
  320. if ( mPhysicsRep )
  321. mPhysicsRep->setTransform( getTransform() );
  322. setMaskBits( DeployedMask );
  323. }
  324. void ProximityMine::processTick( const Move* move )
  325. {
  326. Parent::processTick( move );
  327. // Process state machine
  328. mStateTimeout -= TickSec;
  329. State lastState = NumStates;;
  330. while ( mState != lastState )
  331. {
  332. lastState = mState;
  333. switch ( mState )
  334. {
  335. case Thrown:
  336. if ( mAtRest )
  337. {
  338. mState = Deployed;
  339. mStateTimeout = mDataBlock->armingDelay;
  340. // Get deployed position if mine was not stuck to another surface
  341. if ( mStickyCollisionPos.isZero() )
  342. {
  343. mObjToWorld.getColumn( 2, &mStickyCollisionNormal );
  344. mObjToWorld.getColumn( 3, &mStickyCollisionPos );
  345. }
  346. setDeployedPos( mStickyCollisionPos, mStickyCollisionNormal );
  347. if ( mDataBlock->armingSequence != -1 )
  348. {
  349. mAnimThread = mShapeInstance->addThread();
  350. mShapeInstance->setSequence( mAnimThread, mDataBlock->armingSequence, 0.0f );
  351. }
  352. if ( mDataBlock->armingSound )
  353. SFX->playOnce( mDataBlock->armingSound, &getRenderTransform() );
  354. }
  355. break;
  356. case Deployed:
  357. // Timeout into Armed state
  358. if ( mStateTimeout <= 0 )
  359. {
  360. mState = Armed;
  361. mStateTimeout = mDataBlock->autoTriggerDelay ? mDataBlock->autoTriggerDelay : F32_MAX;
  362. }
  363. break;
  364. case Armed:
  365. {
  366. // Check for objects within the trigger area
  367. Box3F triggerBox( mDataBlock->triggerRadius * 2 );
  368. triggerBox.setCenter( getTransform().getPosition() );
  369. SimpleQueryList sql;
  370. getContainer()->findObjects( triggerBox, sTriggerCollisionMask,
  371. SimpleQueryList::insertionCallback, &sql );
  372. for ( S32 i = 0; i < sql.mList.size(); i++ )
  373. {
  374. // Detect movement in the trigger area
  375. if ( ( sql.mList[i] == mOwner && !mDataBlock->triggerOnOwner ) ||
  376. sql.mList[i]->getVelocity().len() < mDataBlock->triggerSpeed )
  377. continue;
  378. // Mine has been triggered
  379. mShapeInstance->destroyThread( mAnimThread );
  380. mAnimThread = NULL;
  381. mState = Triggered;
  382. mStateTimeout = mDataBlock->triggerDelay;
  383. if ( mDataBlock->triggerSequence != -1 )
  384. {
  385. mAnimThread = mShapeInstance->addThread();
  386. mShapeInstance->setSequence( mAnimThread, mDataBlock->triggerSequence, 0.0f );
  387. }
  388. if ( mDataBlock->triggerSound )
  389. SFX->playOnce( mDataBlock->triggerSound, &getRenderTransform() );
  390. if ( isServerObject() )
  391. mDataBlock->onTriggered_callback( this, sql.mList[0] );
  392. }
  393. break;
  394. }
  395. case Triggered:
  396. // Timeout into exploded state
  397. if ( mStateTimeout <= 0 )
  398. {
  399. explode();
  400. }
  401. break;
  402. case Exploded:
  403. // Mine's delete themselves on the server after exploding
  404. if ( isServerObject() && ( mStateTimeout <= 0 ) )
  405. {
  406. deleteObject();
  407. return;
  408. }
  409. break;
  410. }
  411. }
  412. }
  413. //----------------------------------------------------------------------------
  414. void ProximityMine::explode()
  415. {
  416. // Make sure we don't explode twice
  417. if ( mState == Exploded )
  418. {
  419. return;
  420. }
  421. mState = Exploded;
  422. mStateTimeout = TickSec * gAutoDeleteTicks; // auto-delete on server N ticks after exploding
  423. // Move the explosion point slightly off the surface to avoid problems with radius damage
  424. Point3F normal = getTransform().getUpVector();
  425. Point3F explodePos = getTransform().getPosition() + normal * mDataBlock->explosionOffset;
  426. if ( isServerObject() )
  427. {
  428. // Do what the server needs to do, damage the surrounding objects, etc.
  429. mDataBlock->onExplode_callback( this, explodePos );
  430. setMaskBits( ExplosionMask );
  431. // Wait till the timeout to self delete. This gives the server object time
  432. // to get ghosted to the client
  433. }
  434. else
  435. {
  436. // Client just plays the explosion effect at the right place
  437. if ( mDataBlock->explosion )
  438. {
  439. Explosion *pExplosion = new Explosion;
  440. pExplosion->onNewDataBlock( mDataBlock->explosion, false );
  441. MatrixF xform( true );
  442. xform.setPosition( explodePos );
  443. pExplosion->setTransform( xform );
  444. pExplosion->setInitialState( explodePos, normal );
  445. pExplosion->setCollideType( sTriggerCollisionMask );
  446. if ( pExplosion->registerObject() == false )
  447. {
  448. Con::errorf( ConsoleLogEntry::General, "ProximityMine(%s)::explode: couldn't register explosion",
  449. mDataBlock->getName() );
  450. delete pExplosion;
  451. }
  452. }
  453. }
  454. }
  455. void ProximityMine::advanceTime( F32 dt )
  456. {
  457. Parent::advanceTime( dt );
  458. if ( mAnimThread )
  459. mShapeInstance->advancePos( dt, mAnimThread );
  460. }
  461. //----------------------------------------------------------------------------
  462. U32 ProximityMine::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
  463. {
  464. // Handle rotation ourselves (so it is not locked to the Z axis like for Items)
  465. U32 retMask = Parent::packUpdate( connection, mask & (~Item::RotationMask), stream );
  466. if ( stream->writeFlag( mask & Item::RotationMask ) )
  467. {
  468. QuatF rot( mObjToWorld );
  469. mathWrite( *stream, rot );
  470. }
  471. if ( stream->writeFlag( !mStatic && ( mask & DeployedMask ) && ( mState > Thrown ) ) )
  472. {
  473. mathWrite( *stream, mStickyCollisionPos );
  474. mathWrite( *stream, mStickyCollisionNormal );
  475. }
  476. stream->writeFlag( ( mask & ExplosionMask ) && ( mState == Exploded ) );
  477. return retMask;
  478. }
  479. void ProximityMine::unpackUpdate( NetConnection* connection, BitStream* stream )
  480. {
  481. Parent::unpackUpdate( connection, stream );
  482. // Item::RotationMask
  483. if ( stream->readFlag() )
  484. {
  485. QuatF rot;
  486. mathRead( *stream, &rot );
  487. Point3F pos = mObjToWorld.getPosition();
  488. rot.setMatrix( &mObjToWorld );
  489. mObjToWorld.setPosition( pos );
  490. }
  491. // !mStatic && ( mask & DeployedMask ) && ( mState > Thrown )
  492. if ( stream->readFlag() )
  493. {
  494. mathRead( *stream, &mStickyCollisionPos );
  495. mathRead( *stream, &mStickyCollisionNormal );
  496. mAtRest = true;
  497. setDeployedPos( mStickyCollisionPos, mStickyCollisionNormal );
  498. }
  499. // ( mask & ExplosionMask ) && ( mState == Exploded )
  500. if ( stream->readFlag() )
  501. {
  502. // start the explosion visuals on the client
  503. explode();
  504. }
  505. if ( mStatic && mState <= Deployed )
  506. {
  507. // static mines are armed immediately
  508. mState = Deployed;
  509. mStateTimeout = 0;
  510. }
  511. }
  512. //----------------------------------------------------------------------------
  513. void ProximityMine::prepRenderImage( SceneRenderState* state )
  514. {
  515. // Don't render the mine if exploded
  516. if ( mState == Exploded )
  517. return;
  518. // Use ShapeBase to render the 3D shape
  519. Parent::prepRenderImage( state );
  520. // Add a custom render instance to draw the trigger area
  521. if ( !state->isShadowPass() )
  522. {
  523. ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  524. ri->renderDelegate.bind( this, &ProximityMine::renderObject );
  525. ri->type = RenderPassManager::RIT_ObjectTranslucent;
  526. ri->translucentSort = true;
  527. ri->defaultKey = 1;
  528. state->getRenderPass()->addInst( ri );
  529. }
  530. }
  531. void ProximityMine::renderObject( ObjectRenderInst* ri,
  532. SceneRenderState* state,
  533. BaseMatInstance* overrideMat )
  534. {
  535. if ( overrideMat )
  536. return;
  537. /*
  538. // Render the trigger area
  539. if ( mState == Armed || mState == Triggered )
  540. {
  541. const ColorF drawColor(1, 0, 0, 0.05f);
  542. if ( drawColor.alpha > 0 )
  543. {
  544. GFXStateBlockDesc desc;
  545. desc.setZReadWrite( true, false );
  546. desc.setBlend( true );
  547. GFXTransformSaver saver;
  548. MatrixF mat = getRenderTransform();
  549. mat.scale( getScale() );
  550. GFX->getDrawUtil()->drawSphere( desc, mDataBlock->triggerRadius, mat.getPosition(),
  551. drawColor, true, false, &mat );
  552. }
  553. }
  554. */
  555. }
  556. //----------------------------------------------------------------------------
  557. DefineEngineMethod( ProximityMine, explode, void, (),,
  558. "@brief Manually cause the mine to explode.\n\n")
  559. {
  560. object->explode();
  561. }