proximityMine.cpp 22 KB

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