PageRenderTime 61ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/neo/d3xp/Misc.cpp

https://bitbucket.org/peanut64/iodoom3-copy-original
C++ | 3753 lines | 1981 code | 541 blank | 1231 comment | 321 complexity | 83cfe221af36628eda5dd7bb0df35ee0 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. #ifdef _D3XP
  160. SetTimeState ts( player->timeGroup );
  161. #endif
  162. if ( f && ent ) {
  163. // place in private camera view for some time
  164. // the entity needs to teleport to where the camera view is to have the PVS right
  165. player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this );
  166. player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL );
  167. player->SetPrivateCameraView( static_cast<idCamera*>(ent) );
  168. // the player entity knows where to spawn from the previous Teleport call
  169. if ( !gameLocal.isClient ) {
  170. player->PostEventSec( &EV_Player_ExitTeleporter, f );
  171. }
  172. } else {
  173. // direct to exit, Teleport will take care of the killbox
  174. player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL );
  175. // multiplayer hijacked this entity, so only push the player in multiplayer
  176. if ( gameLocal.isMultiplayer ) {
  177. player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel );
  178. }
  179. }
  180. }
  181. /*
  182. ===============
  183. idPlayerStart::Event_TeleportPlayer
  184. ================
  185. */
  186. void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) {
  187. idPlayer *player;
  188. if ( activator->IsType( idPlayer::Type ) ) {
  189. player = static_cast<idPlayer*>( activator );
  190. } else {
  191. player = gameLocal.GetLocalPlayer();
  192. }
  193. if ( player ) {
  194. if ( spawnArgs.GetBool( "visualFx" ) ) {
  195. teleportStage = 0;
  196. Event_TeleportStage( player );
  197. } else {
  198. if ( gameLocal.isServer ) {
  199. idBitMsg msg;
  200. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  201. msg.Init( msgBuf, sizeof( msgBuf ) );
  202. msg.BeginWriting();
  203. msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
  204. ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false, -1 );
  205. }
  206. TeleportPlayer( player );
  207. }
  208. }
  209. }
  210. /*
  211. ===============================================================================
  212. idActivator
  213. ===============================================================================
  214. */
  215. CLASS_DECLARATION( idEntity, idActivator )
  216. EVENT( EV_Activate, idActivator::Event_Activate )
  217. END_CLASS
  218. /*
  219. ===============
  220. idActivator::Save
  221. ================
  222. */
  223. void idActivator::Save( idSaveGame *savefile ) const {
  224. savefile->WriteBool( stay_on );
  225. }
  226. /*
  227. ===============
  228. idActivator::Restore
  229. ================
  230. */
  231. void idActivator::Restore( idRestoreGame *savefile ) {
  232. savefile->ReadBool( stay_on );
  233. if ( stay_on ) {
  234. BecomeActive( TH_THINK );
  235. }
  236. }
  237. /*
  238. ===============
  239. idActivator::Spawn
  240. ================
  241. */
  242. void idActivator::Spawn( void ) {
  243. bool start_off;
  244. spawnArgs.GetBool( "stay_on", "0", stay_on );
  245. spawnArgs.GetBool( "start_off", "0", start_off );
  246. GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f );
  247. GetPhysics()->SetContents( 0 );
  248. if ( !start_off ) {
  249. BecomeActive( TH_THINK );
  250. }
  251. }
  252. /*
  253. ===============
  254. idActivator::Think
  255. ================
  256. */
  257. void idActivator::Think( void ) {
  258. RunPhysics();
  259. if ( thinkFlags & TH_THINK ) {
  260. if ( TouchTriggers() ) {
  261. if ( !stay_on ) {
  262. BecomeInactive( TH_THINK );
  263. }
  264. }
  265. }
  266. Present();
  267. }
  268. /*
  269. ===============
  270. idActivator::Activate
  271. ================
  272. */
  273. void idActivator::Event_Activate( idEntity *activator ) {
  274. if ( thinkFlags & TH_THINK ) {
  275. BecomeInactive( TH_THINK );
  276. } else {
  277. BecomeActive( TH_THINK );
  278. }
  279. }
  280. /*
  281. ===============================================================================
  282. idPathCorner
  283. ===============================================================================
  284. */
  285. CLASS_DECLARATION( idEntity, idPathCorner )
  286. EVENT( AI_RandomPath, idPathCorner::Event_RandomPath )
  287. END_CLASS
  288. /*
  289. =====================
  290. idPathCorner::Spawn
  291. =====================
  292. */
  293. void idPathCorner::Spawn( void ) {
  294. }
  295. /*
  296. =====================
  297. idPathCorner::DrawDebugInfo
  298. =====================
  299. */
  300. void idPathCorner::DrawDebugInfo( void ) {
  301. idEntity *ent;
  302. idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) );
  303. for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  304. if ( !ent->IsType( idPathCorner::Type ) ) {
  305. continue;
  306. }
  307. idVec3 org = ent->GetPhysics()->GetOrigin();
  308. gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 );
  309. }
  310. }
  311. /*
  312. ============
  313. idPathCorner::RandomPath
  314. ============
  315. */
  316. idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) {
  317. int i;
  318. int num;
  319. int which;
  320. idEntity *ent;
  321. idPathCorner *path[ MAX_GENTITIES ];
  322. num = 0;
  323. for( i = 0; i < source->targets.Num(); i++ ) {
  324. ent = source->targets[ i ].GetEntity();
  325. if ( ent && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) {
  326. path[ num++ ] = static_cast<idPathCorner *>( ent );
  327. if ( num >= MAX_GENTITIES ) {
  328. break;
  329. }
  330. }
  331. }
  332. if ( !num ) {
  333. return NULL;
  334. }
  335. which = gameLocal.random.RandomInt( num );
  336. return path[ which ];
  337. }
  338. /*
  339. =====================
  340. idPathCorner::Event_RandomPath
  341. =====================
  342. */
  343. void idPathCorner::Event_RandomPath( void ) {
  344. idPathCorner *path;
  345. path = RandomPath( this, NULL );
  346. idThread::ReturnEntity( path );
  347. }
  348. /*
  349. ===============================================================================
  350. idDamagable
  351. ===============================================================================
  352. */
  353. const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" );
  354. CLASS_DECLARATION( idEntity, idDamagable )
  355. EVENT( EV_Activate, idDamagable::Event_BecomeBroken )
  356. EVENT( EV_RestoreDamagable, idDamagable::Event_RestoreDamagable )
  357. END_CLASS
  358. /*
  359. ================
  360. idDamagable::idDamagable
  361. ================
  362. */
  363. idDamagable::idDamagable( void ) {
  364. count = 0;
  365. nextTriggerTime = 0;
  366. }
  367. /*
  368. ================
  369. idDamagable::Save
  370. ================
  371. */
  372. void idDamagable::Save( idSaveGame *savefile ) const {
  373. savefile->WriteInt( count );
  374. savefile->WriteInt( nextTriggerTime );
  375. }
  376. /*
  377. ================
  378. idDamagable::Restore
  379. ================
  380. */
  381. void idDamagable::Restore( idRestoreGame *savefile ) {
  382. savefile->ReadInt( count );
  383. savefile->ReadInt( nextTriggerTime );
  384. }
  385. /*
  386. ================
  387. idDamagable::Spawn
  388. ================
  389. */
  390. void idDamagable::Spawn( void ) {
  391. idStr broken;
  392. health = spawnArgs.GetInt( "health", "5" );
  393. spawnArgs.GetInt( "count", "1", count );
  394. nextTriggerTime = 0;
  395. // make sure the model gets cached
  396. spawnArgs.GetString( "broken", "", broken );
  397. if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) {
  398. gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() );
  399. }
  400. fl.takedamage = true;
  401. GetPhysics()->SetContents( CONTENTS_SOLID );
  402. }
  403. /*
  404. ================
  405. idDamagable::BecomeBroken
  406. ================
  407. */
  408. void idDamagable::BecomeBroken( idEntity *activator ) {
  409. float forceState;
  410. int numStates;
  411. int cycle;
  412. float wait;
  413. if ( gameLocal.time < nextTriggerTime ) {
  414. return;
  415. }
  416. spawnArgs.GetFloat( "wait", "0.1", wait );
  417. nextTriggerTime = gameLocal.time + SEC2MS( wait );
  418. if ( count > 0 ) {
  419. count--;
  420. if ( !count ) {
  421. fl.takedamage = false;
  422. } else {
  423. health = spawnArgs.GetInt( "health", "5" );
  424. }
  425. }
  426. idStr broken;
  427. spawnArgs.GetString( "broken", "", broken );
  428. if ( broken.Length() ) {
  429. SetModel( broken );
  430. }
  431. // offset the start time of the shader to sync it to the gameLocal time
  432. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  433. spawnArgs.GetInt( "numstates", "1", numStates );
  434. spawnArgs.GetInt( "cycle", "0", cycle );
  435. spawnArgs.GetFloat( "forcestate", "0", forceState );
  436. // set the state parm
  437. if ( cycle ) {
  438. renderEntity.shaderParms[ SHADERPARM_MODE ]++;
  439. if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) {
  440. renderEntity.shaderParms[ SHADERPARM_MODE ] = 0;
  441. }
  442. } else if ( forceState ) {
  443. renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState;
  444. } else {
  445. renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1;
  446. }
  447. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  448. ActivateTargets( activator );
  449. if ( spawnArgs.GetBool( "hideWhenBroken" ) ) {
  450. Hide();
  451. PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time );
  452. BecomeActive( TH_THINK );
  453. }
  454. }
  455. /*
  456. ================
  457. idDamagable::Killed
  458. ================
  459. */
  460. void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  461. if ( gameLocal.time < nextTriggerTime ) {
  462. health += damage;
  463. return;
  464. }
  465. BecomeBroken( attacker );
  466. }
  467. #ifdef _D3XP
  468. /*
  469. ================
  470. idDamagable::Hide
  471. ================
  472. */
  473. void idDamagable::Hide( void ) {
  474. idEntity::Hide();
  475. GetPhysics()->SetContents( 0 );
  476. }
  477. /*
  478. ================
  479. idDamagable::Show
  480. ================
  481. */
  482. void idDamagable::Show( void ) {
  483. idEntity::Show();
  484. GetPhysics()->SetContents( CONTENTS_SOLID );
  485. }
  486. #endif
  487. /*
  488. ================
  489. idDamagable::Event_BecomeBroken
  490. ================
  491. */
  492. void idDamagable::Event_BecomeBroken( idEntity *activator ) {
  493. BecomeBroken( activator );
  494. }
  495. /*
  496. ================
  497. idDamagable::Event_RestoreDamagable
  498. ================
  499. */
  500. void idDamagable::Event_RestoreDamagable( void ) {
  501. health = spawnArgs.GetInt( "health", "5" );
  502. Show();
  503. }
  504. /*
  505. ===============================================================================
  506. idExplodable
  507. ===============================================================================
  508. */
  509. CLASS_DECLARATION( idEntity, idExplodable )
  510. EVENT( EV_Activate, idExplodable::Event_Explode )
  511. END_CLASS
  512. /*
  513. ================
  514. idExplodable::Spawn
  515. ================
  516. */
  517. void idExplodable::Spawn( void ) {
  518. Hide();
  519. }
  520. /*
  521. ================
  522. idExplodable::Event_Explode
  523. ================
  524. */
  525. void idExplodable::Event_Explode( idEntity *activator ) {
  526. const char *temp;
  527. if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) {
  528. gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp );
  529. }
  530. StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
  531. // Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms
  532. renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
  533. renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
  534. renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
  535. renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
  536. renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  537. renderEntity.shaderParms[SHADERPARM_DIVERSITY] = 0.0f;
  538. Show();
  539. PostEventMS( &EV_Remove, 2000 );
  540. ActivateTargets( activator );
  541. }
  542. /*
  543. ===============================================================================
  544. idSpring
  545. ===============================================================================
  546. */
  547. CLASS_DECLARATION( idEntity, idSpring )
  548. EVENT( EV_PostSpawn, idSpring::Event_LinkSpring )
  549. END_CLASS
  550. /*
  551. ================
  552. idSpring::Think
  553. ================
  554. */
  555. void idSpring::Think( void ) {
  556. idVec3 start, end, origin;
  557. idMat3 axis;
  558. // run physics
  559. RunPhysics();
  560. if ( thinkFlags & TH_THINK ) {
  561. // evaluate force
  562. spring.Evaluate( gameLocal.time );
  563. start = p1;
  564. if ( ent1->GetPhysics() ) {
  565. axis = ent1->GetPhysics()->GetAxis();
  566. origin = ent1->GetPhysics()->GetOrigin();
  567. start = origin + start * axis;
  568. }
  569. end = p2;
  570. if ( ent2->GetPhysics() ) {
  571. axis = ent2->GetPhysics()->GetAxis();
  572. origin = ent2->GetPhysics()->GetOrigin();
  573. end = origin + p2 * axis;
  574. }
  575. gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true );
  576. }
  577. Present();
  578. }
  579. /*
  580. ================
  581. idSpring::Event_LinkSpring
  582. ================
  583. */
  584. void idSpring::Event_LinkSpring( void ) {
  585. idStr name1, name2;
  586. spawnArgs.GetString( "ent1", "", name1 );
  587. spawnArgs.GetString( "ent2", "", name2 );
  588. if ( name1.Length() ) {
  589. ent1 = gameLocal.FindEntity( name1 );
  590. if ( !ent1 ) {
  591. gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() );
  592. }
  593. }
  594. else {
  595. ent1 = gameLocal.entities[ENTITYNUM_WORLD];
  596. }
  597. if ( name2.Length() ) {
  598. ent2 = gameLocal.FindEntity( name2 );
  599. if ( !ent2 ) {
  600. gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() );
  601. }
  602. }
  603. else {
  604. ent2 = gameLocal.entities[ENTITYNUM_WORLD];
  605. }
  606. spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 );
  607. BecomeActive( TH_THINK );
  608. }
  609. /*
  610. ================
  611. idSpring::Spawn
  612. ================
  613. */
  614. void idSpring::Spawn( void ) {
  615. float Kstretch, damping, restLength;
  616. spawnArgs.GetInt( "id1", "0", id1 );
  617. spawnArgs.GetInt( "id2", "0", id2 );
  618. spawnArgs.GetVector( "point1", "0 0 0", p1 );
  619. spawnArgs.GetVector( "point2", "0 0 0", p2 );
  620. spawnArgs.GetFloat( "constant", "100.0f", Kstretch );
  621. spawnArgs.GetFloat( "damping", "10.0f", damping );
  622. spawnArgs.GetFloat( "restlength", "0.0f", restLength );
  623. spring.InitSpring( Kstretch, 0.0f, damping, restLength );
  624. ent1 = ent2 = NULL;
  625. PostEventMS( &EV_PostSpawn, 0 );
  626. }
  627. /*
  628. ===============================================================================
  629. idForceField
  630. ===============================================================================
  631. */
  632. const idEventDef EV_Toggle( "Toggle", NULL );
  633. CLASS_DECLARATION( idEntity, idForceField )
  634. EVENT( EV_Activate, idForceField::Event_Activate )
  635. EVENT( EV_Toggle, idForceField::Event_Toggle )
  636. EVENT( EV_FindTargets, idForceField::Event_FindTargets )
  637. END_CLASS
  638. /*
  639. ===============
  640. idForceField::Toggle
  641. ================
  642. */
  643. void idForceField::Toggle( void ) {
  644. if ( thinkFlags & TH_THINK ) {
  645. BecomeInactive( TH_THINK );
  646. } else {
  647. BecomeActive( TH_THINK );
  648. }
  649. }
  650. /*
  651. ================
  652. idForceField::Think
  653. ================
  654. */
  655. void idForceField::Think( void ) {
  656. if ( thinkFlags & TH_THINK ) {
  657. // evaluate force
  658. forceField.Evaluate( gameLocal.time );
  659. }
  660. Present();
  661. }
  662. /*
  663. ================
  664. idForceField::Save
  665. ================
  666. */
  667. void idForceField::Save( idSaveGame *savefile ) const {
  668. savefile->WriteStaticObject( forceField );
  669. }
  670. /*
  671. ================
  672. idForceField::Restore
  673. ================
  674. */
  675. void idForceField::Restore( idRestoreGame *savefile ) {
  676. savefile->ReadStaticObject( forceField );
  677. }
  678. /*
  679. ================
  680. idForceField::Spawn
  681. ================
  682. */
  683. void idForceField::Spawn( void ) {
  684. idVec3 uniform;
  685. float explosion, implosion, randomTorque;
  686. if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) {
  687. forceField.Uniform( uniform );
  688. } else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) {
  689. forceField.Explosion( explosion );
  690. } else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) {
  691. forceField.Implosion( implosion );
  692. }
  693. if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) {
  694. forceField.RandomTorque( randomTorque );
  695. }
  696. if ( spawnArgs.GetBool( "applyForce", "0" ) ) {
  697. forceField.SetApplyType( FORCEFIELD_APPLY_FORCE );
  698. } else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) {
  699. forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE );
  700. } else {
  701. forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY );
  702. }
  703. forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) );
  704. forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) );
  705. // set the collision model on the force field
  706. forceField.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ) );
  707. // remove the collision model from the physics object
  708. GetPhysics()->SetClipModel( NULL, 1.0f );
  709. if ( spawnArgs.GetBool( "start_on" ) ) {
  710. BecomeActive( TH_THINK );
  711. }
  712. }
  713. /*
  714. ===============
  715. idForceField::Event_Toggle
  716. ================
  717. */
  718. void idForceField::Event_Toggle( void ) {
  719. Toggle();
  720. }
  721. /*
  722. ================
  723. idForceField::Event_Activate
  724. ================
  725. */
  726. void idForceField::Event_Activate( idEntity *activator ) {
  727. float wait;
  728. Toggle();
  729. if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) {
  730. PostEventSec( &EV_Toggle, wait );
  731. }
  732. }
  733. /*
  734. ================
  735. idForceField::Event_FindTargets
  736. ================
  737. */
  738. void idForceField::Event_FindTargets( void ) {
  739. FindTargets();
  740. RemoveNullTargets();
  741. if ( targets.Num() ) {
  742. forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() );
  743. }
  744. }
  745. /*
  746. ===============================================================================
  747. idAnimated
  748. ===============================================================================
  749. */
  750. const idEventDef EV_Animated_Start( "<start>" );
  751. const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" );
  752. const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" );
  753. const idEventDef EV_AnimDone( "<AnimDone>", "d" );
  754. const idEventDef EV_StartRagdoll( "startRagdoll" );
  755. #ifdef _D3XP
  756. const idEventDef EV_SetAnimation( "setAnimation", "s" );
  757. const idEventDef EV_GetAnimationLength( "getAnimationLength", NULL, 'f' );
  758. #endif
  759. CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated )
  760. EVENT( EV_Activate, idAnimated::Event_Activate )
  761. EVENT( EV_Animated_Start, idAnimated::Event_Start )
  762. EVENT( EV_StartRagdoll, idAnimated::Event_StartRagdoll )
  763. EVENT( EV_AnimDone, idAnimated::Event_AnimDone )
  764. EVENT( EV_Footstep, idAnimated::Event_Footstep )
  765. EVENT( EV_FootstepLeft, idAnimated::Event_Footstep )
  766. EVENT( EV_FootstepRight, idAnimated::Event_Footstep )
  767. EVENT( EV_LaunchMissiles, idAnimated::Event_LaunchMissiles )
  768. EVENT( EV_LaunchMissilesUpdate, idAnimated::Event_LaunchMissilesUpdate )
  769. #ifdef _D3XP
  770. EVENT( EV_SetAnimation, idAnimated::Event_SetAnimation )
  771. EVENT( EV_GetAnimationLength, idAnimated::Event_GetAnimationLength )
  772. #endif
  773. END_CLASS
  774. /*
  775. ===============
  776. idAnimated::idAnimated
  777. ================
  778. */
  779. idAnimated::idAnimated() {
  780. anim = 0;
  781. blendFrames = 0;
  782. soundJoint = INVALID_JOINT;
  783. activated = false;
  784. combatModel = NULL;
  785. activator = NULL;
  786. current_anim_index = 0;
  787. num_anims = 0;
  788. }
  789. /*
  790. ===============
  791. idAnimated::idAnimated
  792. ================
  793. */
  794. idAnimated::~idAnimated() {
  795. delete combatModel;
  796. combatModel = NULL;
  797. }
  798. /*
  799. ===============
  800. idAnimated::Save
  801. ================
  802. */
  803. void idAnimated::Save( idSaveGame *savefile ) const {
  804. savefile->WriteInt( current_anim_index );
  805. savefile->WriteInt( num_anims );
  806. savefile->WriteInt( anim );
  807. savefile->WriteInt( blendFrames );
  808. savefile->WriteJoint( soundJoint );
  809. activator.Save( savefile );
  810. savefile->WriteBool( activated );
  811. }
  812. /*
  813. ===============
  814. idAnimated::Restore
  815. ================
  816. */
  817. void idAnimated::Restore( idRestoreGame *savefile ) {
  818. savefile->ReadInt( current_anim_index );
  819. savefile->ReadInt( num_anims );
  820. savefile->ReadInt( anim );
  821. savefile->ReadInt( blendFrames );
  822. savefile->ReadJoint( soundJoint );
  823. activator.Restore( savefile );
  824. savefile->ReadBool( activated );
  825. }
  826. /*
  827. ===============
  828. idAnimated::Spawn
  829. ================
  830. */
  831. void idAnimated::Spawn( void ) {
  832. idStr animname;
  833. int anim2;
  834. float wait;
  835. const char *joint;
  836. joint = spawnArgs.GetString( "sound_bone", "origin" );
  837. soundJoint = animator.GetJointHandle( joint );
  838. if ( soundJoint == INVALID_JOINT ) {
  839. gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint );
  840. }
  841. LoadAF();
  842. // allow bullets to collide with a combat model
  843. if ( spawnArgs.GetBool( "combatModel", "0" ) ) {
  844. combatModel = new idClipModel( modelDefHandle );
  845. }
  846. // allow the entity to take damage
  847. if ( spawnArgs.GetBool( "takeDamage", "0" ) ) {
  848. fl.takedamage = true;
  849. }
  850. blendFrames = 0;
  851. current_anim_index = 0;
  852. spawnArgs.GetInt( "num_anims", "0", num_anims );
  853. blendFrames = spawnArgs.GetInt( "blend_in" );
  854. animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" );
  855. if ( !animname.Length() ) {
  856. anim = 0;
  857. } else {
  858. anim = animator.GetAnim( animname );
  859. if ( !anim ) {
  860. gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
  861. }
  862. }
  863. if ( spawnArgs.GetBool( "hide" ) ) {
  864. Hide();
  865. if ( !num_anims ) {
  866. blendFrames = 0;
  867. }
  868. } else if ( spawnArgs.GetString( "start_anim", "", animname ) ) {
  869. anim2 = animator.GetAnim( animname );
  870. if ( !anim2 ) {
  871. gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
  872. }
  873. animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 );
  874. } else if ( anim ) {
  875. // init joints to the first frame of the animation
  876. animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 );
  877. if ( !num_anims ) {
  878. blendFrames = 0;
  879. }
  880. }
  881. spawnArgs.GetFloat( "wait", "-1", wait );
  882. if ( wait >= 0 ) {
  883. PostEventSec( &EV_Activate, wait, this );
  884. }
  885. }
  886. /*
  887. ===============
  888. idAnimated::LoadAF
  889. ===============
  890. */
  891. bool idAnimated::LoadAF( void ) {
  892. idStr fileName;
  893. if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) {
  894. return false;
  895. }
  896. af.SetAnimator( GetAnimator() );
  897. return af.Load( this, fileName );
  898. }
  899. /*
  900. ===============
  901. idAnimated::GetPhysicsToSoundTransform
  902. ===============
  903. */
  904. bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  905. animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
  906. axis = renderEntity.axis;
  907. return true;
  908. }
  909. /*
  910. ================
  911. idAnimated::StartRagdoll
  912. ================
  913. */
  914. bool idAnimated::StartRagdoll( void ) {
  915. // if no AF loaded
  916. if ( !af.IsLoaded() ) {
  917. return false;
  918. }
  919. // if the AF is already active
  920. if ( af.IsActive() ) {
  921. return true;
  922. }
  923. // disable any collision model used
  924. GetPhysics()->DisableClip();
  925. // start using the AF
  926. af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
  927. return true;
  928. }
  929. /*
  930. =====================
  931. idAnimated::PlayNextAnim
  932. =====================
  933. */
  934. void idAnimated::PlayNextAnim( void ) {
  935. const char *animname;
  936. int len;
  937. int cycle;
  938. if ( current_anim_index >= num_anims ) {
  939. Hide();
  940. if ( spawnArgs.GetBool( "remove" ) ) {
  941. PostEventMS( &EV_Remove, 0 );
  942. } else {
  943. current_anim_index = 0;
  944. }
  945. return;
  946. }
  947. Show();
  948. current_anim_index++;
  949. spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname );
  950. if ( !animname ) {
  951. anim = 0;
  952. animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) );
  953. return;
  954. }
  955. anim = animator.GetAnim( animname );
  956. if ( !anim ) {
  957. gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() );
  958. return;
  959. }
  960. if ( g_debugCinematic.GetBool() ) {
  961. gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname );
  962. }
  963. spawnArgs.GetInt( "cycle", "1", cycle );
  964. if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) {
  965. cycle = -1;
  966. }
  967. animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
  968. animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
  969. len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
  970. if ( len >= 0 ) {
  971. PostEventMS( &EV_AnimDone, len, current_anim_index );
  972. }
  973. // offset the start time of the shader to sync it to the game time
  974. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  975. animator.ForceUpdate();
  976. UpdateAnimation();
  977. UpdateVisuals();
  978. Present();
  979. }
  980. /*
  981. ===============
  982. idAnimated::Event_StartRagdoll
  983. ================
  984. */
  985. void idAnimated::Event_StartRagdoll( void ) {
  986. StartRagdoll();
  987. }
  988. /*
  989. ===============
  990. idAnimated::Event_AnimDone
  991. ================
  992. */
  993. void idAnimated::Event_AnimDone( int animindex ) {
  994. if ( g_debugCinematic.GetBool() ) {
  995. const idAnim *animPtr = animator.GetAnim( anim );
  996. gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
  997. }
  998. if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) {
  999. Hide();
  1000. PostEventMS( &EV_Remove, 0 );
  1001. } else if ( spawnArgs.GetBool( "auto_advance" ) ) {
  1002. PlayNextAnim();
  1003. } else {
  1004. activated = false;
  1005. }
  1006. ActivateTargets( activator.GetEntity() );
  1007. }
  1008. /*
  1009. ===============
  1010. idAnimated::Event_Activate
  1011. ================
  1012. */
  1013. void idAnimated::Event_Activate( idEntity *_activator ) {
  1014. if ( num_anims ) {
  1015. PlayNextAnim();
  1016. activator = _activator;
  1017. return;
  1018. }
  1019. if ( activated ) {
  1020. // already activated
  1021. return;
  1022. }
  1023. activated = true;
  1024. activator = _activator;
  1025. ProcessEvent( &EV_Animated_Start );
  1026. }
  1027. /*
  1028. ===============
  1029. idAnimated::Event_Start
  1030. ================
  1031. */
  1032. void idAnimated::Event_Start( void ) {
  1033. int cycle;
  1034. int len;
  1035. Show();
  1036. if ( num_anims ) {
  1037. PlayNextAnim();
  1038. return;
  1039. }
  1040. if ( anim ) {
  1041. if ( g_debugCinematic.GetBool() ) {
  1042. const idAnim *animPtr = animator.GetAnim( anim );
  1043. gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
  1044. }
  1045. spawnArgs.GetInt( "cycle", "1", cycle );
  1046. animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
  1047. animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
  1048. len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
  1049. if ( len >= 0 ) {
  1050. PostEventMS( &EV_AnimDone, len, 1 );
  1051. }
  1052. }
  1053. // offset the start time of the shader to sync it to the game time
  1054. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1055. animator.ForceUpdate();
  1056. UpdateAnimation();
  1057. UpdateVisuals();
  1058. Present();
  1059. }
  1060. /*
  1061. ===============
  1062. idAnimated::Event_Footstep
  1063. ===============
  1064. */
  1065. void idAnimated::Event_Footstep( void ) {
  1066. StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL );
  1067. }
  1068. /*
  1069. =====================
  1070. idAnimated::Event_LaunchMissilesUpdate
  1071. =====================
  1072. */
  1073. void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) {
  1074. idVec3 launchPos;
  1075. idVec3 targetPos;
  1076. idMat3 axis;
  1077. idVec3 dir;
  1078. idEntity * ent;
  1079. idProjectile * projectile;
  1080. const idDict * projectileDef;
  1081. const char * projectilename;
  1082. projectilename = spawnArgs.GetString( "projectilename" );
  1083. projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
  1084. if ( !projectileDef ) {
  1085. gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1086. return;
  1087. }
  1088. StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL );
  1089. animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis );
  1090. launchPos = renderEntity.origin + launchPos * renderEntity.axis;
  1091. animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis );
  1092. targetPos = renderEntity.origin + targetPos * renderEntity.axis;
  1093. dir = targetPos - launchPos;
  1094. dir.Normalize();
  1095. gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
  1096. if ( !ent || !ent->IsType( idProjectile::Type ) ) {
  1097. gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1098. }
  1099. projectile = ( idProjectile * )ent;
  1100. projectile->Create( this, launchPos, dir );
  1101. projectile->Launch( launchPos, dir, vec3_origin );
  1102. if ( numshots > 0 ) {
  1103. PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay );
  1104. }
  1105. }
  1106. /*
  1107. =====================
  1108. idAnimated::Event_LaunchMissiles
  1109. =====================
  1110. */
  1111. void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) {
  1112. const idDict * projectileDef;
  1113. jointHandle_t launch;
  1114. jointHandle_t target;
  1115. projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
  1116. if ( !projectileDef ) {
  1117. gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
  1118. return;
  1119. }
  1120. launch = animator.GetJointHandle( launchjoint );
  1121. if ( launch == INVALID_JOINT ) {
  1122. gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint );
  1123. gameLocal.Error( "Unknown joint '%s'", launchjoint );
  1124. }
  1125. target = animator.GetJointHandle( targetjoint );
  1126. if ( target == INVALID_JOINT ) {
  1127. gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint );
  1128. }
  1129. spawnArgs.Set( "projectilename", projectilename );
  1130. spawnArgs.Set( "missilesound", sound );
  1131. CancelEvents( &EV_LaunchMissilesUpdate );
  1132. ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay );
  1133. }
  1134. #ifdef _D3XP
  1135. /*
  1136. =====================
  1137. idAnimated::Event_SetAnimation
  1138. =====================
  1139. */
  1140. void idAnimated::Event_SetAnimation( const char *animName ) {
  1141. //BSM Nerve: Need to add some error checking so we don't change the animation
  1142. //in the middle of the existing animation
  1143. anim = animator.GetAnim( animName );
  1144. if ( !anim ) {
  1145. gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animName );
  1146. }
  1147. }
  1148. /*
  1149. =====================
  1150. idAnimated::Event_GetAnimationLength
  1151. =====================
  1152. */
  1153. void idAnimated::Event_GetAnimationLength() {
  1154. float length = 0;
  1155. if(anim) {
  1156. length = (float)(animator.AnimLength( anim )) / 1000.f;
  1157. }
  1158. idThread::ReturnFloat(length);
  1159. }
  1160. #endif
  1161. /*
  1162. ===============================================================================
  1163. idStaticEntity
  1164. Some static entities may be optimized into inline geometry by dmap
  1165. ===============================================================================
  1166. */
  1167. CLASS_DECLARATION( idEntity, idStaticEntity )
  1168. EVENT( EV_Activate, idStaticEntity::Event_Activate )
  1169. END_CLASS
  1170. /*
  1171. ===============
  1172. idStaticEntity::idStaticEntity
  1173. ===============
  1174. */
  1175. idStaticEntity::idStaticEntity( void ) {
  1176. spawnTime = 0;
  1177. active = false;
  1178. fadeFrom.Set( 1, 1, 1, 1 );
  1179. fadeTo.Set( 1, 1, 1, 1 );
  1180. fadeStart = 0;
  1181. fadeEnd = 0;
  1182. runGui = false;
  1183. }
  1184. /*
  1185. ===============
  1186. idStaticEntity::Save
  1187. ===============
  1188. */
  1189. void idStaticEntity::Save( idSaveGame *savefile ) const {
  1190. savefile->WriteInt( spawnTime );
  1191. savefile->WriteBool( active );
  1192. savefile->WriteVec4( fadeFrom );
  1193. savefile->WriteVec4( fadeTo );
  1194. savefile->WriteInt( fadeStart );
  1195. savefile->WriteInt( fadeEnd );
  1196. savefile->WriteBool( runGui );
  1197. }
  1198. /*
  1199. ===============
  1200. idStaticEntity::Restore
  1201. ===============
  1202. */
  1203. void idStaticEntity::Restore( idRestoreGame *savefile ) {
  1204. savefile->ReadInt( spawnTime );
  1205. savefile->ReadBool( active );
  1206. savefile->ReadVec4( fadeFrom );
  1207. savefile->ReadVec4( fadeTo );
  1208. savefile->ReadInt( fadeStart );
  1209. savefile->ReadInt( fadeEnd );
  1210. savefile->ReadBool( runGui );
  1211. }
  1212. /*
  1213. ===============
  1214. idStaticEntity::Spawn
  1215. ===============
  1216. */
  1217. void idStaticEntity::Spawn( void ) {
  1218. bool solid;
  1219. bool hidden;
  1220. // an inline static model will not do anything at all
  1221. if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) {
  1222. Hide();
  1223. return;
  1224. }
  1225. solid = spawnArgs.GetBool( "solid" );
  1226. hidden = spawnArgs.GetBool( "hide" );
  1227. if ( solid && !hidden ) {
  1228. GetPhysics()->SetContents( CONTENTS_SOLID );
  1229. } else {
  1230. GetPhysics()->SetContents( 0 );
  1231. }
  1232. spawnTime = gameLocal.time;
  1233. active = false;
  1234. idStr model = spawnArgs.GetString( "model" );
  1235. if ( model.Find( ".prt" ) >= 0 ) {
  1236. // we want the parametric particles out of sync with each other
  1237. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 );
  1238. }
  1239. fadeFrom.Set( 1, 1, 1, 1 );
  1240. fadeTo.Set( 1, 1, 1, 1 );
  1241. fadeStart = 0;
  1242. fadeEnd = 0;
  1243. // NOTE: this should be used very rarely because it is expensive
  1244. runGui = spawnArgs.GetBool( "runGui" );
  1245. if ( runGui ) {
  1246. BecomeActive( TH_THINK );
  1247. }
  1248. }
  1249. /*
  1250. ================
  1251. idStaticEntity::ShowEditingDialog
  1252. ================
  1253. */
  1254. void idStaticEntity::ShowEditingDialog( void ) {
  1255. common->InitTool( EDITOR_PARTICLE, &spawnArgs );
  1256. }
  1257. /*
  1258. ================
  1259. idStaticEntity::Think
  1260. ================
  1261. */
  1262. void idStaticEntity::Think( void ) {
  1263. idEntity::Think();
  1264. if ( thinkFlags & TH_THINK ) {
  1265. if ( runGui && renderEntity.gui[0] ) {
  1266. idPlayer *player = gameLocal.GetLocalPlayer();
  1267. if ( player ) {
  1268. if ( !player->objectiveSystemOpen ) {
  1269. renderEntity.gui[0]->StateChanged( gameLocal.time, true );
  1270. if ( renderEntity.gui[1] ) {
  1271. renderEntity.gui[1]->StateChanged( gameLocal.time, true );
  1272. }
  1273. if ( renderEntity.gui[2] ) {
  1274. renderEntity.gui[2]->StateChanged( gameLocal.time, true );
  1275. }
  1276. }
  1277. }
  1278. }
  1279. if ( fadeEnd > 0 ) {
  1280. idVec4 color;
  1281. if ( gameLocal.time < fadeEnd ) {
  1282. color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
  1283. } else {
  1284. color = fadeTo;
  1285. fadeEnd = 0;
  1286. BecomeInactive( TH_THINK );
  1287. }
  1288. SetColor( color );
  1289. }
  1290. }
  1291. }
  1292. /*
  1293. ================
  1294. idStaticEntity::Fade
  1295. ================
  1296. */
  1297. void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) {
  1298. GetColor( fadeFrom );
  1299. fadeTo = to;
  1300. fadeStart = gameLocal.time;
  1301. fadeEnd = gameLocal.time + SEC2MS( fadeTime );
  1302. BecomeActive( TH_THINK );
  1303. }
  1304. /*
  1305. ================
  1306. idStaticEntity::Hide
  1307. ================
  1308. */
  1309. void idStaticEntity::Hide( void ) {
  1310. idEntity::Hide();
  1311. GetPhysics()->SetContents( 0 );
  1312. }
  1313. /*
  1314. ================
  1315. idStaticEntity::Show
  1316. ================
  1317. */
  1318. void idStaticEntity::Show( void ) {
  1319. idEntity::Show();
  1320. if ( spawnArgs.GetBool( "solid" ) ) {
  1321. GetPhysics()->SetContents( CONTENTS_SOLID );
  1322. }
  1323. }
  1324. /*
  1325. ================
  1326. idStaticEntity::Event_Activate
  1327. ================
  1328. */
  1329. void idStaticEntity::Event_Activate( idEntity *activator ) {
  1330. idStr activateGui;
  1331. spawnTime = gameLocal.time;
  1332. active = !active;
  1333. const idKeyValue *kv = spawnArgs.FindKey( "hide" );
  1334. if ( kv ) {
  1335. if ( IsHidden() ) {
  1336. Show();
  1337. } else {
  1338. Hide();
  1339. }
  1340. }
  1341. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime );
  1342. renderEntity.shaderParms[5] = active;
  1343. // this change should be a good thing, it will automatically turn on
  1344. // lights etc.. when triggered so that does not have to be specifically done
  1345. // with trigger parms.. it MIGHT break things so need to keep an eye on it
  1346. renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ? 0.0f : 1.0f;
  1347. BecomeActive( TH_UPDATEVISUALS );
  1348. }
  1349. /*
  1350. ================
  1351. idStaticEntity::WriteToSnapshot
  1352. ================
  1353. */
  1354. void idStaticEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1355. GetPhysics()->WriteToSnapshot( msg );
  1356. WriteBindToSnapshot( msg );
  1357. WriteColorToSnapshot( msg );
  1358. WriteGUIToSnapshot( msg );
  1359. msg.WriteBits( IsHidden()?1:0, 1 );
  1360. }
  1361. /*
  1362. ================
  1363. idStaticEntity::ReadFromSnapshot
  1364. ================
  1365. */
  1366. void idStaticEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1367. bool hidden;
  1368. GetPhysics()->ReadFromSnapshot( msg );
  1369. ReadBindFromSnapshot( msg );
  1370. ReadColorFromSnapshot( msg );
  1371. ReadGUIFromSnapshot( msg );
  1372. hidden = msg.ReadBits( 1 ) == 1;
  1373. if ( hidden != IsHidden() ) {
  1374. if ( hidden ) {
  1375. Hide();
  1376. } else {
  1377. Show();
  1378. }
  1379. }
  1380. if ( msg.HasChanged() ) {
  1381. UpdateVisuals();
  1382. }
  1383. }
  1384. /*
  1385. ===============================================================================
  1386. idFuncEmitter
  1387. ===============================================================================
  1388. */
  1389. CLASS_DECLARATION( idStaticEntity, idFuncEmitter )
  1390. EVENT( EV_Activate, idFuncEmitter::Event_Activate )
  1391. END_CLASS
  1392. /*
  1393. ===============
  1394. idFuncEmitter::idFuncEmitter
  1395. ===============
  1396. */
  1397. idFuncEmitter::idFuncEmitter( void ) {
  1398. hidden = false;
  1399. }
  1400. /*
  1401. ===============
  1402. idFuncEmitter::Spawn
  1403. ===============
  1404. */
  1405. void idFuncEmitter::Spawn( void ) {
  1406. if ( spawnArgs.GetBool( "start_off" ) ) {
  1407. hidden = true;
  1408. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 );
  1409. UpdateVisuals();
  1410. } else {
  1411. hidden = false;
  1412. }
  1413. }
  1414. /*
  1415. ===============
  1416. idFuncEmitter::Save
  1417. ===============
  1418. */
  1419. void idFuncEmitter::Save( idSaveGame *savefile ) const {
  1420. savefile->WriteBool( hidden );
  1421. }
  1422. /*
  1423. ===============
  1424. idFuncEmitter::Restore
  1425. ===============
  1426. */
  1427. void idFuncEmitter::Restore( idRestoreGame *savefile ) {
  1428. savefile->ReadBool( hidden );
  1429. }
  1430. /*
  1431. ================
  1432. idFuncEmitter::Event_Activate
  1433. ================
  1434. */
  1435. void idFuncEmitter::Event_Activate( idEntity *activator ) {
  1436. if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) {
  1437. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0;
  1438. renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  1439. hidden = false;
  1440. } else {
  1441. renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time );
  1442. hidden = true;
  1443. }
  1444. UpdateVisuals();
  1445. }
  1446. /*
  1447. ================
  1448. idFuncEmitter::WriteToSnapshot
  1449. ================
  1450. */
  1451. void idFuncEmitter::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1452. msg.WriteBits( hidden ? 1 : 0, 1 );
  1453. msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
  1454. msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
  1455. }
  1456. /*
  1457. ================
  1458. idFuncEmitter::ReadFromSnapshot
  1459. ================
  1460. */
  1461. void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1462. hidden = msg.ReadBits( 1 ) != 0;
  1463. renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
  1464. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
  1465. if ( msg.HasChanged() ) {
  1466. UpdateVisuals();
  1467. }
  1468. }
  1469. /*
  1470. ===============================================================================
  1471. idFuncSplat
  1472. ===============================================================================
  1473. */
  1474. const idEventDef EV_Splat( "<Splat>" );
  1475. CLASS_DECLARATION( idFuncEmitter, idFuncSplat )
  1476. EVENT( EV_Activate, idFuncSplat::Event_Activate )
  1477. EVENT( EV_Splat, idFuncSplat::Event_Splat )
  1478. END_CLASS
  1479. /*
  1480. ===============
  1481. idFuncSplat::idFuncSplat
  1482. ===============
  1483. */
  1484. idFuncSplat::idFuncSplat( void ) {
  1485. }
  1486. /*
  1487. ===============
  1488. idFuncSplat::Spawn
  1489. ===============
  1490. */
  1491. void idFuncSplat::Spawn( void ) {
  1492. }
  1493. /*
  1494. ================
  1495. idFuncSplat::Event_Splat
  1496. ================
  1497. */
  1498. void idFuncSplat::Event_Splat( void ) {
  1499. const char *splat = NULL;
  1500. int count = spawnArgs.GetInt( "splatCount", "1" );
  1501. for ( int i = 0; i < count; i++ ) {
  1502. splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random );
  1503. if ( splat && *splat ) {
  1504. float size = spawnArgs.GetFloat( "splatSize", "128" );
  1505. float dist = spawnArgs.GetFloat( "splatDistance", "128" );
  1506. float angle = spawnArgs.GetFloat( "splatAngle", "0" );
  1507. gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle );
  1508. }
  1509. }
  1510. StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL );
  1511. }
  1512. /*
  1513. ================
  1514. idFuncSplat::Event_Activate
  1515. ================
  1516. */
  1517. void idFuncSplat::Event_Activate( idEntity *activator ) {
  1518. idFuncEmitter::Event_Activate( activator );
  1519. PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) );
  1520. StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL );
  1521. }
  1522. /*
  1523. ===============================================================================
  1524. idFuncSmoke
  1525. ===============================================================================
  1526. */
  1527. CLASS_DECLARATION( idEntity, idFuncSmoke )
  1528. EVENT( EV_Activate, idFuncSmoke::Event_Activate )
  1529. END_CLASS
  1530. /*
  1531. ===============
  1532. idFuncSmoke::idFuncSmoke
  1533. ===============
  1534. */
  1535. idFuncSmoke::idFuncSmoke() {
  1536. smokeTime = 0;
  1537. smoke = NULL;
  1538. restart = false;
  1539. }
  1540. /*
  1541. ===============
  1542. idFuncSmoke::Save
  1543. ===============
  1544. */
  1545. void idFuncSmoke::Save( idSaveGame *savefile ) const {
  1546. savefile->WriteInt( smokeTime );
  1547. savefile->WriteParticle( smoke );
  1548. savefile->WriteBool( restart );
  1549. }
  1550. /*
  1551. ===============
  1552. idFuncSmoke::Restore
  1553. ===============
  1554. */
  1555. void idFuncSmoke::Restore( idRestoreGame *savefile ) {
  1556. savefile->ReadInt( smokeTime );
  1557. savefile->ReadParticle( smoke );
  1558. savefile->ReadBool( restart );
  1559. }
  1560. /*
  1561. ===============
  1562. idFuncSmoke::Spawn
  1563. ===============
  1564. */
  1565. void idFuncSmoke::Spawn( void ) {
  1566. const char *smokeName = spawnArgs.GetString( "smoke" );
  1567. if ( *smokeName != '\0' ) {
  1568. smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  1569. } else {
  1570. smoke = NULL;
  1571. }
  1572. if ( spawnArgs.GetBool( "start_off" ) ) {
  1573. smokeTime = 0;
  1574. restart = false;
  1575. } else if ( smoke ) {
  1576. smokeTime = gameLocal.time;
  1577. BecomeActive( TH_UPDATEPARTICLES );
  1578. restart = true;
  1579. }
  1580. GetPhysics()->SetContents( 0 );
  1581. }
  1582. /*
  1583. ================
  1584. idFuncSmoke::Event_Activate
  1585. ================
  1586. */
  1587. void idFuncSmoke::Event_Activate( idEntity *activator ) {
  1588. if ( thinkFlags & TH_UPDATEPARTICLES ) {
  1589. restart = false;
  1590. return;
  1591. } else {
  1592. BecomeActive( TH_UPDATEPARTICLES );
  1593. restart = true;
  1594. smokeTime = gameLocal.time;
  1595. }
  1596. }
  1597. /*
  1598. ===============
  1599. idFuncSmoke::Think
  1600. ================
  1601. */
  1602. void idFuncSmoke::Think( void ) {
  1603. // if we are completely closed off from the player, don't do anything at all
  1604. if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) {
  1605. return;
  1606. }
  1607. if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
  1608. if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) {
  1609. if ( restart ) {
  1610. smokeTime = gameLocal.time;
  1611. } else {
  1612. smokeTime = 0;
  1613. BecomeInactive( TH_UPDATEPARTICLES );
  1614. }
  1615. }
  1616. }
  1617. }
  1618. /*
  1619. ===============================================================================
  1620. idTextEntity
  1621. ===============================================================================
  1622. */
  1623. CLASS_DECLARATION( idEntity, idTextEntity )
  1624. END_CLASS
  1625. /*
  1626. ================
  1627. idTextEntity::Spawn
  1628. ================
  1629. */
  1630. void idTextEntity::Spawn( void ) {
  1631. // these are cached as the are used each frame
  1632. text = spawnArgs.GetString( "text" );
  1633. playerOriented = spawnArgs.GetBool( "playerOriented" );
  1634. bool force = spawnArgs.GetBool( "force" );
  1635. if ( developer.GetBool() || force ) {
  1636. BecomeActive(TH_THINK);
  1637. }
  1638. }
  1639. /*
  1640. ================
  1641. idTextEntity::Save
  1642. ================
  1643. */
  1644. void idTextEntity::Save( idSaveGame *savefile ) const {
  1645. savefile->WriteString( text );
  1646. savefile->WriteBool( playerOriented );
  1647. }
  1648. /*
  1649. ================
  1650. idTextEntity::Restore
  1651. ================
  1652. */
  1653. void idTextEntity::Restore( idRestoreGame *savefile ) {
  1654. savefile->ReadString( text );
  1655. savefile->ReadBool( playerOriented );
  1656. }
  1657. /*
  1658. ================
  1659. idTextEntity::Think
  1660. ================
  1661. */
  1662. void idTextEntity::Think( void ) {
  1663. if ( thinkFlags & TH_THINK ) {
  1664. gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 );
  1665. for ( int i = 0; i < targets.Num(); i++ ) {
  1666. if ( targets[i].GetEntity() ) {
  1667. gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 );
  1668. }
  1669. }
  1670. } else {
  1671. BecomeInactive( TH_ALL );
  1672. }
  1673. }
  1674. /*
  1675. ===============================================================================
  1676. idVacuumSeperatorEntity
  1677. Can be triggered to let vacuum through a portal (blown out window)
  1678. ===============================================================================
  1679. */
  1680. CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity )
  1681. EVENT( EV_Activate, idVacuumSeparatorEntity::Event_Activate )
  1682. END_CLASS
  1683. /*
  1684. ================
  1685. idVacuumSeparatorEntity::idVacuumSeparatorEntity
  1686. ================
  1687. */
  1688. idVacuumSeparatorEntity::idVacuumSeparatorEntity( void ) {
  1689. portal = 0;
  1690. }
  1691. /*
  1692. ================
  1693. idVacuumSeparatorEntity::Save
  1694. ================
  1695. */
  1696. void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const {
  1697. savefile->WriteInt( (int)portal );
  1698. savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) );
  1699. }
  1700. /*
  1701. ================
  1702. idVacuumSeparatorEntity::Restore
  1703. ================
  1704. */
  1705. void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) {
  1706. int state;
  1707. savefile->ReadInt( (int &)portal );
  1708. savefile->ReadInt( state );
  1709. gameLocal.SetPortalState( portal, state );
  1710. }
  1711. /*
  1712. ================
  1713. idVacuumSeparatorEntity::Spawn
  1714. ================
  1715. */
  1716. void idVacuumSeparatorEntity::Spawn() {
  1717. idBounds b;
  1718. b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
  1719. portal = gameRenderWorld->FindPortal( b );
  1720. if ( !portal ) {
  1721. gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
  1722. return;
  1723. }
  1724. gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION );
  1725. }
  1726. /*
  1727. ================
  1728. idVacuumSeparatorEntity::Event_Activate
  1729. ================
  1730. */
  1731. void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) {
  1732. if ( !portal ) {
  1733. return;
  1734. }
  1735. gameLocal.SetPortalState( portal, PS_BLOCK_NONE );
  1736. }
  1737. /*
  1738. ===============================================================================
  1739. idLocationSeparatorEntity
  1740. ===============================================================================
  1741. */
  1742. CLASS_DECLARATION( idEntity, idLocationSeparatorEntity )
  1743. END_CLASS
  1744. /*
  1745. ================
  1746. idLocationSeparatorEntity::Spawn
  1747. ================
  1748. */
  1749. void idLocationSeparatorEntity::Spawn() {
  1750. idBounds b;
  1751. b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
  1752. qhandle_t portal = gameRenderWorld->FindPortal( b );
  1753. if ( !portal ) {
  1754. gameLocal.Warning( "LocationSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
  1755. }
  1756. gameLocal.SetPortalState( portal, PS_BLOCK_LOCATION );
  1757. }
  1758. /*
  1759. ===============================================================================
  1760. idVacuumEntity
  1761. Levels should only have a single vacuum entity.
  1762. ===============================================================================
  1763. */
  1764. CLASS_DECLARATION( idEntity, idVacuumEntity )
  1765. END_CLASS
  1766. /*
  1767. ================
  1768. idVacuumEntity::Spawn
  1769. ================
  1770. */
  1771. void idVacuumEntity::Spawn() {
  1772. if ( gameLocal.vacuumAreaNum != -1 ) {
  1773. gameLocal.Warning( "idVacuumEntity::Spawn: multiple idVacuumEntity in level" );
  1774. return;
  1775. }
  1776. idVec3 org = spawnArgs.GetVector( "origin" );
  1777. gameLocal.vacuumAreaNum = gameRenderWorld->PointInArea( org );
  1778. }
  1779. /*
  1780. ===============================================================================
  1781. idLocationEntity
  1782. ===============================================================================
  1783. */
  1784. CLASS_DECLARATION( idEntity, idLocationEntity )
  1785. END_CLASS
  1786. /*
  1787. ======================
  1788. idLocationEntity::Spawn
  1789. ======================
  1790. */
  1791. void idLocationEntity::Spawn() {
  1792. idStr realName;
  1793. // this just holds dict information
  1794. // if "location" not already set, use the entity name.
  1795. if ( !spawnArgs.GetString( "location", "", realName ) ) {
  1796. spawnArgs.Set( "location", name );
  1797. }
  1798. }
  1799. /*
  1800. ======================
  1801. idLocationEntity::GetLocation
  1802. ======================
  1803. */
  1804. const char *idLocationEntity::GetLocation( void ) const {
  1805. return spawnArgs.GetString( "location" );
  1806. }
  1807. /*
  1808. ===============================================================================
  1809. idBeam
  1810. ===============================================================================
  1811. */
  1812. CLASS_DECLARATION( idEntity, idBeam )
  1813. EVENT( EV_PostSpawn, idBeam::Event_MatchTarget )
  1814. EVENT( EV_Activate, idBeam::Event_Activate )
  1815. END_CLASS
  1816. /*
  1817. ===============
  1818. idBeam::idBeam
  1819. ===============
  1820. */
  1821. idBeam::idBeam() {
  1822. target = NULL;
  1823. master = NULL;
  1824. }
  1825. /*
  1826. ===============
  1827. idBeam::Save
  1828. ===============
  1829. */
  1830. void idBeam::Save( idSaveGame *savefile ) const {
  1831. target.Save( savefile );
  1832. master.Save( savefile );
  1833. }
  1834. /*
  1835. ===============
  1836. idBeam::Restore
  1837. ===============
  1838. */
  1839. void idBeam::Restore( idRestoreGame *savefile ) {
  1840. target.Restore( savefile );
  1841. master.Restore( savefile );
  1842. }
  1843. /*
  1844. ===============
  1845. idBeam::Spawn
  1846. ===============
  1847. */
  1848. void idBeam::Spawn( void ) {
  1849. float width;
  1850. if ( spawnArgs.GetFloat( "width", "0", width ) ) {
  1851. renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = width;
  1852. }
  1853. SetModel( "_BEAM" );
  1854. Hide();
  1855. PostEventMS( &EV_PostSpawn, 0 );
  1856. }
  1857. /*
  1858. ================
  1859. idBeam::Think
  1860. ================
  1861. */
  1862. void idBeam::Think( void ) {
  1863. idBeam *masterEnt;
  1864. if ( !IsHidden() && !target.GetEntity() ) {
  1865. // hide if our target is removed
  1866. Hide();
  1867. }
  1868. RunPhysics();
  1869. masterEnt = master.GetEntity();
  1870. if ( masterEnt ) {
  1871. const idVec3 &origin = GetPhysics()->GetOrigin();
  1872. masterEnt->SetBeamTarget( origin );
  1873. }
  1874. Present();
  1875. }
  1876. /*
  1877. ================
  1878. idBeam::SetMaster
  1879. ================
  1880. */
  1881. void idBeam::SetMaster( idBeam *masterbeam ) {
  1882. master = masterbeam;
  1883. }
  1884. /*
  1885. ================
  1886. idBeam::SetBeamTarget
  1887. ================
  1888. */
  1889. void idBeam::SetBeamTarget( const idVec3 &origin ) {
  1890. 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 ) ) {
  1891. renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = origin.x;
  1892. renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = origin.y;
  1893. renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = origin.z;
  1894. UpdateVisuals();
  1895. }
  1896. }
  1897. /*
  1898. ================
  1899. idBeam::Show
  1900. ================
  1901. */
  1902. void idBeam::Show( void ) {
  1903. idBeam *targetEnt;
  1904. idEntity::Show();
  1905. targetEnt = target.GetEntity();
  1906. if ( targetEnt ) {
  1907. const idVec3 &origin = targetEnt->GetPhysics()->GetOrigin();
  1908. SetBeamTarget( origin );
  1909. }
  1910. }
  1911. /*
  1912. ================
  1913. idBeam::Event_MatchTarget
  1914. ================
  1915. */
  1916. void idBeam::Event_MatchTarget( void ) {
  1917. int i;
  1918. idEntity *targetEnt;
  1919. idBeam *targetBeam;
  1920. if ( !targets.Num() ) {
  1921. return;
  1922. }
  1923. targetBeam = NULL;
  1924. for( i = 0; i < targets.Num(); i++ ) {
  1925. targetEnt = targets[ i ].GetEntity();
  1926. if ( targetEnt && targetEnt->IsType( idBeam::Type ) ) {
  1927. targetBeam = static_cast<idBeam *>( targetEnt );
  1928. break;
  1929. }
  1930. }
  1931. if ( !targetBeam ) {
  1932. gameLocal.Error( "Could not find valid beam target for '%s'", name.c_str() );
  1933. }
  1934. target = targetBeam;
  1935. targetBeam->SetMaster( this );
  1936. if ( !spawnArgs.GetBool( "start_off" ) ) {
  1937. Show();
  1938. }
  1939. }
  1940. /*
  1941. ================
  1942. idBeam::Event_Activate
  1943. ================
  1944. */
  1945. void idBeam::Event_Activate( idEntity *activator ) {
  1946. if ( IsHidden() ) {
  1947. Show();
  1948. } else {
  1949. Hide();
  1950. }
  1951. }
  1952. /*
  1953. ================
  1954. idBeam::WriteToSnapshot
  1955. ================
  1956. */
  1957. void idBeam::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1958. GetPhysics()->WriteToSnapshot( msg );
  1959. WriteBindToSnapshot( msg );
  1960. WriteColorToSnapshot( msg );
  1961. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
  1962. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] );
  1963. msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] );
  1964. }
  1965. /*
  1966. ================
  1967. idBeam::ReadFromSnapshot
  1968. ================
  1969. */
  1970. void idBeam::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1971. GetPhysics()->ReadFromSnapshot( msg );
  1972. ReadBindFromSnapshot( msg );
  1973. ReadColorFromSnapshot( msg );
  1974. renderEntity.shaderParms[SHADERPARM_BEAM_END_X] = msg.ReadFloat();
  1975. renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] = msg.ReadFloat();
  1976. renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] = msg.ReadFloat();
  1977. if ( msg.HasChanged() ) {
  1978. UpdateVisuals();
  1979. }
  1980. }
  1981. /*
  1982. ===============================================================================
  1983. idLiquid
  1984. ===============================================================================
  1985. */
  1986. CLASS_DECLARATION( idEntity, idLiquid )
  1987. EVENT( EV_Touch, idLiquid::Event_Touch )
  1988. END_CLASS
  1989. /*
  1990. ================
  1991. idLiquid::Save
  1992. ================
  1993. */
  1994. void idLiquid::Save( idSaveGame *savefile ) const {
  1995. // Nothing to save
  1996. }
  1997. /*
  1998. ================
  1999. idLiquid::Restore
  2000. ================
  2001. */
  2002. void idLiquid::Restore( idRestoreGame *savefile ) {
  2003. //FIXME: NO!
  2004. Spawn();
  2005. }
  2006. /*
  2007. ================
  2008. idLiquid::Spawn
  2009. ================
  2010. */
  2011. void idLiquid::Spawn() {
  2012. /*
  2013. model = dynamic_cast<idRenderModelLiquid *>( renderEntity.hModel );
  2014. if ( !model ) {
  2015. gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() );
  2016. }
  2017. model->Reset();
  2018. GetPhysics()->SetContents( CONTENTS_TRIGGER );
  2019. */
  2020. }
  2021. /*
  2022. ================
  2023. idLiquid::Event_Touch
  2024. ================
  2025. */
  2026. void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) {
  2027. // FIXME: for QuakeCon
  2028. /*
  2029. idVec3 pos;
  2030. pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  2031. model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f );
  2032. */
  2033. }
  2034. /*
  2035. ===============================================================================
  2036. idShaking
  2037. ===============================================================================
  2038. */
  2039. CLASS_DECLARATION( idEntity, idShaking )
  2040. EVENT( EV_Activate, idShaking::Event_Activate )
  2041. END_CLASS
  2042. /*
  2043. ===============
  2044. idShaking::idShaking
  2045. ===============
  2046. */
  2047. idShaking::idShaking() {
  2048. active = false;
  2049. }
  2050. /*
  2051. ===============
  2052. idShaking::Save
  2053. ===============
  2054. */
  2055. void idShaking::Save( idSaveGame *savefile ) const {
  2056. savefile->WriteBool( active );
  2057. savefile->WriteStaticObject( physicsObj );
  2058. }
  2059. /*
  2060. ===============
  2061. idShaking::Restore
  2062. ===============
  2063. */
  2064. void idShaking::Restore( idRestoreGame *savefile ) {
  2065. savefile->ReadBool( active );
  2066. savefile->ReadStaticObject( physicsObj );
  2067. RestorePhysics( &physicsObj );
  2068. }
  2069. /*
  2070. ===============
  2071. idShaking::Spawn
  2072. ===============
  2073. */
  2074. void idShaking::Spawn( void ) {
  2075. physicsObj.SetSelf( this );
  2076. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  2077. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  2078. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  2079. physicsObj.SetClipMask( MASK_SOLID );
  2080. SetPhysics( &physicsObj );
  2081. active = false;
  2082. if ( !spawnArgs.GetBool( "start_off" ) ) {
  2083. BeginShaking();
  2084. }
  2085. }
  2086. /*
  2087. ================
  2088. idShaking::BeginShaking
  2089. ================
  2090. */
  2091. void idShaking::BeginShaking( void ) {
  2092. int phase;
  2093. idAngles shake;
  2094. int period;
  2095. active = true;
  2096. phase = gameLocal.random.RandomInt( 1000 );
  2097. shake = spawnArgs.GetAngles( "shake", "0.5 0.5 0.5" );
  2098. period = spawnArgs.GetFloat( "period", "0.05" ) * 1000;
  2099. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase, period * 0.25f, GetPhysics()->GetAxis().ToAngles(), shake, ang_zero );
  2100. }
  2101. /*
  2102. ================
  2103. idShaking::Event_Activate
  2104. ================
  2105. */
  2106. void idShaking::Event_Activate( idEntity *activator ) {
  2107. if ( !active ) {
  2108. BeginShaking();
  2109. } else {
  2110. active = false;
  2111. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, physicsObj.GetAxis().ToAngles(), ang_zero, ang_zero );
  2112. }
  2113. }
  2114. /*
  2115. ===============================================================================
  2116. idEarthQuake
  2117. ===============================================================================
  2118. */
  2119. CLASS_DECLARATION( idEntity, idEarthQuake )
  2120. EVENT( EV_Activate, idEarthQuake::Event_Activate )
  2121. END_CLASS
  2122. /*
  2123. ===============
  2124. idEarthQuake::idEarthQuake
  2125. ===============
  2126. */
  2127. idEarthQuake::idEarthQuake() {
  2128. wait = 0.0f;
  2129. random = 0.0f;
  2130. nextTriggerTime = 0;
  2131. shakeStopTime = 0;
  2132. triggered = false;
  2133. playerOriented = false;
  2134. disabled = false;
  2135. shakeTime = 0.0f;
  2136. }
  2137. /*
  2138. ===============
  2139. idEarthQuake::Save
  2140. ===============
  2141. */
  2142. void idEarthQuake::Save( idSaveGame *savefile ) const {
  2143. savefile->WriteInt( nextTriggerTime );
  2144. savefile->WriteInt( shakeStopTime );
  2145. savefile->WriteFloat( wait );
  2146. savefile->WriteFloat( random );
  2147. savefile->WriteBool( triggered );
  2148. savefile->WriteBool( playerOriented );
  2149. savefile->WriteBool( disabled );
  2150. savefile->WriteFloat( shakeTime );
  2151. }
  2152. /*
  2153. ===============
  2154. idEarthQuake::Restore
  2155. ===============
  2156. */
  2157. void idEarthQuake::Restore( idRestoreGame *savefile ) {
  2158. savefile->ReadInt( nextTriggerTime );
  2159. savefile->ReadInt( shakeStopTime );
  2160. savefile->ReadFloat( wait );
  2161. savefile->ReadFloat( random );
  2162. savefile->ReadBool( triggered );
  2163. savefile->ReadBool( playerOriented );
  2164. savefile->ReadBool( disabled );
  2165. savefile->ReadFloat( shakeTime );
  2166. if ( shakeStopTime > gameLocal.time ) {
  2167. BecomeActive( TH_THINK );
  2168. }
  2169. }
  2170. /*
  2171. ===============
  2172. idEarthQuake::Spawn
  2173. ===============
  2174. */
  2175. void idEarthQuake::Spawn( void ) {
  2176. nextTriggerTime = 0;
  2177. shakeStopTime = 0;
  2178. wait = spawnArgs.GetFloat( "wait", "15" );
  2179. random = spawnArgs.GetFloat( "random", "5" );
  2180. triggered = spawnArgs.GetBool( "triggered" );
  2181. playerOriented = spawnArgs.GetBool( "playerOriented" );
  2182. disabled = false;
  2183. shakeTime = spawnArgs.GetFloat( "shakeTime", "0" );
  2184. if ( !triggered ){
  2185. PostEventSec( &EV_Activate, spawnArgs.GetFloat( "wait" ), this );
  2186. }
  2187. BecomeInactive( TH_THINK );
  2188. }
  2189. /*
  2190. ================
  2191. idEarthQuake::Event_Activate
  2192. ================
  2193. */
  2194. void idEarthQuake::Event_Activate( idEntity *activator ) {
  2195. if ( nextTriggerTime > gameLocal.time ) {
  2196. return;
  2197. }
  2198. if ( disabled && activator == this ) {
  2199. return;
  2200. }
  2201. idPlayer *player = gameLocal.GetLocalPlayer();
  2202. if ( player == NULL ) {
  2203. return;
  2204. }
  2205. nextTriggerTime = 0;
  2206. if ( !triggered && activator != this ){
  2207. // if we are not triggered ( i.e. random ), disable or enable
  2208. disabled ^= 1;
  2209. if (disabled) {
  2210. return;
  2211. } else {
  2212. PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
  2213. }
  2214. }
  2215. ActivateTargets( activator );
  2216. const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_quake" ) );
  2217. if ( playerOriented ) {
  2218. player->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  2219. } else {
  2220. StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  2221. }
  2222. if ( shakeTime > 0.0f ) {
  2223. shakeStopTime = gameLocal.time + SEC2MS( shakeTime );
  2224. BecomeActive( TH_THINK );
  2225. }
  2226. if ( wait > 0.0f ) {
  2227. if ( !triggered ) {
  2228. PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this );
  2229. } else {
  2230. nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
  2231. }
  2232. } else if ( shakeTime == 0.0f ) {
  2233. PostEventMS( &EV_Remove, 0 );
  2234. }
  2235. }
  2236. /*
  2237. ===============
  2238. idEarthQuake::Think
  2239. ================
  2240. */
  2241. void idEarthQuake::Think( void ) {
  2242. if ( thinkFlags & TH_THINK ) {
  2243. if ( gameLocal.time > shakeStopTime ) {
  2244. BecomeInactive( TH_THINK );
  2245. if ( wait <= 0.0f ) {
  2246. PostEventMS( &EV_Remove, 0 );
  2247. }
  2248. return;
  2249. }
  2250. float shakeVolume = gameSoundWorld->CurrentShakeAmplitudeForPosition( gameLocal.time, gameLocal.GetLocalPlayer()->firstPersonViewOrigin );
  2251. gameLocal.RadiusPush( GetPhysics()->GetOrigin(), 256, 1500 * shakeVolume, this, this, 1.0f, true );
  2252. }
  2253. BecomeInactive( TH_UPDATEVISUALS );
  2254. }
  2255. /*
  2256. ===============================================================================
  2257. idFuncPortal
  2258. ===============================================================================
  2259. */
  2260. CLASS_DECLARATION( idEntity, idFuncPortal )
  2261. EVENT( EV_Activate, idFuncPortal::Event_Activate )
  2262. END_CLASS
  2263. /*
  2264. ===============
  2265. idFuncPortal::idFuncPortal
  2266. ===============
  2267. */
  2268. idFuncPortal::idFuncPortal() {
  2269. portal = 0;
  2270. state = false;
  2271. }
  2272. /*
  2273. ===============
  2274. idFuncPortal::Save
  2275. ===============
  2276. */
  2277. void idFuncPortal::Save( idSaveGame *savefile ) const {
  2278. savefile->WriteInt( (int)portal );
  2279. savefile->WriteBool( state );
  2280. }
  2281. /*
  2282. ===============
  2283. idFuncPortal::Restore
  2284. ===============
  2285. */
  2286. void idFuncPortal::Restore( idRestoreGame *savefile ) {
  2287. savefile->ReadInt( (int &)portal );
  2288. savefile->ReadBool( state );
  2289. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2290. }
  2291. /*
  2292. ===============
  2293. idFuncPortal::Spawn
  2294. ===============
  2295. */
  2296. void idFuncPortal::Spawn( void ) {
  2297. portal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds().Expand( 32.0f ) );
  2298. if ( portal > 0 ) {
  2299. state = spawnArgs.GetBool( "start_on" );
  2300. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2301. }
  2302. }
  2303. /*
  2304. ================
  2305. idFuncPortal::Event_Activate
  2306. ================
  2307. */
  2308. void idFuncPortal::Event_Activate( idEntity *activator ) {
  2309. if ( portal > 0 ) {
  2310. state = !state;
  2311. gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE );
  2312. }
  2313. }
  2314. /*
  2315. ===============================================================================
  2316. idFuncAASPortal
  2317. ===============================================================================
  2318. */
  2319. CLASS_DECLARATION( idEntity, idFuncAASPortal )
  2320. EVENT( EV_Activate, idFuncAASPortal::Event_Activate )
  2321. END_CLASS
  2322. /*
  2323. ===============
  2324. idFuncAASPortal::idFuncAASPortal
  2325. ===============
  2326. */
  2327. idFuncAASPortal::idFuncAASPortal() {
  2328. state = false;
  2329. }
  2330. /*
  2331. ===============
  2332. idFuncAASPortal::Save
  2333. ===============
  2334. */
  2335. void idFuncAASPortal::Save( idSaveGame *savefile ) const {
  2336. savefile->WriteBool( state );
  2337. }
  2338. /*
  2339. ===============
  2340. idFuncAASPortal::Restore
  2341. ===============
  2342. */
  2343. void idFuncAASPortal::Restore( idRestoreGame *savefile ) {
  2344. savefile->ReadBool( state );
  2345. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2346. }
  2347. /*
  2348. ===============
  2349. idFuncAASPortal::Spawn
  2350. ===============
  2351. */
  2352. void idFuncAASPortal::Spawn( void ) {
  2353. state = spawnArgs.GetBool( "start_on" );
  2354. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2355. }
  2356. /*
  2357. ================
  2358. idFuncAASPortal::Event_Activate
  2359. ================
  2360. */
  2361. void idFuncAASPortal::Event_Activate( idEntity *activator ) {
  2362. state ^= 1;
  2363. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state );
  2364. }
  2365. /*
  2366. ===============================================================================
  2367. idFuncAASObstacle
  2368. ===============================================================================
  2369. */
  2370. CLASS_DECLARATION( idEntity, idFuncAASObstacle )
  2371. EVENT( EV_Activate, idFuncAASObstacle::Event_Activate )
  2372. END_CLASS
  2373. /*
  2374. ===============
  2375. idFuncAASObstacle::idFuncAASObstacle
  2376. ===============
  2377. */
  2378. idFuncAASObstacle::idFuncAASObstacle() {
  2379. state = false;
  2380. }
  2381. /*
  2382. ===============
  2383. idFuncAASObstacle::Save
  2384. ===============
  2385. */
  2386. void idFuncAASObstacle::Save( idSaveGame *savefile ) const {
  2387. savefile->WriteBool( state );
  2388. }
  2389. /*
  2390. ===============
  2391. idFuncAASObstacle::Restore
  2392. ===============
  2393. */
  2394. void idFuncAASObstacle::Restore( idRestoreGame *savefile ) {
  2395. savefile->ReadBool( state );
  2396. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2397. }
  2398. /*
  2399. ===============
  2400. idFuncAASObstacle::Spawn
  2401. ===============
  2402. */
  2403. void idFuncAASObstacle::Spawn( void ) {
  2404. state = spawnArgs.GetBool( "start_on" );
  2405. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2406. }
  2407. /*
  2408. ================
  2409. idFuncAASObstacle::Event_Activate
  2410. ================
  2411. */
  2412. void idFuncAASObstacle::Event_Activate( idEntity *activator ) {
  2413. state ^= 1;
  2414. gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state );
  2415. }
  2416. /*
  2417. ===============================================================================
  2418. idFuncRadioChatter
  2419. ===============================================================================
  2420. */
  2421. const idEventDef EV_ResetRadioHud( "<resetradiohud>", "e" );
  2422. CLASS_DECLARATION( idEntity, idFuncRadioChatter )
  2423. EVENT( EV_Activate, idFuncRadioChatter::Event_Activate )
  2424. EVENT( EV_ResetRadioHud, idFuncRadioChatter::Event_ResetRadioHud )
  2425. END_CLASS
  2426. /*
  2427. ===============
  2428. idFuncRadioChatter::idFuncRadioChatter
  2429. ===============
  2430. */
  2431. idFuncRadioChatter::idFuncRadioChatter() {
  2432. time = 0.0;
  2433. }
  2434. /*
  2435. ===============
  2436. idFuncRadioChatter::Save
  2437. ===============
  2438. */
  2439. void idFuncRadioChatter::Save( idSaveGame *savefile ) const {
  2440. savefile->WriteFloat( time );
  2441. }
  2442. /*
  2443. ===============
  2444. idFuncRadioChatter::Restore
  2445. ===============
  2446. */
  2447. void idFuncRadioChatter::Restore( idRestoreGame *savefile ) {
  2448. savefile->ReadFloat( time );
  2449. }
  2450. /*
  2451. ===============
  2452. idFuncRadioChatter::Spawn
  2453. ===============
  2454. */
  2455. void idFuncRadioChatter::Spawn( void ) {
  2456. time = spawnArgs.GetFloat( "time", "5.0" );
  2457. }
  2458. /*
  2459. ================
  2460. idFuncRadioChatter::Event_Activate
  2461. ================
  2462. */
  2463. void idFuncRadioChatter::Event_Activate( idEntity *activator ) {
  2464. idPlayer *player;
  2465. const char *sound;
  2466. const idSoundShader *shader;
  2467. int length;
  2468. if ( activator->IsType( idPlayer::Type ) ) {
  2469. player = static_cast<idPlayer *>( activator );
  2470. } else {
  2471. player = gameLocal.GetLocalPlayer();
  2472. }
  2473. player->hud->HandleNamedEvent( "radioChatterUp" );
  2474. sound = spawnArgs.GetString( "snd_radiochatter", "" );
  2475. if ( sound && *sound ) {
  2476. shader = declManager->FindSound( sound );
  2477. player->StartSoundShader( shader, SND_CHANNEL_RADIO, SSF_GLOBAL, false, &length );
  2478. time = MS2SEC( length + 150 );
  2479. }
  2480. // we still put the hud up because this is used with no sound on
  2481. // certain frame commands when the chatter is triggered
  2482. PostEventSec( &EV_ResetRadioHud, time, player );
  2483. }
  2484. /*
  2485. ================
  2486. idFuncRadioChatter::Event_ResetRadioHud
  2487. ================
  2488. */
  2489. void idFuncRadioChatter::Event_ResetRadioHud( idEntity *activator ) {
  2490. idPlayer *player = ( activator->IsType( idPlayer::Type ) ) ? static_cast<idPlayer *>( activator ) : gameLocal.GetLocalPlayer();
  2491. player->hud->HandleNamedEvent( "radioChatterDown" );
  2492. ActivateTargets( activator );
  2493. }
  2494. /*
  2495. ===============================================================================
  2496. idPhantomObjects
  2497. ===============================================================================
  2498. */
  2499. CLASS_DECLARATION( idEntity, idPhantomObjects )
  2500. EVENT( EV_Activate, idPhantomObjects::Event_Activate )
  2501. END_CLASS
  2502. /*
  2503. ===============
  2504. idPhantomObjects::idPhantomObjects
  2505. ===============
  2506. */
  2507. idPhantomObjects::idPhantomObjects() {
  2508. target = NULL;
  2509. end_time = 0;
  2510. throw_time = 0.0f;
  2511. shake_time = 0.0f;
  2512. shake_ang.Zero();
  2513. speed = 0.0f;
  2514. min_wait = 0;
  2515. max_wait = 0;
  2516. fl.neverDormant = false;
  2517. }
  2518. /*
  2519. ===============
  2520. idPhantomObjects::Save
  2521. ===============
  2522. */
  2523. void idPhantomObjects::Save( idSaveGame *savefile ) const {
  2524. int i;
  2525. savefile->WriteInt( end_time );
  2526. savefile->WriteFloat( throw_time );
  2527. savefile->WriteFloat( shake_time );
  2528. savefile->WriteVec3( shake_ang );
  2529. savefile->WriteFloat( speed );
  2530. savefile->WriteInt( min_wait );
  2531. savefile->WriteInt( max_wait );
  2532. target.Save( savefile );
  2533. savefile->WriteInt( targetTime.Num() );
  2534. for( i = 0; i < targetTime.Num(); i++ ) {
  2535. savefile->WriteInt( targetTime[ i ] );
  2536. }
  2537. for( i = 0; i < lastTargetPos.Num(); i++ ) {
  2538. savefile->WriteVec3( lastTargetPos[ i ] );
  2539. }
  2540. }
  2541. /*
  2542. ===============
  2543. idPhantomObjects::Restore
  2544. ===============
  2545. */
  2546. void idPhantomObjects::Restore( idRestoreGame *savefile ) {
  2547. int num;
  2548. int i;
  2549. savefile->ReadInt( end_time );
  2550. savefile->ReadFloat( throw_time );
  2551. savefile->ReadFloat( shake_time );
  2552. savefile->ReadVec3( shake_ang );
  2553. savefile->ReadFloat( speed );
  2554. savefile->ReadInt( min_wait );
  2555. savefile->ReadInt( max_wait );
  2556. target.Restore( savefile );
  2557. savefile->ReadInt( num );
  2558. targetTime.SetGranularity( 1 );
  2559. targetTime.SetNum( num );
  2560. lastTargetPos.SetGranularity( 1 );
  2561. lastTargetPos.SetNum( num );
  2562. for( i = 0; i < num; i++ ) {
  2563. savefile->ReadInt( targetTime[ i ] );
  2564. }
  2565. for( i = 0; i < num; i++ ) {
  2566. savefile->ReadVec3( lastTargetPos[ i ] );
  2567. }
  2568. }
  2569. /*
  2570. ===============
  2571. idPhantomObjects::Spawn
  2572. ===============
  2573. */
  2574. void idPhantomObjects::Spawn( void ) {
  2575. throw_time = spawnArgs.GetFloat( "time", "5" );
  2576. speed = spawnArgs.GetFloat( "speed", "1200" );
  2577. shake_time = spawnArgs.GetFloat( "shake_time", "1" );
  2578. throw_time -= shake_time;
  2579. if ( throw_time < 0.0f ) {
  2580. throw_time = 0.0f;
  2581. }
  2582. min_wait = SEC2MS( spawnArgs.GetFloat( "min_wait", "1" ) );
  2583. max_wait = SEC2MS( spawnArgs.GetFloat( "max_wait", "3" ) );
  2584. shake_ang = spawnArgs.GetVector( "shake_ang", "65 65 65" );
  2585. Hide();
  2586. GetPhysics()->SetContents( 0 );
  2587. }
  2588. /*
  2589. ================
  2590. idPhantomObjects::Event_Activate
  2591. ================
  2592. */
  2593. void idPhantomObjects::Event_Activate( idEntity *activator ) {
  2594. int i;
  2595. float time;
  2596. float frac;
  2597. float scale;
  2598. if ( thinkFlags & TH_THINK ) {
  2599. BecomeInactive( TH_THINK );
  2600. return;
  2601. }
  2602. RemoveNullTargets();
  2603. if ( !targets.Num() ) {
  2604. return;
  2605. }
  2606. if ( !activator || !activator->IsType( idActor::Type ) ) {
  2607. target = gameLocal.GetLocalPlayer();
  2608. } else {
  2609. target = static_cast<idActor *>( activator );
  2610. }
  2611. end_time = gameLocal.time + SEC2MS( spawnArgs.GetFloat( "end_time", "0" ) );
  2612. targetTime.SetNum( targets.Num() );
  2613. lastTargetPos.SetNum( targets.Num() );
  2614. const idVec3 &toPos = target.GetEntity()->GetEyePosition();
  2615. // calculate the relative times of all the objects
  2616. time = 0.0f;
  2617. for( i = 0; i < targetTime.Num(); i++ ) {
  2618. targetTime[ i ] = SEC2MS( time );
  2619. lastTargetPos[ i ] = toPos;
  2620. frac = 1.0f - ( float )i / ( float )targetTime.Num();
  2621. time += ( gameLocal.random.RandomFloat() + 1.0f ) * 0.5f * frac + 0.1f;
  2622. }
  2623. // scale up the times to fit within throw_time
  2624. scale = throw_time / time;
  2625. for( i = 0; i < targetTime.Num(); i++ ) {
  2626. targetTime[ i ] = gameLocal.time + SEC2MS( shake_time )+ targetTime[ i ] * scale;
  2627. }
  2628. BecomeActive( TH_THINK );
  2629. }
  2630. /*
  2631. ===============
  2632. idPhantomObjects::Think
  2633. ================
  2634. */
  2635. void idPhantomObjects::Think( void ) {
  2636. int i;
  2637. int num;
  2638. float time;
  2639. idVec3 vel;
  2640. idVec3 ang;
  2641. idEntity *ent;
  2642. idActor *targetEnt;
  2643. idPhysics *entPhys;
  2644. trace_t tr;
  2645. // if we are completely closed off from the player, don't do anything at all
  2646. if ( CheckDormant() ) {
  2647. return;
  2648. }
  2649. if ( !( thinkFlags & TH_THINK ) ) {
  2650. BecomeInactive( thinkFlags & ~TH_THINK );
  2651. return;
  2652. }
  2653. targetEnt = target.GetEntity();
  2654. if ( !targetEnt || ( targetEnt->health <= 0 ) || ( end_time && ( gameLocal.time > end_time ) ) || gameLocal.inCinematic ) {
  2655. BecomeInactive( TH_THINK );
  2656. }
  2657. const idVec3 &toPos = targetEnt->GetEyePosition();
  2658. num = 0;
  2659. for ( i = 0; i < targets.Num(); i++ ) {
  2660. ent = targets[ i ].GetEntity();
  2661. if ( !ent ) {
  2662. continue;
  2663. }
  2664. if ( ent->fl.hidden ) {
  2665. // don't throw hidden objects
  2666. continue;
  2667. }
  2668. if ( !targetTime[ i ] ) {
  2669. // already threw this object
  2670. continue;
  2671. }
  2672. num++;
  2673. time = MS2SEC( targetTime[ i ] - gameLocal.time );
  2674. if ( time > shake_time ) {
  2675. continue;
  2676. }
  2677. entPhys = ent->GetPhysics();
  2678. const idVec3 &entOrg = entPhys->GetOrigin();
  2679. gameLocal.clip.TracePoint( tr, entOrg, toPos, MASK_OPAQUE, ent );
  2680. if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == targetEnt ) ) {
  2681. lastTargetPos[ i ] = toPos;
  2682. }
  2683. if ( time < 0.0f ) {
  2684. idAI::PredictTrajectory( entPhys->GetOrigin(), lastTargetPos[ i ], speed, entPhys->GetGravity(),
  2685. entPhys->GetClipModel(), entPhys->GetClipMask(), 256.0f, ent, targetEnt, ai_debugTrajectory.GetBool() ? 1 : 0, vel );
  2686. vel *= speed;
  2687. entPhys->SetLinearVelocity( vel );
  2688. if ( !end_time ) {
  2689. targetTime[ i ] = 0;
  2690. } else {
  2691. targetTime[ i ] = gameLocal.time + gameLocal.random.RandomInt( max_wait - min_wait ) + min_wait;
  2692. }
  2693. if ( ent->IsType( idMoveable::Type ) ) {
  2694. idMoveable *ment = static_cast<idMoveable*>( ent );
  2695. ment->EnableDamage( true, 2.5f );
  2696. }
  2697. } else {
  2698. // this is not the right way to set the angular velocity, but the effect is nice, so I'm keeping it. :)
  2699. ang.Set( gameLocal.random.CRandomFloat() * shake_ang.x, gameLocal.random.CRandomFloat() * shake_ang.y, gameLocal.random.CRandomFloat() * shake_ang.z );
  2700. ang *= ( 1.0f - time / shake_time );
  2701. entPhys->SetAngularVelocity( ang );
  2702. }
  2703. }
  2704. if ( !num ) {
  2705. BecomeInactive( TH_THINK );
  2706. }
  2707. }
  2708. #ifdef _D3XP
  2709. /*
  2710. ===============================================================================
  2711. idShockwave
  2712. ===============================================================================
  2713. */
  2714. CLASS_DECLARATION( idEntity, idShockwave )
  2715. EVENT( EV_Activate, idShockwave::Event_Activate )
  2716. END_CLASS
  2717. /*
  2718. ===============
  2719. idShockwave::idShockwave
  2720. ===============
  2721. */
  2722. idShockwave::idShockwave() {
  2723. isActive = false;
  2724. startTime = 0;
  2725. duration = 0;
  2726. startSize = 0.f;
  2727. endSize = 0.f;
  2728. currentSize = 0.f;
  2729. magnitude = 0.f;
  2730. height = 0.0f;
  2731. playerDamaged = false;
  2732. playerDamageSize = 0.0f;
  2733. }
  2734. /*
  2735. ===============
  2736. idShockwave::~idShockwave
  2737. ===============
  2738. */
  2739. idShockwave::~idShockwave() {
  2740. }
  2741. /*
  2742. ===============
  2743. idShockwave::Save
  2744. ===============
  2745. */
  2746. void idShockwave::Save( idSaveGame *savefile ) const {
  2747. savefile->WriteBool( isActive );
  2748. savefile->WriteInt( startTime );
  2749. savefile->WriteInt( duration );
  2750. savefile->WriteFloat( startSize );
  2751. savefile->WriteFloat( endSize );
  2752. savefile->WriteFloat( currentSize );
  2753. savefile->WriteFloat( magnitude );
  2754. savefile->WriteFloat( height );
  2755. savefile->WriteBool( playerDamaged );
  2756. savefile->WriteFloat( playerDamageSize );
  2757. }
  2758. /*
  2759. ===============
  2760. idShockwave::Restore
  2761. ===============
  2762. */
  2763. void idShockwave::Restore( idRestoreGame *savefile ) {
  2764. savefile->ReadBool( isActive );
  2765. savefile->ReadInt( startTime );
  2766. savefile->ReadInt( duration );
  2767. savefile->ReadFloat( startSize );
  2768. savefile->ReadFloat( endSize );
  2769. savefile->ReadFloat( currentSize );
  2770. savefile->ReadFloat( magnitude );
  2771. savefile->ReadFloat( height );
  2772. savefile->ReadBool( playerDamaged );
  2773. savefile->ReadFloat( playerDamageSize );
  2774. }
  2775. /*
  2776. ===============
  2777. idShockwave::Spawn
  2778. ===============
  2779. */
  2780. void idShockwave::Spawn() {
  2781. spawnArgs.GetInt( "duration", "1000", duration );
  2782. spawnArgs.GetFloat( "startsize", "8", startSize );
  2783. spawnArgs.GetFloat( "endsize", "512", endSize );
  2784. spawnArgs.GetFloat( "magnitude", "100", magnitude );
  2785. spawnArgs.GetFloat( "height", "0", height);
  2786. spawnArgs.GetFloat( "player_damage_size", "20", playerDamageSize);
  2787. if ( spawnArgs.GetBool( "start_on" ) ) {
  2788. ProcessEvent( &EV_Activate, this );
  2789. }
  2790. }
  2791. /*
  2792. ===============
  2793. idShockwave::Think
  2794. ===============
  2795. */
  2796. void idShockwave::Think() {
  2797. int endTime;
  2798. if ( !isActive ) {
  2799. BecomeInactive( TH_THINK );
  2800. return;
  2801. }
  2802. endTime = startTime + duration;
  2803. if ( gameLocal.time < endTime ) {
  2804. float u;
  2805. float newSize;
  2806. // Expand shockwave
  2807. u = (float)(gameLocal.time - startTime) / (float)duration;
  2808. newSize = startSize + u * (endSize - startSize);
  2809. // Find all clipmodels between currentSize and newSize
  2810. idVec3 pos, end;
  2811. idClipModel *clipModelList[ MAX_GENTITIES ];
  2812. idClipModel *clip;
  2813. idEntity *ent;
  2814. int i, listedClipModels;
  2815. // Set bounds
  2816. pos = GetPhysics()->GetOrigin();
  2817. float zVal;
  2818. if(!height) {
  2819. zVal = newSize;
  2820. } else {
  2821. zVal = height/2.0f;
  2822. }
  2823. //Expand in a sphere
  2824. end = pos + idVec3( newSize, newSize, zVal );
  2825. idBounds bounds( end );
  2826. end = pos + idVec3( -newSize, -newSize, -zVal );
  2827. bounds.AddPoint( end );
  2828. if(g_debugShockwave.GetBool()) {
  2829. gameRenderWorld->DebugBounds(colorRed, bounds, vec3_origin);
  2830. }
  2831. listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
  2832. for ( i = 0; i < listedClipModels; i++ ) {
  2833. clip = clipModelList[ i ];
  2834. ent = clip->GetEntity();
  2835. if ( ent->IsHidden() ) {
  2836. continue;
  2837. }
  2838. if ( !ent->IsType( idMoveable::Type ) && !ent->IsType( idAFEntity_Base::Type ) && !ent->IsType( idPlayer::Type )) {
  2839. continue;
  2840. }
  2841. idVec3 point = ent->GetPhysics()->GetOrigin();
  2842. idVec3 force = point - pos;
  2843. float dist = force.Normalize();
  2844. if(ent->IsType( idPlayer::Type )) {
  2845. if(ent->GetPhysics()->GetAbsBounds().IntersectsBounds(bounds)) {
  2846. //For player damage we check the current radius and a specified player damage ring size
  2847. if ( dist <= newSize && dist > newSize-playerDamageSize ) {
  2848. idStr damageDef = spawnArgs.GetString("def_player_damage", "");
  2849. if(damageDef.Length() > 0 && !playerDamaged) {
  2850. playerDamaged = true; //Only damage once per shockwave
  2851. idPlayer* player = static_cast< idPlayer* >( ent );
  2852. idVec3 dir = ent->GetPhysics()->GetOrigin() - pos;
  2853. dir.NormalizeFast();
  2854. player->Damage(NULL, NULL, dir, damageDef, 1.0f, INVALID_JOINT);
  2855. }
  2856. }
  2857. }
  2858. } else {
  2859. // If the object is inside the current expansion...
  2860. if ( dist <= newSize && dist > currentSize ) {
  2861. force.z += 4.f;
  2862. force.NormalizeFast();
  2863. if ( ent->IsType( idAFEntity_Base::Type ) ) {
  2864. force = force * (ent->GetPhysics()->GetMass() * magnitude * 0.01f);
  2865. } else {
  2866. force = force * ent->GetPhysics()->GetMass() * magnitude;
  2867. }
  2868. // Kick it up, move force point off object origin
  2869. float rad = ent->GetPhysics()->GetBounds().GetRadius();
  2870. point.x += gameLocal.random.CRandomFloat() * rad;
  2871. point.y += gameLocal.random.CRandomFloat() * rad;
  2872. int j;
  2873. for( j=0; j < ent->GetPhysics()->GetNumClipModels(); j++ ) {
  2874. ent->GetPhysics()->AddForce( j, point, force );
  2875. }
  2876. }
  2877. }
  2878. }
  2879. // Update currentSize for next frame
  2880. currentSize = newSize;
  2881. } else {
  2882. // turn off
  2883. isActive = false;
  2884. }
  2885. }
  2886. /*
  2887. ===============
  2888. idShockwave::Event_Activate
  2889. ===============
  2890. */
  2891. void idShockwave::Event_Activate( idEntity *activator ) {
  2892. isActive = true;
  2893. startTime = gameLocal.time;
  2894. playerDamaged = false;
  2895. BecomeActive( TH_THINK );
  2896. }
  2897. /*
  2898. ===============================================================================
  2899. idFuncMountedObject
  2900. ===============================================================================
  2901. */
  2902. CLASS_DECLARATION( idEntity, idFuncMountedObject )
  2903. EVENT( EV_Touch, idFuncMountedObject::Event_Touch )
  2904. EVENT( EV_Activate, idFuncMountedObject::Event_Activate )
  2905. END_CLASS
  2906. /*
  2907. ===============
  2908. idFuncMountedObject::idFuncMountedObject
  2909. ===============
  2910. */
  2911. idFuncMountedObject::idFuncMountedObject() {
  2912. isMounted = false;
  2913. scriptFunction = NULL;
  2914. mountedPlayer = NULL;
  2915. harc = 0;
  2916. varc = 0;
  2917. }
  2918. /*
  2919. ===============
  2920. idFuncMountedObject::idFuncMountedObject
  2921. ===============
  2922. */
  2923. idFuncMountedObject::~idFuncMountedObject() {
  2924. }
  2925. /*
  2926. ===============
  2927. idFuncMountedObject::Spawn
  2928. ===============
  2929. */
  2930. void idFuncMountedObject::Spawn( void ) {
  2931. // Get viewOffset
  2932. spawnArgs.GetInt( "harc", "45", harc );
  2933. spawnArgs.GetInt( "varc", "30", varc );
  2934. // Get script function
  2935. idStr funcName = spawnArgs.GetString( "call", "" );
  2936. if ( funcName.Length() ) {
  2937. scriptFunction = gameLocal.program.FindFunction( funcName );
  2938. if ( scriptFunction == NULL ) {
  2939. gameLocal.Warning( "idFuncMountedObject '%s' at (%s) calls unknown function '%s'\n", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcName.c_str() );
  2940. }
  2941. }
  2942. BecomeActive( TH_THINK );
  2943. }
  2944. /*
  2945. ================
  2946. idFuncMountedObject::Think
  2947. ================
  2948. */
  2949. void idFuncMountedObject::Think( void ) {
  2950. idEntity::Think();
  2951. }
  2952. /*
  2953. ================
  2954. idFuncMountedObject::GetViewInfo
  2955. ================
  2956. */
  2957. void idFuncMountedObject::GetAngleRestrictions( int &yaw_min, int &yaw_max, int &pitch ) {
  2958. idMat3 axis;
  2959. idAngles angs;
  2960. axis = GetPhysics()->GetAxis();
  2961. angs = axis.ToAngles();
  2962. yaw_min = angs.yaw - harc;
  2963. yaw_min = idMath::AngleNormalize180( yaw_min );
  2964. yaw_max = angs.yaw + harc;
  2965. yaw_max = idMath::AngleNormalize180( yaw_max );
  2966. pitch = varc;
  2967. }
  2968. /*
  2969. ================
  2970. idFuncMountedObject::Event_Touch
  2971. ================
  2972. */
  2973. void idFuncMountedObject::Event_Touch( idEntity *other, trace_t *trace ) {
  2974. ProcessEvent( &EV_Activate, other );
  2975. }
  2976. /*
  2977. ================
  2978. idFuncMountedObject::Event_Activate
  2979. ================
  2980. */
  2981. void idFuncMountedObject::Event_Activate( idEntity *activator ) {
  2982. if ( !isMounted && activator->IsType( idPlayer::Type ) ) {
  2983. idPlayer *client = (idPlayer *)activator;
  2984. mountedPlayer = client;
  2985. /*
  2986. // Place player at path_corner targeted by mounted object
  2987. int i;
  2988. idPathCorner *spot;
  2989. for ( i = 0; i < targets.Num(); i++ ) {
  2990. if ( targets[i]->IsType( idPathCorner::Type ) ) {
  2991. spot = (idPathCorner*)targets[i];
  2992. break;
  2993. }
  2994. }
  2995. mountedPlayer->GetPhysics()->SetOrigin( spot->GetPhysics()->GetOrigin() );
  2996. mountedPlayer->GetPhysics()->SetAxis( spot->GetPhysics()->GetAxis() );
  2997. */
  2998. mountedPlayer->Bind( this, true );
  2999. mountedPlayer->mountedObject = this;
  3000. // Call a script function
  3001. idThread *mountthread;
  3002. if ( scriptFunction ) {
  3003. mountthread = new idThread( scriptFunction );
  3004. mountthread->DelayedStart( 0 );
  3005. }
  3006. isMounted = true;
  3007. }
  3008. }
  3009. /*
  3010. ===============================================================================
  3011. idFuncMountedWeapon
  3012. ===============================================================================
  3013. */
  3014. CLASS_DECLARATION( idFuncMountedObject, idFuncMountedWeapon )
  3015. EVENT( EV_PostSpawn, idFuncMountedWeapon::Event_PostSpawn )
  3016. END_CLASS
  3017. idFuncMountedWeapon::idFuncMountedWeapon() {
  3018. turret = NULL;
  3019. weaponLastFireTime = 0;
  3020. weaponFireDelay = 0;
  3021. projectile = NULL;
  3022. }
  3023. idFuncMountedWeapon::~idFuncMountedWeapon() {
  3024. }
  3025. void idFuncMountedWeapon::Spawn( void ) {
  3026. // Get projectile info
  3027. projectile = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_projectile" ), false );
  3028. if ( !projectile ) {
  3029. gameLocal.Warning( "Invalid projectile on func_mountedweapon." );
  3030. }
  3031. float firerate;
  3032. spawnArgs.GetFloat( "firerate", "3", firerate );
  3033. weaponFireDelay = 1000.f / firerate;
  3034. // Get the firing sound
  3035. idStr fireSound;
  3036. spawnArgs.GetString( "snd_fire", "", fireSound );
  3037. soundFireWeapon = declManager->FindSound( fireSound );
  3038. PostEventMS( &EV_PostSpawn, 0 );
  3039. }
  3040. void idFuncMountedWeapon::Think( void ) {
  3041. if ( isMounted && turret ) {
  3042. idVec3 vec = mountedPlayer->viewAngles.ToForward();
  3043. idAngles ang = mountedPlayer->GetLocalVector( vec ).ToAngles();
  3044. turret->GetPhysics()->SetAxis( ang.ToMat3() );
  3045. turret->UpdateVisuals();
  3046. // Check for firing
  3047. if ( mountedPlayer->usercmd.buttons & BUTTON_ATTACK && ( gameLocal.time > weaponLastFireTime + weaponFireDelay ) ) {
  3048. // FIRE!
  3049. idEntity *ent;
  3050. idProjectile *proj;
  3051. idBounds projBounds;
  3052. idVec3 dir;
  3053. gameLocal.SpawnEntityDef( *projectile, &ent );
  3054. if ( !ent || !ent->IsType( idProjectile::Type ) ) {
  3055. const char *projectileName = spawnArgs.GetString( "def_projectile" );
  3056. gameLocal.Error( "'%s' is not an idProjectile", projectileName );
  3057. }
  3058. mountedPlayer->GetViewPos( muzzleOrigin, muzzleAxis );
  3059. muzzleOrigin += ( muzzleAxis[0] * 128 );
  3060. muzzleOrigin -= ( muzzleAxis[2] * 20 );
  3061. dir = muzzleAxis[0];
  3062. proj = static_cast<idProjectile *>(ent);
  3063. proj->Create( this, muzzleOrigin, dir );
  3064. projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
  3065. proj->Launch( muzzleOrigin, dir, vec3_origin );
  3066. StartSoundShader( soundFireWeapon, SND_CHANNEL_WEAPON, SSF_GLOBAL, false, NULL );
  3067. weaponLastFireTime = gameLocal.time;
  3068. }
  3069. }
  3070. idFuncMountedObject::Think();
  3071. }
  3072. void idFuncMountedWeapon::Event_PostSpawn( void ) {
  3073. if ( targets.Num() >= 1 ) {
  3074. for ( int i=0; i < targets.Num(); i++ ) {
  3075. if ( targets[i].GetEntity()->IsType( idStaticEntity::Type ) ) {
  3076. turret = targets[i].GetEntity();
  3077. break;
  3078. }
  3079. }
  3080. } else {
  3081. gameLocal.Warning( "idFuncMountedWeapon::Spawn: Please target one model for a turret\n" );
  3082. }
  3083. }
  3084. /*
  3085. ===============================================================================
  3086. idPortalSky
  3087. ===============================================================================
  3088. */
  3089. CLASS_DECLARATION( idEntity, idPortalSky )
  3090. EVENT( EV_PostSpawn, idPortalSky::Event_PostSpawn )
  3091. EVENT( EV_Activate, idPortalSky::Event_Activate )
  3092. END_CLASS
  3093. /*
  3094. ===============
  3095. idPortalSky::idPortalSky
  3096. ===============
  3097. */
  3098. idPortalSky::idPortalSky( void ) {
  3099. }
  3100. /*
  3101. ===============
  3102. idPortalSky::~idPortalSky
  3103. ===============
  3104. */
  3105. idPortalSky::~idPortalSky( void ) {
  3106. }
  3107. /*
  3108. ===============
  3109. idPortalSky::Spawn
  3110. ===============
  3111. */
  3112. void idPortalSky::Spawn( void ) {
  3113. if ( !spawnArgs.GetBool( "triggered" ) ) {
  3114. PostEventMS( &EV_PostSpawn, 1 );
  3115. }
  3116. }
  3117. /*
  3118. ================
  3119. idPortalSky::Event_PostSpawn
  3120. ================
  3121. */
  3122. void idPortalSky::Event_PostSpawn() {
  3123. gameLocal.SetPortalSkyEnt( this );
  3124. }
  3125. /*
  3126. ================
  3127. idPortalSky::Event_Activate
  3128. ================
  3129. */
  3130. void idPortalSky::Event_Activate( idEntity *activator ) {
  3131. gameLocal.SetPortalSkyEnt( this );
  3132. }
  3133. #endif /* _D3XP */