PageRenderTime 73ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/neo/d3xp/Misc.cpp

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