PageRenderTime 70ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/dhewm3-master/neo/d3xp/Misc.cpp

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