proximityMine.cpp 22 KB

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