PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/neo/game/Misc.cpp

https://bitbucket.org/peanut64/iodoom3-copy-original
C++ | 3149 lines | 1669 code | 424 blank | 1056 comment | 281 complexity | 62faa600753eb906f838d3f256c509ed MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, Unlicense, CC0-1.0, GPL-2.0
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. /*
  21. Various utility objects and functions.
  22. */
  23. #include "../idlib/precompiled.h"
  24. #pragma hdrstop
  25. #include "Game_local.h"
  26. /*
  27. ===============================================================================
  28. idSpawnableEntity
  29. A simple, spawnable entity with a model and no functionable ability of it's own.
  30. For example, it can be used as a placeholder during development, for marking
  31. locations on maps for script, or for simple placed models without any behavior
  32. that can be bound to other entities. Should not be subclassed.
  33. ===============================================================================
  34. */
  35. CLASS_DECLARATION( idEntity, idSpawnableEntity )
  36. END_CLASS
  37. /*
  38. ======================
  39. idSpawnableEntity::Spawn
  40. ======================
  41. */
  42. void idSpawnableEntity::Spawn() {
  43. // this just holds dict information
  44. }
  45. /*
  46. ===============================================================================
  47. idPlayerStart
  48. ===============================================================================
  49. */
  50. const idEventDef EV_TeleportStage( "<TeleportStage>", "e" );
  51. CLASS_DECLARATION( idEntity, idPlayerStart )
  52. EVENT( EV_Activate, idPlayerStart::Event_TeleportPlayer )
  53. EVENT( EV_TeleportStage, idPlayerStart::Event_TeleportStage )
  54. END_CLASS
  55. /*
  56. ===============
  57. idPlayerStart::idPlayerStart
  58. ================
  59. */
  60. idPlayerStart::idPlayerStart( void ) {
  61. teleportStage = 0;
  62. }
  63. /*
  64. ===============
  65. idPlayerStart::Spawn
  66. ================
  67. */
  68. void idPlayerStart::Spawn( void ) {
  69. teleportStage = 0;
  70. }
  71. /*
  72. ================
  73. idPlayerStart::Save
  74. ================
  75. */
  76. void idPlayerStart::Save( idSaveGame *savefile ) const {
  77. savefile->WriteInt( teleportStage );
  78. }
  79. /*
  80. ================
  81. idPlayerStart::Restore
  82. ================
  83. */
  84. void idPlayerStart::Restore( idRestoreGame *savefile ) {
  85. savefile->ReadInt( teleportStage );
  86. }
  87. /*
  88. ================
  89. idPlayerStart::ClientReceiveEvent
  90. ================
  91. */
  92. bool idPlayerStart::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  93. int entityNumber;
  94. switch( event ) {
  95. case EVENT_TELEPORTPLAYER: {
  96. entityNumber = msg.ReadBits( GENTITYNUM_BITS );
  97. idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[entityNumber] );
  98. if ( player != NULL && player->IsType( idPlayer::Type ) ) {
  99. Event_TeleportPlayer( player );
  100. }
  101. return true;
  102. }
  103. default: {
  104. return idEntity::ClientReceiveEvent( event, time, msg );
  105. }
  106. }
  107. return false;
  108. }
  109. /*
  110. ===============
  111. idPlayerStart::Event_TeleportStage
  112. FIXME: add functionality to fx system ( could be done with player scripting too )
  113. ================
  114. */
  115. void idPlayerStart::Event_TeleportStage( idEntity *_player ) {
  116. idPlayer *player;
  117. if ( !_player->IsType( idPlayer::Type ) ) {
  118. common->Warning( "idPlayerStart::Event_TeleportStage: entity is not an idPlayer\n" );
  119. return;
  120. }
  121. player = static_cast<idPlayer*>(_player);
  122. float teleportDelay = spawnArgs.GetFloat( "teleportDelay" );
  123. switch ( teleportStage ) {
  124. case 0:
  125. player->playerView.Flash( colorWhite, 125 );
  126. player->SetInfluenceLevel( INFLUENCE_LEVEL3 );
  127. player->SetInfluenceView( spawnArgs.GetString( "mtr_teleportFx" ), NULL, 0.0f, NULL );
  128. gameSoundWorld->FadeSoundClasses( 0, -20.0f, teleportDelay );
  129. player->StartSound( "snd_teleport_start", SND_CHANNEL_BODY2, 0, false, NULL );
  130. teleportStage++;
  131. PostEventSec( &EV_TeleportStage, teleportDelay, player );
  132. break;
  133. case 1:
  134. gameSoundWorld->FadeSoundClasses( 0, 0.0f, 0.25f );
  135. teleportStage++;
  136. PostEventSec( &EV_TeleportStage, 0.25f, player );
  137. break;
  138. case 2:
  139. player->SetInfluenceView( NULL, NULL, 0.0f, NULL );
  140. TeleportPlayer( player );
  141. player->StopSound( SND_CHANNEL_BODY2, false );
  142. player->SetInfluenceLevel( INFLUENCE_NONE );
  143. teleportStage = 0;
  144. break;
  145. default:
  146. break;
  147. }
  148. }
  149. /*
  150. ===============
  151. idPlayerStart::TeleportPlayer
  152. ================
  153. */
  154. void idPlayerStart::TeleportPlayer( idPlayer *player ) {
  155. float pushVel = spawnArgs.GetFloat( "push", "300" );
  156. float f = spawnArgs.GetFloat( "visualEffect", "0" );
  157. const char *viewName = spawnArgs.GetString( "visualView", "" );
  158. idEntity *ent = viewName ? gameLocal.FindEntity( viewName ) : NULL;
  159. if ( f && ent ) {
  160. // place in private camera view for some time
  161. // the entity needs to teleport to where the camera view is to have the PVS right
  162. player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this );
  163. player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL );
  164. player->SetPrivateCameraView( static_cast<idCamera*>(ent) );
  165. // the player entity knows where to spawn from the previous Teleport call
  166. if ( !gameLocal.isClient ) {
  167. player->PostEventSec( &EV_Player_ExitTeleporter, f );
  168. }
  169. } else {
  170. // direct to exit, Teleport will take care of the killbox
  171. player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL );
  172. // multiplayer hijacked this entity, so only push the player in multiplayer
  173. if ( gameLocal.isMultiplayer ) {
  174. player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel );
  175. }
  176. }
  177. }
  178. /*
  179. ===============
  180. idPlayerStart::Event_TeleportPlayer
  181. ================
  182. */
  183. void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) {
  184. idPlayer *player;
  185. if ( activator->IsType( idPlayer::Type ) ) {
  186. player = static_cast<idPlayer*>( activator );
  187. } else {
  188. player = gameLocal.GetLocalPlayer();
  189. }
  190. if ( player ) {
  191. if ( spawnArgs.GetBool( "visualFx" ) ) {
  192. teleportStage = 0;
  193. Event_TeleportStage( player );
  194. } else {
  195. if ( gameLocal.isServer ) {
  196. idBitMsg msg;
  197. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  198. msg.Init( msgBuf, sizeof( msgBuf ) );
  199. msg.BeginWriting();
  200. msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
  201. ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false, -1 );
  202. }
  203. TeleportPlayer( player );
  204. }
  205. }
  206. }
  207. /*
  208. ===============================================================================
  209. idActivator
  210. ===============================================================================
  211. */
  212. CLASS_DECLARATION( idEntity, idActivator )
  213. EVENT( EV_Activate, idActivator::Event_Activate )
  214. END_CLASS
  215. /*
  216. ===============
  217. idActivator::Save
  218. ================
  219. */
  220. void idActivator::Save( idSaveGame *savefile ) const {
  221. savefile->WriteBool( stay_on );
  222. }
  223. /*
  224. ===============
  225. idActivator::Restore
  226. ================
  227. */
  228. void idActivator::Restore( idRestoreGame *savefile ) {
  229. savefile->ReadBool( stay_on );
  230. if ( stay_on ) {
  231. BecomeActive( TH_THINK );
  232. }
  233. }
  234. /*
  235. ===============
  236. idActivator::Spawn
  237. ================
  238. */
  239. void idActivator::Spawn( void ) {
  240. bool start_off;
  241. spawnArgs.GetBool( "stay_on", "0", stay_on );
  242. spawnArgs.GetBool( "start_off", "0", start_off );
  243. GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f );
  244. GetPhysics()->SetContents( 0 );
  245. if ( !start_off ) {
  246. BecomeActive( TH_THINK );
  247. }
  248. }
  249. /*
  250. ===============
  251. idActivator::Think
  252. ================
  253. */
  254. void idActivator::Think( void ) {
  255. RunPhysics();
  256. if ( thinkFlags & TH_THINK ) {
  257. if ( TouchTriggers() ) {
  258. if ( !stay_on ) {
  259. BecomeInactive( TH_THINK );
  260. }
  261. }
  262. }
  263. Present();
  264. }
  265. /*
  266. ===============
  267. idActivator::Activate
  268. ================
  269. */
  270. void idActivator::Event_Activate( idEntity *activator ) {
  271. if ( thinkFlags & TH_THINK ) {
  272. BecomeInactive( TH_THINK );
  273. } else {
  274. BecomeActive( TH_THINK );
  275. }
  276. }
  277. /*
  278. ===============================================================================
  279. idPathCorner
  280. ===============================================================================
  281. */
  282. CLASS_DECLARATION( idEntity, idPathCorner )
  283. EVENT( AI_RandomPath, idPathCorner::Event_RandomPath )
  284. END_CLASS
  285. /*
  286. =====================
  287. idPathCorner::Spawn
  288. =====================
  289. */
  290. void idPathCorner::Spawn( void ) {
  291. }
  292. /*
  293. =====================
  294. idPathCorner::DrawDebugInfo
  295. =====================
  296. */
  297. void idPathCorner::DrawDebugInfo( void ) {
  298. idEntity *ent;
  299. idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) );
  300. for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  301. if ( !ent->IsType( idPathCorner::Type ) ) {
  302. continue;
  303. }
  304. idVec3 org = ent->GetPhysics()->GetOrigin();
  305. gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 );
  306. }
  307. }
  308. /*
  309. ============
  310. idPathCorner::RandomPath
  311. ============
  312. */
  313. idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) {
  314. int i;
  315. int num;
  316. int which;
  317. idEntity *ent;
  318. idPathCorner *path[ MAX_GENTITIES ];
  319. num = 0;
  320. for( i = 0; i < source->targets.Num(); i++ ) {
  321. ent = source->targets[ i ].GetEntity();
  322. if ( ent && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) {
  323. path[ num++ ] = static_cast<idPathCorner *>( ent );
  324. if ( num >= MAX_GENTITIES ) {
  325. break;
  326. }
  327. }
  328. }
  329. if ( !num ) {
  330. return NULL;
  331. }
  332. which = gameLocal.random.RandomInt( num );
  333. return path[ which ];
  334. }
  335. /*
  336. =====================
  337. idPathCorner::Event_RandomPath
  338. =====================
  339. */
  340. void idPathCorner::Event_RandomPath( void ) {
  341. idPathCorner *path;
  342. path = RandomPath( this, NULL );
  343. idThread::ReturnEntity( path );
  344. }
  345. /*
  346. ===============================================================================
  347. idDamagable
  348. ===============================================================================
  349. */
  350. const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" );
  351. CLASS_DECLARATION( idEntity, idDamagable )
  352. EVENT( EV_Activate, idDamagable::Event_BecomeBroken )
  353. EVENT( EV_RestoreDamagable, idDamagable::Event_RestoreDamagable )
  354. END_CLASS
  355. /*
  356. ================
  357. idDamagable::idDamagable
  358. ================
  359. */
  360. idDamagable::idDamagable( void ) {
  361. count = 0;
  362. nextTriggerTime = 0;
  363. }
  364. /*
  365. ================
  366. idDamagable::Save
  367. ================
  368. */
  369. void idDamagable::Save( idSaveGame *savefile ) const {
  370. savefile->WriteInt( count );
  371. savefile->WriteInt( nextTriggerTime );
  372. }
  373. /*
  374. ================
  375. idDamagable::Restore
  376. ================
  377. */
  378. void idDamagable::Restore( idRestoreGame *savefile ) {
  379. savefile->ReadInt( count );
  380. savefile->ReadInt( nextTriggerTime );
  381. }
  382. /*
  383. ================
  384. idDamagable::Spawn
  385. ================
  386. */
  387. void idDamagable::Spawn( void ) {
  388. idStr broken;
  389. health = spawnArgs.GetInt( "health", "5" );
  390. spawnArgs.GetInt( "count", "1", count );
  391. nextTriggerTime = 0;
  392. // make sure the model gets cached
  393. spawnArgs.GetString( "broken", "", broken );
  394. if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) {
  395. gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() );
  396. }
  397. fl.takedamage = true;
  398. GetPhysics()->SetContents( CONTENTS_SOLID );
  399. }
  400. /*
  401. ================
  402. idDamagable::BecomeBroken
  403. ================
  404. */
  405. void idDamagable::BecomeBroken( idEntity *activator ) {
  406. float forceState;
  407. int numStates;
  408. int cycle;
  409. float wait;
  410. if ( gameLocal.time < nextTriggerTime ) {
  411. return;
  412. }
  413. spawnArgs.GetFloat( "wait", "0.1", wait );
  414. nextTriggerTime = gameLocal.time + SEC2MS( wait );
  415. if ( count > 0 ) {
  416. count--;
  417. if ( !count ) {
  418. fl.takedamage = false;
  419. } else {
  420. health = spawnArgs.GetInt( "health", "5" );
  421. }
  422. }
  423. idStr broken;
  424. spawnArgs.GetString( "broken", "", broken );
  425. if ( broken.Length() ) {
  426. SetModel( broken );
  427. }
  428. // offset the start time of the shader to sync it to the gameLocal time
  429. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  430. spawnArgs.GetInt( "numstates", "1", numStates );
  431. spawnArgs.GetInt( "cycle", "0", cycle );
  432. spawnArgs.GetFloat( "forcestate", "0", forceState );
  433. // set the state parm
  434. if ( cycle ) {
  435. renderEntity.shaderParms[ SHADERPARM_MODE ]++;
  436. if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) {
  437. renderEntity.shaderParms[ SHADERPARM_MODE ] = 0;
  438. }
  439. } else if ( forceState ) {
  440. renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState;
  441. } else {
  442. renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1;
  443. }
  444. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  445. ActivateTargets( activator );
  446. if ( spawnArgs.GetBool( "hideWhenBroken" ) ) {
  447. Hide();
  448. PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time );
  449. BecomeActive( TH_THINK );
  450. }
  451. }
  452. /*
  453. ================
  454. idDamagable::Killed
  455. ================
  456. */
  457. void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  458. if ( gameLocal.time < nextTriggerTime ) {
  459. health += damage;
  460. return;
  461. }
  462. BecomeBroken( attacker );
  463. }
  464. /*
  465. ================
  466. idDamagable::Event_BecomeBroken
  467. ================
  468. */
  469. void idDamagable::Event_BecomeBroken( idEntity *activator ) {
  470. BecomeBroken( activator );
  471. }
  472. /*
  473. ================
  474. idDamagable::Event_RestoreDamagable
  475. ================
  476. */
  477. void idDamagable::Event_RestoreDamagable( void ) {
  478. health = spawnArgs.GetInt( "health", "5" );
  479. Show();
  480. }
  481. /*
  482. ===============================================================================
  483. idExplodable
  484. ===============================================================================
  485. */
  486. CLASS_DECLARATION( idEntity, idExplodable )
  487. EVENT( EV_Activate, idExplodable::Event_Explode )
  488. END_CLASS
  489. /*
  490. ================
  491. idExplodable::Spawn
  492. ================
  493. */
  494. void idExplodable::Spawn( void ) {
  495. Hide();
  496. }
  497. /*
  498. ================
  499. idExplodable::Event_Explode
  500. ================
  501. */
  502. void idExplodable::Event_Explode( idEntity *activator ) {
  503. const char *temp;
  504. if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) {
  505. gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp );
  506. }
  507. StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
  508. // Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms
  509. renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
  510. renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
  511. renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
  512. renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
  513. renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  514. renderEntity.shaderParms[SHADERPARM_DIVERSITY] = 0.0f;
  515. Show();
  516. PostEventMS( &EV_Remove, 2000 );
  517. ActivateTargets( activator );
  518. }
  519. /*
  520. ===============================================================================
  521. idSpring
  522. ===============================================================================
  523. */
  524. CLASS_DECLARATION( idEntity, idSpring )
  525. EVENT( EV_PostSpawn, idSpring::Event_LinkSpring )
  526. END_CLASS
  527. /*
  528. ================
  529. idSpring::Think
  530. ================
  531. */
  532. void idSpring::Think( void ) {
  533. idVec3 start, end, origin;
  534. idMat3 axis;
  535. // run physics
  536. RunPhysics();
  537. if ( thinkFlags & TH_THINK ) {
  538. // evaluate force
  539. spring.Evaluate( gameLocal.time );
  540. start = p1;
  541. if ( ent1->GetPhysics() ) {
  542. axis = ent1->GetPhysics()->GetAxis();
  543. origin = ent1->GetPhysics()->GetOrigin();
  544. start = origin + start * axis;
  545. }
  546. end = p2;
  547. if ( ent2->GetPhysics() ) {
  548. axis = ent2->GetPhysics()->GetAxis();
  549. origin = ent2->GetPhysics()->GetOrigin();
  550. end = origin + p2 * axis;
  551. }
  552. gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true );
  553. }
  554. Present();
  555. }
  556. /*
  557. ================
  558. idSpring::Event_LinkSpring
  559. ================
  560. */
  561. void idSpring::Event_LinkSpring( void ) {
  562. idStr name1, name2;
  563. spawnArgs.GetString( "ent1", "", name1 );
  564. spawnArgs.GetString( "ent2", "", name2 );
  565. if ( name1.Length() ) {
  566. ent1 = gameLocal.FindEntity( name1 );
  567. if ( !ent1 ) {
  568. gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() );
  569. }
  570. }
  571. else {
  572. ent1 = gameLocal.entities[ENTITYNUM_WORLD];
  573. }
  574. if ( name2.Length() ) {
  575. ent2 = gameLocal.FindEntity( name2 );
  576. if ( !ent2 ) {
  577. gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() );
  578. }
  579. }
  580. else {
  581. ent2 = gameLocal.entities[ENTITYNUM_WORLD];
  582. }
  583. spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 );
  584. BecomeActive( TH_THINK );
  585. }
  586. /*
  587. ================
  588. idSpring::Spawn
  589. ================
  590. */
  591. void idSpring::Spawn( void ) {
  592. float Kstretch, damping, restLength;
  593. spawnArgs.GetInt( "id1", "0", id1 );
  594. spawnArgs.GetInt( "id2", "0", id2 );
  595. spawnArgs.GetVector( "point1", "0 0 0", p1 );
  596. spawnArgs.GetVector( "point2", "0 0 0", p2 );
  597. spawnArgs.GetFloat( "constant", "100.0f", Kstretch );
  598. spawnArgs.GetFloat( "damping", "10.0f", damping );
  599. spawnArgs.GetFloat( "restlength", "0.0f", restLength );
  600. spring.InitSpring( Kstretch, 0.0f, damping, restLength );
  601. ent1 = ent2 = NULL;
  602. PostEventMS( &EV_PostSpawn, 0 );
  603. }
  604. /*
  605. ===============================================================================
  606. idForceField
  607. ===============================================================================
  608. */
  609. const idEventDef EV_Toggle( "Toggle", NULL );
  610. CLASS_DECLARATION( idEntity, idForceField )
  611. EVENT( EV_Activate, idForceField::Event_Activate )
  612. EVENT( EV_Toggle, idForceField::Event_Toggle )
  613. EVENT( EV_FindTargets, idForceField::Event_FindTargets )
  614. END_CLASS
  615. /*
  616. ===============
  617. idForceField::Toggle
  618. ================
  619. */
  620. void idForceField::Toggle( void ) {
  621. if ( thinkFlags & TH_THINK ) {
  622. BecomeInactive( TH_THINK );
  623. } else {
  624. BecomeActive( TH_THINK );
  625. }
  626. }
  627. /*
  628. ================
  629. idForceField::Think
  630. ================
  631. */
  632. void idForceField::Think( void ) {
  633. if ( thinkFlags & TH_THINK ) {
  634. // evaluate force
  635. forceField.Evaluate( gameLocal.time );
  636. }
  637. Present();
  638. }
  639. /*
  640. ================
  641. idForceField::Save
  642. ================
  643. */
  644. void idForceField::Save( idSaveGame *savefile ) const {
  645. savefile->WriteStaticObject( forceField );
  646. }
  647. /*
  648. ================
  649. idForceField::Restore
  650. ================
  651. */
  652. void idForceField::Restore( idRestoreGame *savefile ) {
  653. savefile->ReadStaticObject( forceField );
  654. }
  655. /*
  656. ================
  657. idForceField::Spawn
  658. ================
  659. */
  660. void idForceField::Spawn( void ) {
  661. idVec3 uniform;
  662. float explosion, implosion, randomTorque;
  663. if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) {
  664. forceField.Uniform( uniform );
  665. } else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) {
  666. forceField.Explosion( explosion );
  667. } else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) {
  668. forceField.Implosion( implosion );
  669. }
  670. if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) {
  671. forceField.RandomTorque( randomTorque );
  672. }
  673. if ( spawnArgs.GetBool( "applyForce", "0" ) ) {
  674. forceField.SetApplyType( FORCEFIELD_APPLY_FORCE );
  675. } else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) {
  676. forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE );
  677. } else {
  678. forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY );
  679. }
  680. forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) );
  681. forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) );
  682. // set the collision model on the force field
  683. forceField.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ) );
  684. // remove the collision model from the physics object
  685. GetPhysics()->SetClipModel( NULL, 1.0f );
  686. if ( spawnArgs.GetBool( "start_on" ) ) {
  687. BecomeActive( TH_THINK );
  688. }
  689. }
  690. /*
  691. ===============
  692. idForceField::Event_Toggle
  693. ================
  694. */
  695. void idForceField::Event_Toggle( void ) {
  696. Toggle();
  697. }
  698. /*
  699. ================
  700. idForceField::Event_Activate
  701. ================
  702. */
  703. void idForceField::Event_Activate( idEntity *activator ) {
  704. float wait;
  705. Toggle();
  706. if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) {
  707. PostEventSec( &EV_Toggle, wait );
  708. }
  709. }
  710. /*
  711. ================
  712. idForceField::Event_FindTargets
  713. ================
  714. */
  715. void idForceField::Event_FindTargets( void ) {
  716. FindTargets();
  717. RemoveNullTargets();
  718. if ( targets.Num() ) {
  719. forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() );
  720. }
  721. }
  722. /*
  723. ===============================================================================
  724. idAnimated
  725. ===============================================================================
  726. */
  727. const idEventDef EV_Animated_Start( "<start>" );
  728. const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" );
  729. const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" );
  730. const idEventDef EV_AnimDone( "<AnimDone>", "d" );
  731. const idEventDef EV_StartRagdoll( "startRagdoll" );
  732. CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated )
  733. EVENT( EV_Activate, idAnimated::Event_Activate )
  734. EVENT( EV_Animated_Start, idAnimated::Event_Start )
  735. EVENT( EV_StartRagdoll, idAnimated::Event_StartRagdoll )
  736. EVENT( EV_AnimDone, idAnimated::Event_AnimDone )
  737. EVENT( EV_Footstep, idAnimated::Event_Footstep )
  738. EVENT( EV_FootstepLeft, idAnimated::Event_Footstep )
  739. EVENT( EV_FootstepRight, idAnimated::Event_Footstep )
  740. EVENT( EV_LaunchMissiles, idAnimated::Event_LaunchMissiles )
  741. EVENT( EV_LaunchMissilesUpdate, idAnimated::Event_LaunchMissilesUpdate )
  742. END_CLASS
  743. /*
  744. ===============
  745. idAnimated::idAnimated
  746. ================
  747. */
  748. idAnimated::idAnimated() {
  749. anim = 0;
  750. blendFrames = 0;
  751. soundJoint = INVALID_JOINT;
  752. activated = false;
  753. combatModel = NULL;
  754. activator = NULL;
  755. current_anim_index = 0;
  756. num_anims = 0;
  757. }
  758. /*
  759. ===============
  760. idAnimated::idAnimated
  761. ================
  762. */
  763. idAnimated::~idAnimated() {
  764. delete combatModel;
  765. combatModel = NULL;
  766. }
  767. /*
  768. ===============
  769. idAnimated::Save
  770. ================
  771. */
  772. void idAnimated::Save( idSaveGame *savefile ) const {
  773. savefile->WriteInt( current_anim_index );
  774. savefile->WriteInt( num_anims );
  775. savefile->WriteInt( anim );
  776. savefile->WriteInt( blendFrames );
  777. savefile->WriteJoint( soundJoint );
  778. activator.Save( savefile );
  779. savefile->WriteBool( activated );
  780. }
  781. /*
  782. ===============
  783. idAnimated::Restore
  784. ================
  785. */
  786. void idAnimated::Restore( idRestoreGame *savefile ) {
  787. savefile->ReadInt( current_anim_index );
  788. savefile->ReadInt( num_anims );
  789. savefile->ReadInt( anim );
  790. savefile->ReadInt( blendFrames );
  791. savefile->ReadJoint( soundJoint );
  792. activator.Restore( savefile );
  793. savefile->ReadBool( activated );
  794. }
  795. /*
  796. ===============
  797. idAnimated::Spawn
  798. ================
  799. */
  800. void idAnimated::Spawn( void ) {
  801. idStr animname;
  802. int anim2;
  803. float wait;
  804. const char *joint;
  805. joint = spawnArgs.GetString( "sound_bone", "origin" );
  806. soundJoint = animator.GetJointHandle( joint );
  807. if ( soundJoint == INVALID_JOINT ) {
  808. gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint );
  809. }
  810. LoadAF();
  811. // allow bullets to collide with a combat model
  812. if ( spawnArgs.GetBool( "combatModel", "0" ) ) {
  813. combatModel = new idClipModel( modelDefHandle );
  814. }
  815. // allow the entity to take damage
  816. if ( spawnArgs.GetBool( "takeDamage", "0" ) ) {
  817. fl.takedamage = true;
  818. }
  819. blendFrames = 0;
  820. current_anim_index = 0;
  821. spawnArgs.GetInt( "num_anims", "0", num_anims );
  822. blendFrames = spawnArgs.GetInt( "blend_in" );
  823. animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" );
  824. if ( !animname.Length() ) {
  825. anim = 0;
  826. } else {
  827. anim = animator.GetAnim( animname );
  828. if ( !anim ) {
  829. gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
  830. }
  831. }
  832. if ( spawnArgs.GetBool( "hide" ) ) {
  833. Hide();
  834. if ( !num_anims ) {
  835. blendFrames = 0;
  836. }
  837. } else if ( spawnArgs.GetString( "start_anim", "", animname ) ) {
  838. anim2 = animator.GetAnim( animname );
  839. if ( !anim2 ) {
  840. gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
  841. }
  842. animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 );
  843. } else if ( anim ) {
  844. // init joints to the first frame of the animation
  845. animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 );
  846. if ( !num_anims ) {
  847. blendFrames = 0;
  848. }
  849. }
  850. spawnArgs.GetFloat( "wait", "-1", wait );
  851. if ( wait >= 0 ) {
  852. PostEventSec( &EV_Activate, wait, this );
  853. }
  854. }
  855. /*
  856. ===============
  857. idAnimated::LoadAF
  858. ===============
  859. */
  860. bool idAnimated::LoadAF( void ) {
  861. idStr fileName;
  862. if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) {
  863. return false;
  864. }
  865. af.SetAnimator( GetAnimator() );
  866. return af.Load( this, fileName );
  867. }
  868. /*
  869. ===============
  870. idAnimated::GetPhysicsToSoundTransform
  871. ===============
  872. */
  873. bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  874. animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
  875. axis = renderEntity.axis;
  876. return true;
  877. }
  878. /*
  879. ================
  880. idAnimated::StartRagdoll
  881. ================
  882. */
  883. bool idAnimated::StartRagdoll( void ) {
  884. // if no AF loaded
  885. if ( !af.IsLoaded() ) {
  886. return false;
  887. }
  888. // if the AF is already active
  889. if ( af.IsActive() ) {
  890. return true;
  891. }
  892. // disable any collision model used
  893. GetPhysics()->DisableClip();
  894. // start using the AF
  895. af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
  896. return true;
  897. }
  898. /*
  899. =====================
  900. idAnimated::PlayNextAnim
  901. =====================
  902. */
  903. void idAnimated::PlayNextAnim( void ) {
  904. const char *animname;
  905. int len;
  906. int cycle;
  907. if ( current_anim_index >= num_anims ) {
  908. Hide();
  909. if ( spawnArgs.GetBool( "remove" ) ) {
  910. PostEventMS( &EV_Remove, 0 );
  911. } else {
  912. current_anim_index = 0;
  913. }
  914. return;
  915. }
  916. Show();
  917. current_anim_index++;
  918. spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname );
  919. if ( !animname ) {
  920. anim = 0;
  921. animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) );
  922. return;
  923. }
  924. anim = animator.GetAnim( animname );
  925. if ( !anim ) {
  926. gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() );
  927. return;
  928. }
  929. if ( g_debugCinematic.GetBool() ) {
  930. gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname );
  931. }
  932. spawnArgs.GetInt( "cycle", "1", cycle );
  933. if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) {
  934. cycle = -1;
  935. }
  936. animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
  937. animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
  938. len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
  939. if ( len >= 0 ) {
  940. PostEventMS( &EV_AnimDone, len, current_anim_index );
  941. }
  942. // offset the start time of the shader to sync it to the game time
  943. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  944. animator.ForceUpdate();
  945. UpdateAnimation();
  946. UpdateVisuals();
  947. Present();
  948. }
  949. /*
  950. ===============
  951. idAnimated::Event_StartRagdoll
  952. ================
  953. */
  954. void idAnimated::Event_StartRagdoll( void ) {
  955. StartRagdoll();
  956. }
  957. /*
  958. ===============
  959. idAnimated::Event_AnimDone
  960. ================
  961. */
  962. void idAnimated::Event_AnimDone( int animindex ) {
  963. if ( g_debugCinematic.GetBool() ) {
  964. const idAnim *animPtr = animator.GetAnim( anim );
  965. gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
  966. }
  967. if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) {
  968. Hide();
  969. PostEventMS( &EV_Remove, 0 );
  970. } else if ( spawnArgs.GetBool( "auto_advance" ) ) {
  971. PlayNextAnim();
  972. } else {
  973. activated = false;
  974. }
  975. ActivateTargets( activator.GetEntity() );
  976. }
  977. /*
  978. ===============
  979. idAnimated::Event_Activate
  980. ================
  981. */
  982. void idAnimated::Event_Activate( idEntity *_activator ) {
  983. if ( num_anims ) {
  984. PlayNextAnim();
  985. activator = _activator;
  986. return;
  987. }
  988. if ( activated ) {
  989. // already activated
  990. return;
  991. }
  992. activated = true;
  993. activator = _activator;
  994. ProcessEvent( &EV_Animated_Start );
  995. }
  996. /*
  997. ===============
  998. idAnimated::Event_Start
  999. ================
  1000. */
  1001. void idAnimated::Event_Start( void ) {
  1002. int cycle;
  1003. int len;
  1004. Show();
  1005. if ( num_anims ) {
  1006. PlayNextAnim();
  1007. return;
  1008. }
  1009. if ( anim ) {
  1010. if ( g_debugCinematic.GetBool() ) {
  1011. const idAnim *animPtr = animator.GetAnim( anim );
  1012. gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
  1013. }
  1014. spawnArgs.GetInt( "cycle", "1", cycle );
  1015. animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
  1016. animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
  1017. len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
  1018. if ( len >= 0 ) {
  1019. PostEventMS( &EV_AnimDone, len, 1 );
  1020. }
  1021. }
  1022. // offset the start time of the shader to sync it to the game time
  1023. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1024. animator.ForceUpdate();
  1025. UpdateAnimation();
  1026. UpdateVisuals();
  1027. Present();
  1028. }
  1029. /*
  1030. ===============
  1031. idAnimated::Event_Footstep
  1032. ===============
  1033. */
  1034. void idAnimated::Event_Footstep( void ) {
  1035. StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL );
  1036. }
  1037. /*
  1038. =====================
  1039. idAnimated::Event_LaunchMissilesUpdate
  1040. =====================
  1041. */
  1042. void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) {
  1043. idVec3 launchPos;
  1044. idVec3 targetPos;
  1045. idMat3 axis;
  1046. idVec3 dir;
  1047. idEntity * ent;
  1048. idProjectile * projectile;
  1049. const idDict * projectileDef;
  1050. const char * projectilename;
  1051. projectilename = spawnArgs.GetString( "projectilename" );
  1052. projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
  1053. if ( !projectileDef ) {
  1054. gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1055. return;
  1056. }
  1057. StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL );
  1058. animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis );
  1059. launchPos = renderEntity.origin + launchPos * renderEntity.axis;
  1060. animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis );
  1061. targetPos = renderEntity.origin + targetPos * renderEntity.axis;
  1062. dir = targetPos - launchPos;
  1063. dir.Normalize();
  1064. gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
  1065. if ( !ent || !ent->IsType( idProjectile::Type ) ) {
  1066. gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1067. }
  1068. projectile = ( idProjectile * )ent;
  1069. projectile->Create( this, launchPos, dir );
  1070. projectile->Launch( launchPos, dir, vec3_origin );
  1071. if ( numshots > 0 ) {
  1072. PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay );
  1073. }
  1074. }
  1075. /*
  1076. =====================
  1077. idAnimated::Event_LaunchMissiles
  1078. =====================
  1079. */
  1080. void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) {
  1081. const idDict * projectileDef;
  1082. jointHandle_t launch;
  1083. jointHandle_t target;
  1084. projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
  1085. if ( !projectileDef ) {
  1086. gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1087. return;
  1088. }
  1089. launch = animator.GetJointHandle( launchjoint );
  1090. if ( launch == INVALID_JOINT ) {
  1091. gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint );
  1092. gameLocal.Error( "Unknown joint '%s'", launchjoint );
  1093. }
  1094. target = animator.GetJointHandle( targetjoint );
  1095. if ( target == INVALID_JOINT ) {
  1096. gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint );
  1097. }
  1098. spawnArgs.Set( "projectilename", projectilename );
  1099. spawnArgs.Set( "missilesound", sound );
  1100. CancelEvents( &EV_LaunchMissilesUpdate );
  1101. ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay );
  1102. }
  1103. /*
  1104. ===============================================================================
  1105. idStaticEntity
  1106. Some static entities may be optimized into inline geometry by dmap
  1107. ===============================================================================
  1108. */
  1109. CLASS_DECLARATION( idEntity, idStaticEntity )
  1110. EVENT( EV_Activate, idStaticEntity::Event_Activate )
  1111. END_CLASS
  1112. /*
  1113. ===============
  1114. idStaticEntity::idStaticEntity
  1115. ===============
  1116. */
  1117. idStaticEntity::idStaticEntity( void ) {
  1118. spawnTime = 0;
  1119. active = false;
  1120. fadeFrom.Set( 1, 1, 1, 1 );
  1121. fadeTo.Set( 1, 1, 1, 1 );
  1122. fadeStart = 0;
  1123. fadeEnd = 0;
  1124. runGui = false;
  1125. }
  1126. /*
  1127. ===============
  1128. idStaticEntity::Save
  1129. ===============
  1130. */
  1131. void idStaticEntity::Save( idSaveGame *savefile ) const {
  1132. savefile->WriteInt( spawnTime );
  1133. savefile->WriteBool( active );
  1134. savefile->WriteVec4( fadeFrom );
  1135. savefile->WriteVec4( fadeTo );
  1136. savefile->WriteInt( fadeStart );
  1137. savefile->WriteInt( fadeEnd );
  1138. savefile->WriteBool( runGui );
  1139. }
  1140. /*
  1141. ===============
  1142. idStaticEntity::Restore
  1143. ===============
  1144. */
  1145. void idStaticEntity::Restore( idRestoreGame *savefile ) {
  1146. savefile->ReadInt( spawnTime );
  1147. savefile->ReadBool( active );
  1148. savefile->ReadVec4( fadeFrom );
  1149. savefile->ReadVec4( fadeTo );
  1150. savefile->ReadInt( fadeStart );
  1151. savefile->ReadInt( fadeEnd );
  1152. savefile->ReadBool( runGui );
  1153. }
  1154. /*
  1155. ===============
  1156. idStaticEntity::Spawn
  1157. ===============
  1158. */
  1159. void idStaticEntity::Spawn( void ) {
  1160. bool solid;
  1161. bool hidden;
  1162. // an inline static model will not do anything at all
  1163. if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) {
  1164. Hide();
  1165. return;
  1166. }
  1167. solid = spawnArgs.GetBool( "solid" );
  1168. hidden = spawnArgs.GetBool( "hide" );
  1169. if ( solid && !hidden ) {
  1170. GetPhysics()->SetContents( CONTENTS_SOLID );
  1171. } else {
  1172. GetPhysics()->SetContents( 0 );
  1173. }
  1174. spawnTime = gameLocal.time;
  1175. active = false;
  1176. idStr model = spawnArgs.GetString( "model" );
  1177. if ( model.Find( ".prt" ) >= 0 ) {
  1178. // we want the parametric particles out of sync with each other
  1179. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 );
  1180. }
  1181. fadeFrom.Set( 1, 1, 1, 1 );
  1182. fadeTo.Set( 1, 1, 1, 1 );
  1183. fadeStart = 0;
  1184. fadeEnd = 0;
  1185. // NOTE: this should be used very rarely because it is expensive
  1186. runGui = spawnArgs.GetBool( "runGui" );
  1187. if ( runGui ) {
  1188. BecomeActive( TH_THINK );
  1189. }
  1190. }
  1191. /*
  1192. ================
  1193. idStaticEntity::ShowEditingDialog
  1194. ================
  1195. */
  1196. void idStaticEntity::ShowEditingDialog( void ) {
  1197. common->InitTool( EDITOR_PARTICLE, &spawnArgs );
  1198. }
  1199. /*
  1200. ================
  1201. idStaticEntity::Think
  1202. ================
  1203. */
  1204. void idStaticEntity::Think( void ) {
  1205. idEntity::Think();
  1206. if ( thinkFlags & TH_THINK ) {
  1207. if ( runGui && renderEntity.gui[0] ) {
  1208. idPlayer *player = gameLocal.GetLocalPlayer();
  1209. if ( player ) {
  1210. if ( !player->objectiveSystemOpen ) {
  1211. renderEntity.gui[0]->StateChanged( gameLocal.time, true );
  1212. if ( renderEntity.gui[1] ) {
  1213. renderEntity.gui[1]->StateChanged( gameLocal.time, true );
  1214. }
  1215. if ( renderEntity.gui[2] ) {
  1216. renderEntity.gui[2]->StateChanged( gameLocal.time, true );
  1217. }
  1218. }
  1219. }
  1220. }
  1221. if ( fadeEnd > 0 ) {
  1222. idVec4 color;
  1223. if ( gameLocal.time < fadeEnd ) {
  1224. color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
  1225. } else {
  1226. color = fadeTo;
  1227. fadeEnd = 0;
  1228. BecomeInactive( TH_THINK );
  1229. }
  1230. SetColor( color );
  1231. }
  1232. }
  1233. }
  1234. /*
  1235. ================
  1236. idStaticEntity::Fade
  1237. ================
  1238. */
  1239. void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) {
  1240. GetColor( fadeFrom );
  1241. fadeTo = to;
  1242. fadeStart = gameLocal.time;
  1243. fadeEnd = gameLocal.time + SEC2MS( fadeTime );
  1244. BecomeActive( TH_THINK );
  1245. }
  1246. /*
  1247. ================
  1248. idStaticEntity::Hide
  1249. ================
  1250. */
  1251. void idStaticEntity::Hide( void ) {
  1252. idEntity::Hide();
  1253. GetPhysics()->SetContents( 0 );
  1254. }
  1255. /*
  1256. ================
  1257. idStaticEntity::Show
  1258. ================
  1259. */
  1260. void idStaticEntity::Show( void ) {
  1261. idEntity::Show();
  1262. if ( spawnArgs.GetBool( "solid" ) ) {
  1263. GetPhysics()->SetContents( CONTENTS_SOLID );
  1264. }
  1265. }
  1266. /*
  1267. ================
  1268. idStaticEntity::Event_Activate
  1269. ================
  1270. */
  1271. void idStaticEntity::Event_Activate( idEntity *activator ) {
  1272. idStr activateGui;
  1273. spawnTime = gameLocal.time;
  1274. active = !active;
  1275. const idKeyValue *kv = spawnArgs.FindKey( "hide" );
  1276. if ( kv ) {
  1277. if ( IsHidden() ) {
  1278. Show();
  1279. } else {
  1280. Hide();
  1281. }
  1282. }
  1283. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime );
  1284. renderEntity.shaderParms[5] = active;
  1285. // this change should be a good thing, it will automatically turn on
  1286. // lights etc.. when triggered so that does not have to be specifically done
  1287. // with trigger parms.. it MIGHT break things so need to keep an eye on it
  1288. renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ? 0.0f : 1.0f;
  1289. BecomeActive( TH_UPDATEVISUALS );
  1290. }
  1291. /*
  1292. ================
  1293. idStaticEntity::WriteToSnapshot
  1294. ================
  1295. */
  1296. void idStaticEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1297. GetPhysics()->WriteToSnapshot( msg );
  1298. WriteBindToSnapshot( msg );
  1299. WriteColorToSnapshot( msg );
  1300. WriteGUIToSnapshot( msg );
  1301. msg.WriteBits( IsHidden()?1:0, 1 );
  1302. }
  1303. /*
  1304. ================
  1305. idStaticEntity::ReadFromSnapshot
  1306. ================
  1307. */
  1308. void idStaticEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1309. bool hidden;
  1310. GetPhysics()->ReadFromSnapshot( msg );
  1311. ReadBindFromSnapshot( msg );
  1312. ReadColorFromSnapshot( msg );
  1313. ReadGUIFromSnapshot( msg );
  1314. hidden = msg.ReadBits( 1 ) == 1;
  1315. if ( hidden != IsHidden() ) {
  1316. if ( hidden ) {
  1317. Hide();
  1318. } else {
  1319. Show();
  1320. }
  1321. }
  1322. if ( msg.HasChanged() ) {
  1323. UpdateVisuals();
  1324. }
  1325. }
  1326. /*
  1327. ===============================================================================
  1328. idFuncEmitter
  1329. ===============================================================================
  1330. */
  1331. CLASS_DECLARATION( idStaticEntity, idFuncEmitter )
  1332. EVENT( EV_Activate, idFuncEmitter::Event_Activate )
  1333. END_CLASS
  1334. /*
  1335. ===============
  1336. idFuncEmitter::idFuncEmitter
  1337. ===============
  1338. */
  1339. idFuncEmitter::idFuncEmitter( void ) {
  1340. hidden = false;
  1341. }
  1342. /*
  1343. ===============
  1344. idFuncEmitter::Spawn
  1345. ===============
  1346. */
  1347. void idFuncEmitter::Spawn( void ) {
  1348. if ( spawnArgs.GetBool( "start_off" ) ) {
  1349. hidden = true;
  1350. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 );
  1351. UpdateVisuals();
  1352. } else {
  1353. hidden = false;
  1354. }
  1355. }
  1356. /*
  1357. ===============
  1358. idFuncEmitter::Save
  1359. ===============
  1360. */
  1361. void idFuncEmitter::Save( idSaveGame *savefile ) const {
  1362. savefile->WriteBool( hidden );
  1363. }
  1364. /*
  1365. ===============
  1366. idFuncEmitter::Restore
  1367. ===============
  1368. */
  1369. void idFuncEmitter::Restore( idRestoreGame *savefile ) {
  1370. savefile->ReadBool( hidden );
  1371. }
  1372. /*
  1373. ================
  1374. idFuncEmitter::Event_Activate
  1375. ================
  1376. */
  1377. void idFuncEmitter::Event_Activate( idEntity *activator ) {
  1378. if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) {
  1379. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0;
  1380. renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  1381. hidden = false;
  1382. } else {
  1383. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time );
  1384. hidden = true;
  1385. }
  1386. UpdateVisuals();
  1387. }
  1388. /*
  1389. ================
  1390. idFuncEmitter::WriteToSnapshot
  1391. ================
  1392. */
  1393. void idFuncEmitter::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1394. msg.WriteBits( hidden ? 1 : 0, 1 );
  1395. msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
  1396. msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
  1397. }
  1398. /*
  1399. ================
  1400. idFuncEmitter::ReadFromSnapshot
  1401. ================
  1402. */
  1403. void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1404. hidden = msg.ReadBits( 1 ) != 0;
  1405. renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
  1406. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
  1407. if ( msg.HasChanged() ) {
  1408. UpdateVisuals();
  1409. }
  1410. }
  1411. /*
  1412. ===============================================================================
  1413. idFuncSplat
  1414. ===============================================================================
  1415. */
  1416. const idEventDef EV_Splat( "<Splat>" );
  1417. CLASS_DECLARATION( idFuncEmitter, idFuncSplat )
  1418. EVENT( EV_Activate, idFuncSplat::Event_Activate )
  1419. EVENT( EV_Splat, idFuncSplat::Event_Splat )
  1420. END_CLASS
  1421. /*
  1422. ===============
  1423. idFuncSplat::idFuncSplat
  1424. ===============
  1425. */
  1426. idFuncSplat::idFuncSplat( void ) {
  1427. }
  1428. /*
  1429. ===============
  1430. idFuncSplat::Spawn
  1431. ===============
  1432. */
  1433. void idFuncSplat::Spawn( void ) {
  1434. }
  1435. /*
  1436. ================
  1437. idFuncSplat::Event_Splat
  1438. ================
  1439. */
  1440. void idFuncSplat::Event_Splat( void ) {
  1441. const char *splat = NULL;
  1442. int count = spawnArgs.GetInt( "splatCount", "1" );
  1443. for ( int i = 0; i < count; i++ ) {
  1444. splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random );
  1445. if ( splat && *splat ) {
  1446. float size = spawnArgs.GetFloat( "splatSize", "128" );
  1447. float dist = spawnArgs.GetFloat( "splatDistance", "128" );
  1448. float angle = spawnArgs.GetFloat( "splatAngle", "0" );
  1449. gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle );
  1450. }
  1451. }
  1452. StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL );
  1453. }
  1454. /*
  1455. ================
  1456. idFuncSplat::Event_Activate
  1457. ================
  1458. */
  1459. void idFuncSplat::Event_Activate( idEntity *activator ) {
  1460. idFuncEmitter::Event_Activate( activator );
  1461. PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) );
  1462. StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL );
  1463. }
  1464. /*
  1465. ===============================================================================
  1466. idFuncSmoke
  1467. ===============================================================================
  1468. */
  1469. CLASS_DECLARATION( idEntity, idFuncSmoke )
  1470. EVENT( EV_Activate, idFuncSmoke::Event_Activate )
  1471. END_CLASS
  1472. /*
  1473. ===============
  1474. idFuncSmoke::idFuncSmoke
  1475. ===============
  1476. */
  1477. idFuncSmoke::idFuncSmoke() {
  1478. smokeTime = 0;
  1479. smoke = NULL;
  1480. restart = false;
  1481. }
  1482. /*
  1483. ===============
  1484. idFuncSmoke::Save
  1485. ===============
  1486. */
  1487. void idFuncSmoke::Save( idSaveGame *savefile ) const {
  1488. savefile->WriteInt( smokeTime );
  1489. savefile->WriteParticle( smoke );
  1490. savefile->WriteBool( restart );
  1491. }
  1492. /*
  1493. ===============
  1494. idFuncSmoke::Restore
  1495. ===============
  1496. */
  1497. void idFuncSmoke::Restore( idRestoreGame *savefile ) {
  1498. savefile->ReadInt( smokeTime );
  1499. savefile->ReadParticle( smoke );
  1500. savefile->ReadBool( restart );
  1501. }
  1502. /*
  1503. ===============
  1504. idFuncSmoke::Spawn
  1505. ===============
  1506. */
  1507. void idFuncSmoke::Spawn( void ) {
  1508. const char *smokeName = spawnArgs.GetString( "smoke" );
  1509. if ( *smokeName != '\0' ) {
  1510. smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  1511. } else {
  1512. smoke = NULL;
  1513. }
  1514. if ( spawnArgs.GetBool( "start_off" ) ) {
  1515. smokeTime = 0;
  1516. restart = false;
  1517. } else if ( smoke ) {
  1518. smokeTime = gameLocal.time;
  1519. BecomeActive( TH_UPDATEPARTICLES );
  1520. restart = true;
  1521. }
  1522. GetPhysics()->SetContents( 0 );
  1523. }
  1524. /*
  1525. ================
  1526. idFuncSmoke::Event_Activate
  1527. ================
  1528. */
  1529. void idFuncSmoke::Event_Activate( idEntity *activator ) {
  1530. if ( thinkFlags & TH_UPDATEPARTICLES ) {
  1531. restart = false;
  1532. return;
  1533. } else {
  1534. BecomeActive( TH_UPDATEPARTICLES );
  1535. restart = true;
  1536. smokeTime = gameLocal.time;
  1537. }
  1538. }
  1539. /*
  1540. ===============
  1541. idFuncSmoke::Think
  1542. ================
  1543. */
  1544. void idFuncSmoke::Think( void ) {
  1545. // if we are completely closed off from the player, don't do anything at all
  1546. if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) {
  1547. return;
  1548. }
  1549. if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
  1550. if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
  1551. if ( restart ) {
  1552. smokeTime = gameLocal.time;
  1553. } else {
  1554. smokeTime = 0;
  1555. BecomeInactive( TH_UPDATEPARTICLES );
  1556. }
  1557. }
  1558. }
  1559. }
  1560. /*
  1561. ===============================================================================
  1562. idTextEntity
  1563. ===============================================================================
  1564. */
  1565. CLASS_DECLARATION( idEntity, idTextEntity )
  1566. END_CLASS
  1567. /*
  1568. ================
  1569. idTextEntity::Spawn
  1570. ================
  1571. */
  1572. void idTextEntity::Spawn( void ) {
  1573. // these are cached as the are used each frame
  1574. text = spawnArgs.GetString( "text" );
  1575. playerOriented = spawnArgs.GetBool( "playerOriented" );
  1576. bool force = spawnArgs.GetBool( "force" );
  1577. if ( developer.GetBool() || force ) {
  1578. BecomeActive(TH_THINK);
  1579. }
  1580. }
  1581. /*
  1582. ================
  1583. idTextEntity::Save
  1584. ================
  1585. */
  1586. void idTextEntity::Save( idSaveGame *savefile ) const {
  1587. savefile->WriteString( text );
  1588. savefile->WriteBool( playerOriented );
  1589. }
  1590. /*
  1591. ================
  1592. idTextEntity::Restore
  1593. ================
  1594. */
  1595. void idTextEntity::Restore( idRestoreGame *savefile ) {
  1596. savefile->ReadString( text );
  1597. savefile->ReadBool( playerOriented );
  1598. }
  1599. /*
  1600. ================
  1601. idTextEntity::Think
  1602. ================
  1603. */
  1604. void idTextEntity::Think( void ) {
  1605. if ( thinkFlags & TH_THINK ) {
  1606. gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 );
  1607. for ( int i = 0; i < targets.Num(); i++ ) {
  1608. if ( targets[i].GetEntity() ) {
  1609. gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 );
  1610. }
  1611. }
  1612. } else {
  1613. BecomeInactive( TH_ALL );
  1614. }
  1615. }
  1616. /*
  1617. ===============================================================================
  1618. idVacuumSeperatorEntity
  1619. Can be triggered to let vacuum through a portal (blown out window)
  1620. ===============================================================================
  1621. */
  1622. CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity )
  1623. EVENT( EV_Activate, idVacuumSeparatorEntity::Event_Activate )
  1624. END_CLASS
  1625. /*
  1626. ================
  1627. idVacuumSeparatorEntity::idVacuumSeparatorEntity
  1628. ================
  1629. */
  1630. idVacuumSeparatorEntity::idVacuumSeparatorEntity( void ) {
  1631. portal = 0;
  1632. }
  1633. /*
  1634. ================
  1635. idVacuumSeparatorEntity::Save
  1636. ================
  1637. */
  1638. void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const {
  1639. savefile->WriteInt( (int)portal );
  1640. savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) );
  1641. }
  1642. /*
  1643. ================
  1644. idVacuumSeparatorEntity::Restore
  1645. ================
  1646. */
  1647. void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) {
  1648. int state;
  1649. savefile->ReadInt( (int &)portal );
  1650. savefile->ReadInt( state );
  1651. gameLocal.SetPortalState( portal, state );
  1652. }
  1653. /*
  1654. ================
  1655. idVacuumSeparatorEntity::Spawn
  1656. ================
  1657. */
  1658. void idVacuumSeparatorEntity::Spawn() {
  1659. idBounds b;
  1660. b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
  1661. portal = gameRenderWorld->FindPortal( b );
  1662. if ( !portal ) {
  1663. gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
  1664. return;
  1665. }
  1666. gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION );
  1667. }
  1668. /*
  1669. ================
  1670. idVacuumSeparatorEntity::Event_Activate
  1671. ================
  1672. */
  1673. void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) {
  1674. if ( !portal ) {
  1675. return;
  1676. }
  1677. gameLocal.SetPortalState( portal, PS_BLOCK_NONE );
  1678. }
  1679. /*
  1680. ===============================================================================
  1681. idLocationSeparatorEntity
  1682. ===============================================================================
  1683. */
  1684. CLASS_DECLARATION( idEntity, idLocationSeparatorEntity )
  1685. END_CLASS
  1686. /*
  1687. ================
  1688. idLocationSeparatorEntity::Spawn
  1689. ================
  1690. */
  1691. void idLocationSeparatorEntity::Spawn() {
  1692. idBounds b;
  1693. b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
  1694. qhandle_t portal = gameRenderWorld->FindPortal( b );
  1695. if ( !portal ) {
  1696. gameLocal.Warning( "LocationSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
  1697. }
  1698. gameLocal.SetPortalState( portal, PS_BLOCK_LOCATION );
  1699. }
  1700. /*
  1701. ===============================================================================
  1702. idVacuumEntity
  1703. Levels should only have a single vacuum entity.
  1704. ===============================================================================
  1705. */
  1706. CLASS_DECLARATION( idEntity, idVacuumEntity )
  1707. END_CLASS
  1708. /*
  1709. ================
  1710. idVacuumEntity::Spawn
  1711. ================
  1712. */
  1713. void idVacuumEntity::Spawn() {
  1714. if ( gameLocal.vacuumAreaNum != -1 ) {
  1715. gameLocal.Warning( "idVacuumEntity::Spawn: multiple idVacuumEntity in level" );
  1716. return;
  1717. }
  1718. idVec3 org = spawnArgs.GetVector( "origin" );
  1719. gameLocal.vacuumAreaNum = gameRenderWorld->PointInArea( org );
  1720. }
  1721. /*
  1722. ===============================================================================
  1723. idLocationEntity
  1724. ===============================================================================
  1725. */
  1726. CLASS_DECLARATION( idEntity, idLocationEntity )
  1727. END_CLASS
  1728. /*
  1729. ======================
  1730. idLocationEntity::Spawn
  1731. ======================
  1732. */
  1733. void idLocationEntity::Spawn() {
  1734. idStr realName;
  1735. // this just holds dict information
  1736. // if "location" not already set, use the entity name.
  1737. if ( !spawnArgs.GetString( "location", "", realName ) ) {
  1738. spawnArgs.Set( "location", name );
  1739. }
  1740. }
  1741. /*
  1742. ======================
  1743. idLocationEntity::GetLocation
  1744. ======================
  1745. */
  1746. const char *idLocationEntity::GetLocation( void ) const {
  1747. return spawnArgs.GetString( "location" );
  1748. }
  1749. /*
  1750. ===============================================================================
  1751. idBeam
  1752. ===============================================================================
  1753. */
  1754. CLASS_DECLARATION( idEntity, idBeam )
  1755. EVENT( EV_PostSpawn, idBeam::Event_MatchTarget )
  1756. EVENT( EV_Activate, idBeam::Event_Activate )
  1757. END_CLASS
  1758. /*
  1759. ===============
  1760. idBeam::idBeam
  1761. ===============
  1762. */
  1763. idBeam::idBeam() {
  1764. target = NULL;
  1765. master = NULL;
  1766. }
  1767. /*
  1768. ===============
  1769. idBeam::Save
  1770. ===============
  1771. */
  1772. void idBeam::Save( idSaveGame *savefile ) const {
  1773. target.Save( savefile );
  1774. master.Save( savefile );
  1775. }
  1776. /*
  1777. ===============
  1778. idBeam::Restore
  1779. ===============
  1780. */
  1781. void idBeam::Restore( idRestoreGame *savefile ) {
  1782. target.Restore( savefile );
  1783. master.Restore( savefile );
  1784. }
  1785. /*
  1786. ===============
  1787. idBeam::Spawn
  1788. ===============
  1789. */
  1790. void idBeam::Spawn( void ) {
  1791. float width;
  1792. if ( spawnArgs.GetFloat( "width", "0", width ) ) {
  1793. renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = width;
  1794. }
  1795. SetModel( "_BEAM" );
  1796. Hide();
  1797. PostEventMS( &EV_PostSpawn, 0 );
  1798. }
  1799. /*
  1800. ================
  1801. idBeam::Think
  1802. ================
  1803. */
  1804. void idBeam::Think( void ) {
  1805. idBeam *masterEnt;
  1806. if ( !IsHidden() && !target.GetEntity() ) {
  1807. // hide if our target is removed
  1808. Hide();
  1809. }
  1810. RunPhysics();
  1811. masterEnt = master.GetEntity();
  1812. if ( masterEnt ) {
  1813. const idVec3 &origin = GetPhysics()->GetOrigin();
  1814. masterEnt->SetBeamTarget( origin );
  1815. }
  1816. Present();
  1817. }
  1818. /*
  1819. ================
  1820. idBeam::SetMaster
  1821. ================
  1822. */
  1823. void idBeam::SetMaster( idBeam *masterbeam ) {
  1824. master = masterbeam;
  1825. }
  1826. /*
  1827. ================
  1828. idBeam::SetBeamTarget
  1829. ================
  1830. */
  1831. void idBeam::SetBeamTarget( const idVec3 &origin ) {
  1832. if ( ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] != origin.x ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] != origin.y ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] != origin.z ) ) {
  1833. renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = origin.x;
  1834. renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = origin.y;
  1835. renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = origin.z;
  1836. UpdateVisuals();
  1837. }
  1838. }
  1839. /*
  1840. ================
  1841. idBeam::Show
  1842. ================
  1843. */
  1844. void idBeam::Show( void ) {
  1845. idBeam *targetEnt;
  1846. idEntity::Show();
  1847. targetEnt = target.GetEntity();
  1848. if ( targetEnt ) {
  1849. const idVec3 &origin = targetEnt->GetPhysics()->GetOrigin();
  1850. SetBeamTarget( origin );
  1851. }
  1852. }
  1853. /*
  1854. ================
  1855. idBeam::Event_MatchTarget
  1856. ================
  1857. */
  1858. void idBeam::Event_MatchTarget( void ) {
  1859. int i;
  1860. idEntity *targetEnt;
  1861. idBeam *targetBeam;
  1862. if ( !targets.Num() ) {
  1863. return;
  1864. }
  1865. targetBeam = NULL;
  1866. for( i = 0; i < targets.Num(); i++ ) {
  1867. targetEnt = targets[ i ].GetEntity();
  1868. if ( targetEnt && targetEnt->IsType( idBeam::Type ) ) {
  1869. targetBeam = static_cast<idBeam *>( targetEnt );
  1870. break;
  1871. }
  1872. }
  1873. if ( !targetBeam ) {
  1874. gameLocal.Error( "Could not find valid beam target for '%s'", name.c_str() );
  1875. }
  1876. target = targetBeam;
  1877. targetBeam->SetMaster( this );
  1878. if ( !spawnArgs.GetBool( "start_off" ) ) {
  1879. Show();
  1880. }
  1881. }
  1882. /*
  1883. ================
  1884. idBeam::Event_Activate
  1885. ================
  1886. */
  1887. void idBeam::Event_Activate( idEntity *activator ) {
  1888. if ( IsHidden() ) {
  1889. Show();
  1890. } else {
  1891. Hide();
  1892. }
  1893. }
  1894. /*
  1895. ================
  1896. idBeam::WriteToSnapshot
  1897. ================
  1898. */
  1899. void idBeam::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1900. GetPhysics()->WriteToSnapshot( msg );
  1901. WriteBindToSnapshot( msg );
  1902. WriteColorToSnapshot( msg );
  1903. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
  1904. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] );
  1905. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] );
  1906. }
  1907. /*
  1908. ================
  1909. idBeam::ReadFromSnapshot
  1910. ================
  1911. */
  1912. void idBeam::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1913. GetPhysics()->ReadFromSnapshot( msg );
  1914. ReadBindFromSnapshot( msg );
  1915. ReadColorFromSnapshot( msg );
  1916. renderEntity.shaderParms[SHADERPARM_BEAM_END_X] = msg.ReadFloat();
  1917. renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] = msg.ReadFloat();
  1918. renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] = msg.ReadFloat();
  1919. if ( msg.HasChanged() ) {
  1920. UpdateVisuals();
  1921. }
  1922. }
  1923. /*
  1924. ===============================================================================
  1925. idLiquid
  1926. ===============================================================================
  1927. */
  1928. CLASS_DECLARATION( idEntity, idLiquid )
  1929. EVENT( EV_Touch, idLiquid::Event_Touch )
  1930. END_CLASS
  1931. /*
  1932. ================
  1933. idLiquid::Save
  1934. ================
  1935. */
  1936. void idLiquid::Save( idSaveGame *savefile ) const {
  1937. // Nothing to save
  1938. }
  1939. /*
  1940. ================
  1941. idLiquid::Restore
  1942. ================
  1943. */
  1944. void idLiquid::Restore( idRestoreGame *savefile ) {
  1945. //FIXME: NO!
  1946. Spawn();
  1947. }
  1948. /*
  1949. ================
  1950. idLiquid::Spawn
  1951. ================
  1952. */
  1953. void idLiquid::Spawn() {
  1954. /*
  1955. model = dynamic_cast<idRenderModelLiquid *>( renderEntity.hModel );
  1956. if ( !model ) {
  1957. gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() );
  1958. }
  1959. model->Reset();
  1960. GetPhysics()->SetContents( CONTENTS_TRIGGER );
  1961. */
  1962. }
  1963. /*
  1964. ================
  1965. idLiquid::Event_Touch
  1966. ================
  1967. */
  1968. void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) {
  1969. // FIXME: for QuakeCon
  1970. /*
  1971. idVec3 pos;
  1972. pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  1973. model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f );
  1974. */
  1975. }
  1976. /*
  1977. ===============================================================================
  1978. idShaking
  1979. ===============================================================================
  1980. */
  1981. CLASS_DECLARATION( idEntity, idShaking )
  1982. EVENT( EV_Activate, idShaking::Event_Activate )
  1983. END_CLASS
  1984. /*
  1985. ===============
  1986. idShaking::idShaking
  1987. ===============
  1988. */
  1989. idShaking::idShaking() {
  1990. active = false;
  1991. }
  1992. /*
  1993. ===============
  1994. idShaking::Save
  1995. ===============
  1996. */
  1997. void idShaking::Save( idSaveGame *savefile ) const {
  1998. savefile->WriteBool( active );
  1999. savefile->WriteStaticObject( physicsObj );
  2000. }
  2001. /*
  2002. ===============
  2003. idShaking::Restore
  2004. ===============
  2005. */
  2006. void idShaking::Restore( idRestoreGame *savefile ) {
  2007. savefile->ReadBool( active );
  2008. savefile->ReadStaticObject( physicsObj );
  2009. RestorePhysics( &physicsObj );
  2010. }
  2011. /*
  2012. ===============
  2013. idShaking::Spawn
  2014. ===============
  2015. */
  2016. void idShaking::Spawn( void ) {
  2017. physicsObj.SetSelf( this );
  2018. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  2019. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  2020. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  2021. physicsObj.SetClipMask( MASK_SOLID );
  2022. SetPhysics( &physicsObj );
  2023. active = false;
  2024. if ( !spawnArgs.GetBool( "start_off" ) ) {
  2025. BeginShaking();
  2026. }
  2027. }
  2028. /*
  2029. ================
  2030. idShaking::BeginShaking
  2031. ================
  2032. */
  2033. void idShaking::BeginShaking( void ) {
  2034. int phase;
  2035. idAngles shake;
  2036. int period;
  2037. active = true;
  2038. phase = gameLocal.random.RandomInt( 1000 );
  2039. shake = spawnArgs.GetAngles( "shake", "0.5 0.5 0.5" );
  2040. period = spawnArgs.GetFloat( "period", "0.05" ) * 1000;
  2041. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase, period * 0.25f, GetPhysics()->GetAxis().ToAngles(), shake, ang_zero );
  2042. }
  2043. /*
  2044. ================
  2045. idShaking::Event_Activate
  2046. ================
  2047. */
  2048. void idShaking::Event_Activate( idEntity *activator ) {
  2049. if ( !active ) {
  2050. BeginShaking();
  2051. } else {
  2052. active = false;
  2053. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, physicsObj.GetAxis().ToAngles(), ang_zero, ang_zero );
  2054. }
  2055. }
  2056. /*
  2057. ===============================================================================
  2058. idEarthQuake
  2059. ===============================================================================
  2060. */
  2061. CLASS_DECLARATION( idEntity, idEarthQuake )
  2062. EVENT( EV_Activate, idEarthQuake::Event_Activate )
  2063. END_CLASS
  2064. /*
  2065. ===============
  2066. idEarthQuake::idEarthQuake
  2067. ===============
  2068. */
  2069. idEarthQuake::idEarthQuake() {
  2070. wait = 0.0f;
  2071. random = 0.0f;
  2072. nextTriggerTime = 0;
  2073. shakeStopTime = 0;
  2074. triggered = false;
  2075. playerOriented = false;
  2076. disabled = false;
  2077. shakeTime = 0.0f;
  2078. }
  2079. /*
  2080. ===============
  2081. idEarthQuake::Save
  2082. ===============
  2083. */
  2084. void idEarthQuake::Save( idSaveGame *savefile ) const {
  2085. savefile->WriteInt( nextTriggerTime );
  2086. savefile->WriteInt( shakeStopTime );
  2087. savefile->WriteFloat( wait );
  2088. savefile->WriteFloat( random );
  2089. savefile->WriteBool( triggered );
  2090. savefile->WriteBool( playerOriented );
  2091. savefile->WriteBool( disabled );
  2092. savefile->WriteFloat( shakeTime );
  2093. }
  2094. /*
  2095. ===============
  2096. idEarthQuake::Restore
  2097. ===============
  2098. */
  2099. void idEarthQuake::Restore( idRestoreGame *savefile ) {
  2100. savefile->ReadInt( nextTriggerTime );
  2101. savefile->ReadInt( shakeStopTime );
  2102. savefile->ReadFloat( wait );
  2103. savefile->ReadFloat( random );
  2104. savefile->ReadBool( triggered );
  2105. savefile->ReadBool( playerOriented );
  2106. savefile->ReadBool( disabled );
  2107. savefile->ReadFloat( shakeTime );
  2108. if ( shakeStopTime > gameLocal.time ) {
  2109. BecomeActive( TH_THINK );
  2110. }
  2111. }
  2112. /*
  2113. ===============
  2114. idEarthQuake::Spawn
  2115. ===============
  2116. */
  2117. void idEarthQuake::Spawn( void ) {
  2118. nextTriggerTime = 0;
  2119. shakeStopTime = 0;
  2120. wait = spawnArgs.GetFloat( "wait", "15" );
  2121. random = spawnArgs.GetFloat( "random", "5" );
  2122. triggered = spawnArgs.GetBool( "triggered" );
  2123. playerOriented = spawnArgs.GetBool( "playerOriented" );
  2124. disabled = false;
  2125. shakeTime = spawnArgs.GetFloat( "shakeTime", "0" );
  2126. if ( !triggered ){
  2127. PostEventSec( &EV_Activate, spawnArgs.GetFloat( "wait" ), this );
  2128. }
  2129. BecomeInactive( TH_THINK );
  2130. }
  2131. /*
  2132. ================
  2133. idEarthQuake::Event_Activate
  2134. ================
  2135. */
  2136. void idEarthQuake::Event_Activate( idEntity *activator ) {
  2137. if ( nextTriggerTime > gameLocal.time ) {
  2138. return;
  2139. }
  2140. if ( disabled && activator == this ) {
  2141. return;
  2142. }
  2143. idPlayer *player = gameLocal.GetLocalPlayer();
  2144. if ( player == NULL ) {
  2145. return;
  2146. }
  2147. nextTriggerTime = 0;
  2148. if ( !triggered && activator != this ){
  2149. // if we are not triggered ( i.e. random ), disable or enable
  2150. disabled ^= 1;
  2151. if (disabled) {
  2152. return;
  2153. } else {
  2154. PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
  2155. }
  2156. }
  2157. ActivateTargets( activator );
  2158. const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_quake" ) );
  2159. if ( playerOriented ) {
  2160. player->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  2161. } else {
  2162. StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  2163. }
  2164. if ( shakeTime > 0.0f ) {
  2165. shakeStopTime = gameLocal.time + SEC2MS( shakeTime );
  2166. BecomeActive( TH_THINK );
  2167. }
  2168. if ( wait > 0.0f ) {
  2169. if ( !triggered ) {
  2170. PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
  2171. } else {
  2172. nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
  2173. }
  2174. } else if ( shakeTime == 0.0f ) {
  2175. PostEventMS( &EV_Remove, 0 );
  2176. }
  2177. }
  2178. /*
  2179. ===============
  2180. idEarthQuake::Think
  2181. ================
  2182. */
  2183. void idEarthQuake::Think( void ) {
  2184. if ( thinkFlags & TH_THINK ) {
  2185. if ( gameLocal.time > shakeStopTime ) {
  2186. BecomeInactive( TH_THINK );
  2187. if ( wait <= 0.0f ) {
  2188. PostEventMS( &EV_Remove, 0 );
  2189. }
  2190. return;
  2191. }
  2192. float shakeVolume = gameSoundWorld->CurrentShakeAmplitudeForPosition( gameLocal.time, gameLocal.GetLocalPlayer()->firstPersonViewOrigin );
  2193. gameLocal.RadiusPush( GetPhysics()->GetOrigin(), 256, 1500 * shakeVolume, this, this, 1.0f, true );
  2194. }
  2195. BecomeInactive( TH_UPDATEVISUALS );
  2196. }
  2197. /*
  2198. ===============================================================================
  2199. idFuncPortal
  2200. ===============================================================================
  2201. */
  2202. CLASS_DECLARATION( idEntity, idFuncPortal )
  2203. EVENT( EV_Activate, idFuncPortal::Event_Activate )
  2204. END_CLASS
  2205. /*
  2206. ===============
  2207. idFuncPortal::idFuncPortal
  2208. ===============
  2209. */
  2210. idFuncPortal::idFuncPortal() {
  2211. portal = 0;
  2212. state = false;
  2213. }
  2214. /*
  2215. ===============
  2216. idFuncPortal::Save
  2217. ===============
  2218. */
  2219. void idFuncPortal::Save( idSaveGame *savefile ) const {
  2220. savefile->WriteInt( (int)portal );
  2221. savefile->WriteBool( state );
  2222. }
  2223. /*
  2224. ===============
  2225. idFuncPortal::Restore
  2226. ===============
  2227. */
  2228. void idFuncPortal::Restore( idRestoreGame *savefile ) {
  2229. savefile->ReadInt( (int &)portal );
  2230. savefile->ReadBool( state );
  2231. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2232. }
  2233. /*
  2234. ===============
  2235. idFuncPortal::Spawn
  2236. ===============
  2237. */
  2238. void idFuncPortal::Spawn( void ) {
  2239. portal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds().Expand( 32.0f ) );
  2240. if ( portal > 0 ) {
  2241. state = spawnArgs.GetBool( "start_on" );
  2242. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2243. }
  2244. }
  2245. /*
  2246. ================
  2247. idFuncPortal::Event_Activate
  2248. ================
  2249. */
  2250. void idFuncPortal::Event_Activate( idEntity *activator ) {
  2251. if ( portal > 0 ) {
  2252. state = !state;
  2253. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2254. }
  2255. }
  2256. /*
  2257. ===============================================================================
  2258. idFuncAASPortal
  2259. ===============================================================================
  2260. */
  2261. CLASS_DECLARATION( idEntity, idFuncAASPortal )
  2262. EVENT( EV_Activate, idFuncAASPortal::Event_Activate )
  2263. END_CLASS
  2264. /*
  2265. ===============
  2266. idFuncAASPortal::idFuncAASPortal
  2267. ===============
  2268. */
  2269. idFuncAASPortal::idFuncAASPortal() {
  2270. state = false;
  2271. }
  2272. /*
  2273. ===============
  2274. idFuncAASPortal::Save
  2275. ===============
  2276. */
  2277. void idFuncAASPortal::Save( idSaveGame *savefile ) const {
  2278. savefile->WriteBool( state );
  2279. }
  2280. /*
  2281. ===============
  2282. idFuncAASPortal::Restore
  2283. ===============
  2284. */
  2285. void idFuncAASPortal::Restore( idRestoreGame *savefile ) {
  2286. savefile->ReadBool( state );
  2287. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2288. }
  2289. /*
  2290. ===============
  2291. idFuncAASPortal::Spawn
  2292. ===============
  2293. */
  2294. void idFuncAASPortal::Spawn( void ) {
  2295. state = spawnArgs.GetBool( "start_on" );
  2296. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2297. }
  2298. /*
  2299. ================
  2300. idFuncAASPortal::Event_Activate
  2301. ================
  2302. */
  2303. void idFuncAASPortal::Event_Activate( idEntity *activator ) {
  2304. state ^= 1;
  2305. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2306. }
  2307. /*
  2308. ===============================================================================
  2309. idFuncAASObstacle
  2310. ===============================================================================
  2311. */
  2312. CLASS_DECLARATION( idEntity, idFuncAASObstacle )
  2313. EVENT( EV_Activate, idFuncAASObstacle::Event_Activate )
  2314. END_CLASS
  2315. /*
  2316. ===============
  2317. idFuncAASObstacle::idFuncAASObstacle
  2318. ===============
  2319. */
  2320. idFuncAASObstacle::idFuncAASObstacle() {
  2321. state = false;
  2322. }
  2323. /*
  2324. ===============
  2325. idFuncAASObstacle::Save
  2326. ===============
  2327. */
  2328. void idFuncAASObstacle::Save( idSaveGame *savefile ) const {
  2329. savefile->WriteBool( state );
  2330. }
  2331. /*
  2332. ===============
  2333. idFuncAASObstacle::Restore
  2334. ===============
  2335. */
  2336. void idFuncAASObstacle::Restore( idRestoreGame *savefile ) {
  2337. savefile->ReadBool( state );
  2338. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2339. }
  2340. /*
  2341. ===============
  2342. idFuncAASObstacle::Spawn
  2343. ===============
  2344. */
  2345. void idFuncAASObstacle::Spawn( void ) {
  2346. state = spawnArgs.GetBool( "start_on" );
  2347. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2348. }
  2349. /*
  2350. ================
  2351. idFuncAASObstacle::Event_Activate
  2352. ================
  2353. */
  2354. void idFuncAASObstacle::Event_Activate( idEntity *activator ) {
  2355. state ^= 1;
  2356. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2357. }
  2358. /*
  2359. ===============================================================================
  2360. idFuncRadioChatter
  2361. ===============================================================================
  2362. */
  2363. const idEventDef EV_ResetRadioHud( "<resetradiohud>", "e" );
  2364. CLASS_DECLARATION( idEntity, idFuncRadioChatter )
  2365. EVENT( EV_Activate, idFuncRadioChatter::Event_Activate )
  2366. EVENT( EV_ResetRadioHud, idFuncRadioChatter::Event_ResetRadioHud )
  2367. END_CLASS
  2368. /*
  2369. ===============
  2370. idFuncRadioChatter::idFuncRadioChatter
  2371. ===============
  2372. */
  2373. idFuncRadioChatter::idFuncRadioChatter() {
  2374. time = 0.0;
  2375. }
  2376. /*
  2377. ===============
  2378. idFuncRadioChatter::Save
  2379. ===============
  2380. */
  2381. void idFuncRadioChatter::Save( idSaveGame *savefile ) const {
  2382. savefile->WriteFloat( time );
  2383. }
  2384. /*
  2385. ===============
  2386. idFuncRadioChatter::Restore
  2387. ===============
  2388. */
  2389. void idFuncRadioChatter::Restore( idRestoreGame *savefile ) {
  2390. savefile->ReadFloat( time );
  2391. }
  2392. /*
  2393. ===============
  2394. idFuncRadioChatter::Spawn
  2395. ===============
  2396. */
  2397. void idFuncRadioChatter::Spawn( void ) {
  2398. time = spawnArgs.GetFloat( "time", "5.0" );
  2399. }
  2400. /*
  2401. ================
  2402. idFuncRadioChatter::Event_Activate
  2403. ================
  2404. */
  2405. void idFuncRadioChatter::Event_Activate( idEntity *activator ) {
  2406. idPlayer *player;
  2407. const char *sound;
  2408. const idSoundShader *shader;
  2409. int length;
  2410. if ( activator->IsType( idPlayer::Type ) ) {
  2411. player = static_cast<idPlayer *>( activator );
  2412. } else {
  2413. player = gameLocal.GetLocalPlayer();
  2414. }
  2415. player->hud->HandleNamedEvent( "radioChatterUp" );
  2416. sound = spawnArgs.GetString( "snd_radiochatter", "" );
  2417. if ( sound && *sound ) {
  2418. shader = declManager->FindSound( sound );
  2419. player->StartSoundShader( shader, SND_CHANNEL_RADIO, SSF_GLOBAL, false, &length );
  2420. time = MS2SEC( length + 150 );
  2421. }
  2422. // we still put the hud up because this is used with no sound on
  2423. // certain frame commands when the chatter is triggered
  2424. PostEventSec( &EV_ResetRadioHud, time, player );
  2425. }
  2426. /*
  2427. ================
  2428. idFuncRadioChatter::Event_ResetRadioHud
  2429. ================
  2430. */
  2431. void idFuncRadioChatter::Event_ResetRadioHud( idEntity *activator ) {
  2432. idPlayer *player = ( activator->IsType( idPlayer::Type ) ) ? static_cast<idPlayer *>( activator ) : gameLocal.GetLocalPlayer();
  2433. player->hud->HandleNamedEvent( "radioChatterDown" );
  2434. ActivateTargets( activator );
  2435. }
  2436. /*
  2437. ===============================================================================
  2438. idPhantomObjects
  2439. ===============================================================================
  2440. */
  2441. CLASS_DECLARATION( idEntity, idPhantomObjects )
  2442. EVENT( EV_Activate, idPhantomObjects::Event_Activate )
  2443. END_CLASS
  2444. /*
  2445. ===============
  2446. idPhantomObjects::idPhantomObjects
  2447. ===============
  2448. */
  2449. idPhantomObjects::idPhantomObjects() {
  2450. target = NULL;
  2451. end_time = 0;
  2452. throw_time = 0.0f;
  2453. shake_time = 0.0f;
  2454. shake_ang.Zero();
  2455. speed = 0.0f;
  2456. min_wait = 0;
  2457. max_wait = 0;
  2458. fl.neverDormant = false;
  2459. }
  2460. /*
  2461. ===============
  2462. idPhantomObjects::Save
  2463. ===============
  2464. */
  2465. void idPhantomObjects::Save( idSaveGame *savefile ) const {
  2466. int i;
  2467. savefile->WriteInt( end_time );
  2468. savefile->WriteFloat( throw_time );
  2469. savefile->WriteFloat( shake_time );
  2470. savefile->WriteVec3( shake_ang );
  2471. savefile->WriteFloat( speed );
  2472. savefile->WriteInt( min_wait );
  2473. savefile->WriteInt( max_wait );
  2474. target.Save( savefile );
  2475. savefile->WriteInt( targetTime.Num() );
  2476. for( i = 0; i < targetTime.Num(); i++ ) {
  2477. savefile->WriteInt( targetTime[ i ] );
  2478. }
  2479. for( i = 0; i < lastTargetPos.Num(); i++ ) {
  2480. savefile->WriteVec3( lastTargetPos[ i ] );
  2481. }
  2482. }
  2483. /*
  2484. ===============
  2485. idPhantomObjects::Restore
  2486. ===============
  2487. */
  2488. void idPhantomObjects::Restore( idRestoreGame *savefile ) {
  2489. int num;
  2490. int i;
  2491. savefile->ReadInt( end_time );
  2492. savefile->ReadFloat( throw_time );
  2493. savefile->ReadFloat( shake_time );
  2494. savefile->ReadVec3( shake_ang );
  2495. savefile->ReadFloat( speed );
  2496. savefile->ReadInt( min_wait );
  2497. savefile->ReadInt( max_wait );
  2498. target.Restore( savefile );
  2499. savefile->ReadInt( num );
  2500. targetTime.SetGranularity( 1 );
  2501. targetTime.SetNum( num );
  2502. lastTargetPos.SetGranularity( 1 );
  2503. lastTargetPos.SetNum( num );
  2504. for( i = 0; i < num; i++ ) {
  2505. savefile->ReadInt( targetTime[ i ] );
  2506. }
  2507. if ( savefile->GetBuildNumber() == INITIAL_RELEASE_BUILD_NUMBER ) {
  2508. // these weren't saved out in the first release
  2509. for( i = 0; i < num; i++ ) {
  2510. lastTargetPos[ i ].Zero();
  2511. }
  2512. } else {
  2513. for( i = 0; i < num; i++ ) {
  2514. savefile->ReadVec3( lastTargetPos[ i ] );
  2515. }
  2516. }
  2517. }
  2518. /*
  2519. ===============
  2520. idPhantomObjects::Spawn
  2521. ===============
  2522. */
  2523. void idPhantomObjects::Spawn( void ) {
  2524. throw_time = spawnArgs.GetFloat( "time", "5" );
  2525. speed = spawnArgs.GetFloat( "speed", "1200" );
  2526. shake_time = spawnArgs.GetFloat( "shake_time", "1" );
  2527. throw_time -= shake_time;
  2528. if ( throw_time < 0.0f ) {
  2529. throw_time = 0.0f;
  2530. }
  2531. min_wait = SEC2MS( spawnArgs.GetFloat( "min_wait", "1" ) );
  2532. max_wait = SEC2MS( spawnArgs.GetFloat( "max_wait", "3" ) );
  2533. shake_ang = spawnArgs.GetVector( "shake_ang", "65 65 65" );
  2534. Hide();
  2535. GetPhysics()->SetContents( 0 );
  2536. }
  2537. /*
  2538. ================
  2539. idPhantomObjects::Event_Activate
  2540. ================
  2541. */
  2542. void idPhantomObjects::Event_Activate( idEntity *activator ) {
  2543. int i;
  2544. float time;
  2545. float frac;
  2546. float scale;
  2547. if ( thinkFlags & TH_THINK ) {
  2548. BecomeInactive( TH_THINK );
  2549. return;
  2550. }
  2551. RemoveNullTargets();
  2552. if ( !targets.Num() ) {
  2553. return;
  2554. }
  2555. if ( !activator || !activator->IsType( idActor::Type ) ) {
  2556. target = gameLocal.GetLocalPlayer();
  2557. } else {
  2558. target = static_cast<idActor *>( activator );
  2559. }
  2560. end_time = gameLocal.time + SEC2MS( spawnArgs.GetFloat( "end_time", "0" ) );
  2561. targetTime.SetNum( targets.Num() );
  2562. lastTargetPos.SetNum( targets.Num() );
  2563. const idVec3 &toPos = target.GetEntity()->GetEyePosition();
  2564. // calculate the relative times of all the objects
  2565. time = 0.0f;
  2566. for( i = 0; i < targetTime.Num(); i++ ) {
  2567. targetTime[ i ] = SEC2MS( time );
  2568. lastTargetPos[ i ] = toPos;
  2569. frac = 1.0f - ( float )i / ( float )targetTime.Num();
  2570. time += ( gameLocal.random.RandomFloat() + 1.0f ) * 0.5f * frac + 0.1f;
  2571. }
  2572. // scale up the times to fit within throw_time
  2573. scale = throw_time / time;
  2574. for( i = 0; i < targetTime.Num(); i++ ) {
  2575. targetTime[ i ] = gameLocal.time + SEC2MS( shake_time )+ targetTime[ i ] * scale;
  2576. }
  2577. BecomeActive( TH_THINK );
  2578. }
  2579. /*
  2580. ===============
  2581. idPhantomObjects::Think
  2582. ================
  2583. */
  2584. void idPhantomObjects::Think( void ) {
  2585. int i;
  2586. int num;
  2587. float time;
  2588. idVec3 vel;
  2589. idVec3 ang;
  2590. idEntity *ent;
  2591. idActor *targetEnt;
  2592. idPhysics *entPhys;
  2593. trace_t tr;
  2594. // if we are completely closed off from the player, don't do anything at all
  2595. if ( CheckDormant() ) {
  2596. return;
  2597. }
  2598. if ( !( thinkFlags & TH_THINK ) ) {
  2599. BecomeInactive( thinkFlags & ~TH_THINK );
  2600. return;
  2601. }
  2602. targetEnt = target.GetEntity();
  2603. if ( !targetEnt || ( targetEnt->health <= 0 ) || ( end_time && ( gameLocal.time > end_time ) ) || gameLocal.inCinematic ) {
  2604. BecomeInactive( TH_THINK );
  2605. }
  2606. const idVec3 &toPos = targetEnt->GetEyePosition();
  2607. num = 0;
  2608. for ( i = 0; i < targets.Num(); i++ ) {
  2609. ent = targets[ i ].GetEntity();
  2610. if ( !ent ) {
  2611. continue;
  2612. }
  2613. if ( ent->fl.hidden ) {
  2614. // don't throw hidden objects
  2615. continue;
  2616. }
  2617. if ( !targetTime[ i ] ) {
  2618. // already threw this object
  2619. continue;
  2620. }
  2621. num++;
  2622. time = MS2SEC( targetTime[ i ] - gameLocal.time );
  2623. if ( time > shake_time ) {
  2624. continue;
  2625. }
  2626. entPhys = ent->GetPhysics();
  2627. const idVec3 &entOrg = entPhys->GetOrigin();
  2628. gameLocal.clip.TracePoint( tr, entOrg, toPos, MASK_OPAQUE, ent );
  2629. if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == targetEnt ) ) {
  2630. lastTargetPos[ i ] = toPos;
  2631. }
  2632. if ( time < 0.0f ) {
  2633. idAI::PredictTrajectory( entPhys->GetOrigin(), lastTargetPos[ i ], speed, entPhys->GetGravity(),
  2634. entPhys->GetClipModel(), entPhys->GetClipMask(), 256.0f, ent, targetEnt, ai_debugTrajectory.GetBool() ? 1 : 0, vel );
  2635. vel *= speed;
  2636. entPhys->SetLinearVelocity( vel );
  2637. if ( !end_time ) {
  2638. targetTime[ i ] = 0;
  2639. } else {
  2640. targetTime[ i ] = gameLocal.time + gameLocal.random.RandomInt( max_wait - min_wait ) + min_wait;
  2641. }
  2642. if ( ent->IsType( idMoveable::Type ) ) {
  2643. idMoveable *ment = static_cast<idMoveable*>( ent );
  2644. ment->EnableDamage( true, 2.5f );
  2645. }
  2646. } else {
  2647. // this is not the right way to set the angular velocity, but the effect is nice, so I'm keeping it. :)
  2648. ang.Set( gameLocal.random.CRandomFloat() * shake_ang.x, gameLocal.random.CRandomFloat() * shake_ang.y, gameLocal.random.CRandomFloat() * shake_ang.z );
  2649. ang *= ( 1.0f - time / shake_time );
  2650. entPhys->SetAngularVelocity( ang );
  2651. }
  2652. }
  2653. if ( !num ) {
  2654. BecomeInactive( TH_THINK );
  2655. }
  2656. }