Moveable.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /*
  24. ===============================================================================
  25. idMoveable
  26. ===============================================================================
  27. */
  28. const idEventDef EV_BecomeNonSolid( "becomeNonSolid" );
  29. const idEventDef EV_SetOwnerFromSpawnArgs( "<setOwnerFromSpawnArgs>" );
  30. const idEventDef EV_IsAtRest( "isAtRest", NULL, 'd' );
  31. const idEventDef EV_EnableDamage( "enableDamage", "f" );
  32. CLASS_DECLARATION( idEntity, idMoveable )
  33. EVENT( EV_Activate, idMoveable::Event_Activate )
  34. EVENT( EV_BecomeNonSolid, idMoveable::Event_BecomeNonSolid )
  35. EVENT( EV_SetOwnerFromSpawnArgs, idMoveable::Event_SetOwnerFromSpawnArgs )
  36. EVENT( EV_IsAtRest, idMoveable::Event_IsAtRest )
  37. EVENT( EV_EnableDamage, idMoveable::Event_EnableDamage )
  38. END_CLASS
  39. static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
  40. static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
  41. /*
  42. ================
  43. idMoveable::idMoveable
  44. ================
  45. */
  46. idMoveable::idMoveable( void ) {
  47. minDamageVelocity = 100.0f;
  48. maxDamageVelocity = 200.0f;
  49. nextCollideFxTime = 0;
  50. nextDamageTime = 0;
  51. nextSoundTime = 0;
  52. initialSpline = NULL;
  53. initialSplineDir = vec3_zero;
  54. explode = false;
  55. unbindOnDeath = false;
  56. allowStep = false;
  57. canDamage = false;
  58. #ifdef _D3XP
  59. attacker = NULL;
  60. #endif
  61. }
  62. /*
  63. ================
  64. idMoveable::~idMoveable
  65. ================
  66. */
  67. idMoveable::~idMoveable( void ) {
  68. delete initialSpline;
  69. initialSpline = NULL;
  70. }
  71. /*
  72. ================
  73. idMoveable::Spawn
  74. ================
  75. */
  76. void idMoveable::Spawn( void ) {
  77. idTraceModel trm;
  78. float density, friction, bouncyness, mass;
  79. int clipShrink;
  80. idStr clipModelName;
  81. // check if a clip model is set
  82. spawnArgs.GetString( "clipmodel", "", clipModelName );
  83. if ( !clipModelName[0] ) {
  84. clipModelName = spawnArgs.GetString( "model" ); // use the visual model
  85. }
  86. if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) {
  87. gameLocal.Error( "idMoveable '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() );
  88. return;
  89. }
  90. // if the model should be shrinked
  91. clipShrink = spawnArgs.GetInt( "clipshrink" );
  92. if ( clipShrink != 0 ) {
  93. trm.Shrink( clipShrink * CM_CLIP_EPSILON );
  94. }
  95. // get rigid body properties
  96. spawnArgs.GetFloat( "density", "0.5", density );
  97. density = idMath::ClampFloat( 0.001f, 1000.0f, density );
  98. spawnArgs.GetFloat( "friction", "0.05", friction );
  99. friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
  100. spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness );
  101. bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
  102. explode = spawnArgs.GetBool( "explode" );
  103. unbindOnDeath = spawnArgs.GetBool( "unbindondeath" );
  104. fxCollide = spawnArgs.GetString( "fx_collide" );
  105. nextCollideFxTime = 0;
  106. fl.takedamage = true;
  107. damage = spawnArgs.GetString( "def_damage", "" );
  108. #ifdef _D3XP
  109. monsterDamage = spawnArgs.GetString( "monster_damage", "" );
  110. fl.networkSync = true;
  111. attacker = NULL;
  112. #endif
  113. canDamage = spawnArgs.GetBool( "damageWhenActive" ) ? false : true;
  114. minDamageVelocity = spawnArgs.GetFloat( "minDamageVelocity", "300" ); // _D3XP
  115. maxDamageVelocity = spawnArgs.GetFloat( "maxDamageVelocity", "700" ); // _D3XP
  116. nextDamageTime = 0;
  117. nextSoundTime = 0;
  118. health = spawnArgs.GetInt( "health", "0" );
  119. spawnArgs.GetString( "broken", "", brokenModel );
  120. if ( health ) {
  121. if ( brokenModel != "" && !renderModelManager->CheckModel( brokenModel ) ) {
  122. gameLocal.Error( "idMoveable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), brokenModel.c_str() );
  123. }
  124. }
  125. // setup the physics
  126. physicsObj.SetSelf( this );
  127. physicsObj.SetClipModel( new idClipModel( trm ), density );
  128. physicsObj.GetClipModel()->SetMaterial( GetRenderModelMaterial() );
  129. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  130. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  131. physicsObj.SetBouncyness( bouncyness );
  132. physicsObj.SetFriction( 0.6f, 0.6f, friction );
  133. physicsObj.SetGravity( gameLocal.GetGravity() );
  134. physicsObj.SetContents( CONTENTS_SOLID );
  135. physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP );
  136. SetPhysics( &physicsObj );
  137. if ( spawnArgs.GetFloat( "mass", "10", mass ) ) {
  138. physicsObj.SetMass( mass );
  139. }
  140. if ( spawnArgs.GetBool( "nodrop" ) ) {
  141. physicsObj.PutToRest();
  142. } else {
  143. physicsObj.DropToFloor();
  144. }
  145. if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) ) {
  146. physicsObj.DisableImpact();
  147. }
  148. if ( spawnArgs.GetBool( "nonsolid" ) ) {
  149. BecomeNonSolid();
  150. }
  151. allowStep = spawnArgs.GetBool( "allowStep", "1" );
  152. PostEventMS( &EV_SetOwnerFromSpawnArgs, 0 );
  153. }
  154. /*
  155. ================
  156. idMoveable::Save
  157. ================
  158. */
  159. void idMoveable::Save( idSaveGame *savefile ) const {
  160. savefile->WriteString( brokenModel );
  161. savefile->WriteString( damage );
  162. #ifdef _D3XP
  163. savefile->WriteString( monsterDamage );
  164. savefile->WriteObject( attacker );
  165. #endif
  166. savefile->WriteString( fxCollide );
  167. savefile->WriteInt( nextCollideFxTime );
  168. savefile->WriteFloat( minDamageVelocity );
  169. savefile->WriteFloat( maxDamageVelocity );
  170. savefile->WriteBool( explode );
  171. savefile->WriteBool( unbindOnDeath );
  172. savefile->WriteBool( allowStep );
  173. savefile->WriteBool( canDamage );
  174. savefile->WriteInt( nextDamageTime );
  175. savefile->WriteInt( nextSoundTime );
  176. savefile->WriteInt( initialSpline != NULL ? initialSpline->GetTime( 0 ) : -1 );
  177. savefile->WriteVec3( initialSplineDir );
  178. savefile->WriteStaticObject( physicsObj );
  179. }
  180. /*
  181. ================
  182. idMoveable::Restore
  183. ================
  184. */
  185. void idMoveable::Restore( idRestoreGame *savefile ) {
  186. int initialSplineTime;
  187. savefile->ReadString( brokenModel );
  188. savefile->ReadString( damage );
  189. #ifdef _D3XP
  190. savefile->ReadString( monsterDamage );
  191. savefile->ReadObject( reinterpret_cast<idClass *&>( attacker ) );
  192. #endif
  193. savefile->ReadString( fxCollide );
  194. savefile->ReadInt( nextCollideFxTime );
  195. savefile->ReadFloat( minDamageVelocity );
  196. savefile->ReadFloat( maxDamageVelocity );
  197. savefile->ReadBool( explode );
  198. savefile->ReadBool( unbindOnDeath );
  199. savefile->ReadBool( allowStep );
  200. savefile->ReadBool( canDamage );
  201. savefile->ReadInt( nextDamageTime );
  202. savefile->ReadInt( nextSoundTime );
  203. savefile->ReadInt( initialSplineTime );
  204. savefile->ReadVec3( initialSplineDir );
  205. if ( initialSplineTime != -1 ) {
  206. InitInitialSpline( initialSplineTime );
  207. } else {
  208. initialSpline = NULL;
  209. }
  210. savefile->ReadStaticObject( physicsObj );
  211. RestorePhysics( &physicsObj );
  212. }
  213. /*
  214. ================
  215. idMoveable::Hide
  216. ================
  217. */
  218. void idMoveable::Hide( void ) {
  219. idEntity::Hide();
  220. physicsObj.SetContents( 0 );
  221. }
  222. /*
  223. ================
  224. idMoveable::Show
  225. ================
  226. */
  227. void idMoveable::Show( void ) {
  228. idEntity::Show();
  229. if ( !spawnArgs.GetBool( "nonsolid" ) ) {
  230. physicsObj.SetContents( CONTENTS_SOLID );
  231. }
  232. }
  233. /*
  234. =================
  235. idMoveable::Collide
  236. =================
  237. */
  238. bool idMoveable::Collide( const trace_t &collision, const idVec3 &velocity ) {
  239. float v, f;
  240. idVec3 dir;
  241. idEntity *ent;
  242. v = -( velocity * collision.c.normal );
  243. if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
  244. f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
  245. if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
  246. // don't set the volume unless there is a bounce sound as it overrides the entire channel
  247. // which causes footsteps on ai's to not honor their shader parms
  248. SetSoundVolume( f );
  249. }
  250. nextSoundTime = gameLocal.time + 500;
  251. }
  252. // _D3XP :: changes relating to the addition of monsterDamage
  253. if ( !gameLocal.isClient && canDamage && gameLocal.time > nextDamageTime ) {
  254. bool hasDamage = damage.Length() > 0;
  255. bool hasMonsterDamage = monsterDamage.Length() > 0;
  256. if ( hasDamage || hasMonsterDamage ) {
  257. ent = gameLocal.entities[ collision.c.entityNum ];
  258. if ( ent && v > minDamageVelocity ) {
  259. f = v > maxDamageVelocity ? 1.0f : idMath::Sqrt( v - minDamageVelocity ) * ( 1.0f / idMath::Sqrt( maxDamageVelocity - minDamageVelocity ) );
  260. dir = velocity;
  261. dir.NormalizeFast();
  262. if ( ent->IsType( idAI::Type ) && hasMonsterDamage ) {
  263. #ifdef _D3XP
  264. if ( attacker ) {
  265. ent->Damage( this, attacker, dir, monsterDamage, f, INVALID_JOINT );
  266. }
  267. else {
  268. ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, monsterDamage, f, INVALID_JOINT );
  269. }
  270. #else
  271. ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, monsterDamage, f, INVALID_JOINT );
  272. #endif
  273. } else if ( hasDamage ) {
  274. #ifdef _D3XP
  275. // in multiplayer, scale damage wrt mass of object
  276. if ( gameLocal.isMultiplayer ) {
  277. f *= GetPhysics()->GetMass() * g_moveableDamageScale.GetFloat();
  278. }
  279. if ( attacker ) {
  280. ent->Damage( this, attacker, dir, damage, f, INVALID_JOINT );
  281. }
  282. else {
  283. ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, damage, f, INVALID_JOINT );
  284. }
  285. #else
  286. ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, damage, f, INVALID_JOINT );
  287. #endif
  288. }
  289. nextDamageTime = gameLocal.time + 1000;
  290. }
  291. }
  292. }
  293. #ifdef _D3XP
  294. if ( this->IsType( idExplodingBarrel::Type ) ) {
  295. idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(this);
  296. if ( !ebarrel->IsStable() ) {
  297. PostEventSec( &EV_Explode, 0.04f );
  298. }
  299. }
  300. #endif
  301. if ( fxCollide.Length() && gameLocal.time > nextCollideFxTime ) {
  302. idEntityFx::StartFx( fxCollide, &collision.c.point, NULL, this, false );
  303. nextCollideFxTime = gameLocal.time + 3500;
  304. }
  305. return false;
  306. }
  307. /*
  308. ============
  309. idMoveable::Killed
  310. ============
  311. */
  312. void idMoveable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  313. if ( unbindOnDeath ) {
  314. Unbind();
  315. }
  316. if ( brokenModel != "" ) {
  317. SetModel( brokenModel );
  318. }
  319. if ( explode ) {
  320. if ( brokenModel == "" ) {
  321. PostEventMS( &EV_Remove, 1000 );
  322. }
  323. }
  324. if ( renderEntity.gui[ 0 ] ) {
  325. renderEntity.gui[ 0 ] = NULL;
  326. }
  327. ActivateTargets( this );
  328. fl.takedamage = false;
  329. }
  330. /*
  331. ================
  332. idMoveable::AllowStep
  333. ================
  334. */
  335. bool idMoveable::AllowStep( void ) const {
  336. return allowStep;
  337. }
  338. /*
  339. ================
  340. idMoveable::BecomeNonSolid
  341. ================
  342. */
  343. void idMoveable::BecomeNonSolid( void ) {
  344. // set CONTENTS_RENDERMODEL so bullets still collide with the moveable
  345. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_RENDERMODEL );
  346. physicsObj.SetClipMask( MASK_SOLID | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP );
  347. }
  348. /*
  349. ================
  350. idMoveable::EnableDamage
  351. ================
  352. */
  353. void idMoveable::EnableDamage( bool enable, float duration ) {
  354. #ifdef _D3XP
  355. if ( canDamage == enable ) {
  356. return;
  357. }
  358. #endif
  359. canDamage = enable;
  360. if ( duration ) {
  361. PostEventSec( &EV_EnableDamage, duration, ( /*_D3XP*/enable ) ? 0.0f : 1.0f );
  362. }
  363. }
  364. /*
  365. ================
  366. idMoveable::InitInitialSpline
  367. ================
  368. */
  369. void idMoveable::InitInitialSpline( int startTime ) {
  370. int initialSplineTime;
  371. initialSpline = GetSpline();
  372. initialSplineTime = spawnArgs.GetInt( "initialSplineTime", "300" );
  373. if ( initialSpline != NULL ) {
  374. initialSpline->MakeUniform( initialSplineTime );
  375. initialSpline->ShiftTime( startTime - initialSpline->GetTime( 0 ) );
  376. initialSplineDir = initialSpline->GetCurrentFirstDerivative( startTime );
  377. initialSplineDir *= physicsObj.GetAxis().Transpose();
  378. initialSplineDir.Normalize();
  379. BecomeActive( TH_THINK );
  380. }
  381. }
  382. /*
  383. ================
  384. idMoveable::FollowInitialSplinePath
  385. ================
  386. */
  387. bool idMoveable::FollowInitialSplinePath( void ) {
  388. if ( initialSpline != NULL ) {
  389. if ( gameLocal.time < initialSpline->GetTime( initialSpline->GetNumValues() - 1 ) ) {
  390. idVec3 splinePos = initialSpline->GetCurrentValue( gameLocal.time );
  391. idVec3 linearVelocity = ( splinePos - physicsObj.GetOrigin() ) * USERCMD_HZ;
  392. physicsObj.SetLinearVelocity( linearVelocity );
  393. idVec3 splineDir = initialSpline->GetCurrentFirstDerivative( gameLocal.time );
  394. idVec3 dir = initialSplineDir * physicsObj.GetAxis();
  395. idVec3 angularVelocity = dir.Cross( splineDir );
  396. angularVelocity.Normalize();
  397. angularVelocity *= idMath::ACos16( dir * splineDir / splineDir.Length() ) * USERCMD_HZ;
  398. physicsObj.SetAngularVelocity( angularVelocity );
  399. return true;
  400. } else {
  401. delete initialSpline;
  402. initialSpline = NULL;
  403. }
  404. }
  405. return false;
  406. }
  407. /*
  408. ================
  409. idMoveable::Think
  410. ================
  411. */
  412. void idMoveable::Think( void ) {
  413. if ( thinkFlags & TH_THINK ) {
  414. if ( !FollowInitialSplinePath() ) {
  415. BecomeInactive( TH_THINK );
  416. }
  417. }
  418. idEntity::Think();
  419. }
  420. /*
  421. ================
  422. idMoveable::GetRenderModelMaterial
  423. ================
  424. */
  425. const idMaterial *idMoveable::GetRenderModelMaterial( void ) const {
  426. if ( renderEntity.customShader ) {
  427. return renderEntity.customShader;
  428. }
  429. if ( renderEntity.hModel && renderEntity.hModel->NumSurfaces() ) {
  430. return renderEntity.hModel->Surface( 0 )->shader;
  431. }
  432. return NULL;
  433. }
  434. /*
  435. ================
  436. idMoveable::WriteToSnapshot
  437. ================
  438. */
  439. void idMoveable::WriteToSnapshot( idBitMsgDelta &msg ) const {
  440. physicsObj.WriteToSnapshot( msg );
  441. }
  442. /*
  443. ================
  444. idMoveable::ReadFromSnapshot
  445. ================
  446. */
  447. void idMoveable::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  448. physicsObj.ReadFromSnapshot( msg );
  449. if ( msg.HasChanged() ) {
  450. UpdateVisuals();
  451. }
  452. }
  453. /*
  454. ================
  455. idMoveable::Event_BecomeNonSolid
  456. ================
  457. */
  458. void idMoveable::Event_BecomeNonSolid( void ) {
  459. BecomeNonSolid();
  460. }
  461. #ifdef _D3XP
  462. /*
  463. ================
  464. idMoveable::SetAttacker
  465. ================
  466. */
  467. void idMoveable::SetAttacker( idEntity *ent ) {
  468. attacker = ent;
  469. }
  470. #endif
  471. /*
  472. ================
  473. idMoveable::Event_Activate
  474. ================
  475. */
  476. void idMoveable::Event_Activate( idEntity *activator ) {
  477. float delay;
  478. idVec3 init_velocity, init_avelocity;
  479. Show();
  480. if ( !spawnArgs.GetInt( "notPushable" ) ) {
  481. physicsObj.EnableImpact();
  482. }
  483. physicsObj.Activate();
  484. spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
  485. spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
  486. delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
  487. if ( delay == 0.0f ) {
  488. physicsObj.SetLinearVelocity( init_velocity );
  489. } else {
  490. PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
  491. }
  492. delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
  493. if ( delay == 0.0f ) {
  494. physicsObj.SetAngularVelocity( init_avelocity );
  495. } else {
  496. PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
  497. }
  498. InitInitialSpline( gameLocal.time );
  499. }
  500. /*
  501. ================
  502. idMoveable::Event_SetOwnerFromSpawnArgs
  503. ================
  504. */
  505. void idMoveable::Event_SetOwnerFromSpawnArgs( void ) {
  506. idStr owner;
  507. if ( spawnArgs.GetString( "owner", "", owner ) ) {
  508. ProcessEvent( &EV_SetOwner, gameLocal.FindEntity( owner ) );
  509. }
  510. }
  511. /*
  512. ================
  513. idMoveable::Event_IsAtRest
  514. ================
  515. */
  516. void idMoveable::Event_IsAtRest( void ) {
  517. idThread::ReturnInt( physicsObj.IsAtRest() );
  518. }
  519. /*
  520. ================
  521. idMoveable::Event_EnableDamage
  522. ================
  523. */
  524. void idMoveable::Event_EnableDamage( float enable ) {
  525. #ifdef _D3XP
  526. // clear out attacker
  527. attacker = NULL;
  528. #endif
  529. canDamage = ( enable != 0.0f );
  530. }
  531. /*
  532. ===============================================================================
  533. idBarrel
  534. ===============================================================================
  535. */
  536. CLASS_DECLARATION( idMoveable, idBarrel )
  537. END_CLASS
  538. /*
  539. ================
  540. idBarrel::idBarrel
  541. ================
  542. */
  543. idBarrel::idBarrel() {
  544. radius = 1.0f;
  545. barrelAxis = 0;
  546. lastOrigin.Zero();
  547. lastAxis.Identity();
  548. additionalRotation = 0.0f;
  549. additionalAxis.Identity();
  550. fl.networkSync = true;
  551. }
  552. /*
  553. ================
  554. idBarrel::Save
  555. ================
  556. */
  557. void idBarrel::Save( idSaveGame *savefile ) const {
  558. savefile->WriteFloat( radius );
  559. savefile->WriteInt( barrelAxis );
  560. savefile->WriteVec3( lastOrigin );
  561. savefile->WriteMat3( lastAxis );
  562. savefile->WriteFloat( additionalRotation );
  563. savefile->WriteMat3( additionalAxis );
  564. }
  565. /*
  566. ================
  567. idBarrel::Restore
  568. ================
  569. */
  570. void idBarrel::Restore( idRestoreGame *savefile ) {
  571. savefile->ReadFloat( radius );
  572. savefile->ReadInt( barrelAxis );
  573. savefile->ReadVec3( lastOrigin );
  574. savefile->ReadMat3( lastAxis );
  575. savefile->ReadFloat( additionalRotation );
  576. savefile->ReadMat3( additionalAxis );
  577. }
  578. /*
  579. ================
  580. idBarrel::BarrelThink
  581. ================
  582. */
  583. void idBarrel::BarrelThink( void ) {
  584. bool wasAtRest, onGround;
  585. float movedDistance, rotatedDistance, angle;
  586. idVec3 curOrigin, gravityNormal, dir;
  587. idMat3 curAxis, axis;
  588. wasAtRest = IsAtRest();
  589. // run physics
  590. RunPhysics();
  591. // only need to give the visual model an additional rotation if the physics were run
  592. if ( !wasAtRest ) {
  593. // current physics state
  594. onGround = GetPhysics()->HasGroundContacts();
  595. curOrigin = GetPhysics()->GetOrigin();
  596. curAxis = GetPhysics()->GetAxis();
  597. // if the barrel is on the ground
  598. if ( onGround ) {
  599. gravityNormal = GetPhysics()->GetGravityNormal();
  600. dir = curOrigin - lastOrigin;
  601. dir -= gravityNormal * dir * gravityNormal;
  602. movedDistance = dir.LengthSqr();
  603. // if the barrel moved and the barrel is not aligned with the gravity direction
  604. if ( movedDistance > 0.0f && idMath::Fabs( gravityNormal * curAxis[barrelAxis] ) < 0.7f ) {
  605. // barrel movement since last think frame orthogonal to the barrel axis
  606. movedDistance = idMath::Sqrt( movedDistance );
  607. dir *= 1.0f / movedDistance;
  608. movedDistance = ( 1.0f - idMath::Fabs( dir * curAxis[barrelAxis] ) ) * movedDistance;
  609. // get rotation about barrel axis since last think frame
  610. angle = lastAxis[(barrelAxis+1)%3] * curAxis[(barrelAxis+1)%3];
  611. angle = idMath::ACos( angle );
  612. // distance along cylinder hull
  613. rotatedDistance = angle * radius;
  614. // if the barrel moved further than it rotated about it's axis
  615. if ( movedDistance > rotatedDistance ) {
  616. // additional rotation of the visual model to make it look
  617. // like the barrel rolls instead of slides
  618. angle = 180.0f * (movedDistance - rotatedDistance) / (radius * idMath::PI);
  619. if ( gravityNormal.Cross( curAxis[barrelAxis] ) * dir < 0.0f ) {
  620. additionalRotation += angle;
  621. } else {
  622. additionalRotation -= angle;
  623. }
  624. dir = vec3_origin;
  625. dir[barrelAxis] = 1.0f;
  626. additionalAxis = idRotation( vec3_origin, dir, additionalRotation ).ToMat3();
  627. }
  628. }
  629. }
  630. // save state for next think
  631. lastOrigin = curOrigin;
  632. lastAxis = curAxis;
  633. }
  634. Present();
  635. }
  636. /*
  637. ================
  638. idBarrel::Think
  639. ================
  640. */
  641. void idBarrel::Think( void ) {
  642. if ( thinkFlags & TH_THINK ) {
  643. if ( !FollowInitialSplinePath() ) {
  644. BecomeInactive( TH_THINK );
  645. }
  646. }
  647. BarrelThink();
  648. }
  649. /*
  650. ================
  651. idBarrel::GetPhysicsToVisualTransform
  652. ================
  653. */
  654. bool idBarrel::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
  655. origin = vec3_origin;
  656. axis = additionalAxis;
  657. return true;
  658. }
  659. /*
  660. ================
  661. idBarrel::Spawn
  662. ================
  663. */
  664. void idBarrel::Spawn( void ) {
  665. const idBounds &bounds = GetPhysics()->GetBounds();
  666. // radius of the barrel cylinder
  667. radius = ( bounds[1][0] - bounds[0][0] ) * 0.5f;
  668. // always a vertical barrel with cylinder axis parallel to the z-axis
  669. barrelAxis = 2;
  670. lastOrigin = GetPhysics()->GetOrigin();
  671. lastAxis = GetPhysics()->GetAxis();
  672. additionalRotation = 0.0f;
  673. additionalAxis.Identity();
  674. #ifdef _D3XP
  675. fl.networkSync = true;
  676. #endif
  677. }
  678. /*
  679. ================
  680. idBarrel::ClientPredictionThink
  681. ================
  682. */
  683. void idBarrel::ClientPredictionThink( void ) {
  684. Think();
  685. }
  686. /*
  687. ===============================================================================
  688. idExplodingBarrel
  689. ===============================================================================
  690. */
  691. const idEventDef EV_Respawn( "<respawn>" );
  692. const idEventDef EV_TriggerTargets( "<triggertargets>" );
  693. CLASS_DECLARATION( idBarrel, idExplodingBarrel )
  694. EVENT( EV_Activate, idExplodingBarrel::Event_Activate )
  695. EVENT( EV_Respawn, idExplodingBarrel::Event_Respawn )
  696. EVENT( EV_Explode, idExplodingBarrel::Event_Explode )
  697. EVENT( EV_TriggerTargets, idExplodingBarrel::Event_TriggerTargets )
  698. END_CLASS
  699. /*
  700. ================
  701. idExplodingBarrel::idExplodingBarrel
  702. ================
  703. */
  704. idExplodingBarrel::idExplodingBarrel() {
  705. spawnOrigin.Zero();
  706. spawnAxis.Zero();
  707. state = NORMAL;
  708. #ifdef _D3XP
  709. isStable = true;
  710. #endif
  711. particleModelDefHandle = -1;
  712. lightDefHandle = -1;
  713. memset( &particleRenderEntity, 0, sizeof( particleRenderEntity ) );
  714. memset( &light, 0, sizeof( light ) );
  715. particleTime = 0;
  716. lightTime = 0;
  717. time = 0.0f;
  718. }
  719. /*
  720. ================
  721. idExplodingBarrel::~idExplodingBarrel
  722. ================
  723. */
  724. idExplodingBarrel::~idExplodingBarrel() {
  725. if ( particleModelDefHandle >= 0 ){
  726. gameRenderWorld->FreeEntityDef( particleModelDefHandle );
  727. }
  728. if ( lightDefHandle >= 0 ) {
  729. gameRenderWorld->FreeLightDef( lightDefHandle );
  730. }
  731. }
  732. /*
  733. ================
  734. idExplodingBarrel::Save
  735. ================
  736. */
  737. void idExplodingBarrel::Save( idSaveGame *savefile ) const {
  738. savefile->WriteVec3( spawnOrigin );
  739. savefile->WriteMat3( spawnAxis );
  740. savefile->WriteInt( state );
  741. savefile->WriteInt( particleModelDefHandle );
  742. savefile->WriteInt( lightDefHandle );
  743. savefile->WriteRenderEntity( particleRenderEntity );
  744. savefile->WriteRenderLight( light );
  745. savefile->WriteInt( particleTime );
  746. savefile->WriteInt( lightTime );
  747. savefile->WriteFloat( time );
  748. #ifdef _D3XP
  749. savefile->WriteBool( isStable );
  750. #endif
  751. }
  752. /*
  753. ================
  754. idExplodingBarrel::Restore
  755. ================
  756. */
  757. void idExplodingBarrel::Restore( idRestoreGame *savefile ) {
  758. savefile->ReadVec3( spawnOrigin );
  759. savefile->ReadMat3( spawnAxis );
  760. savefile->ReadInt( (int &)state );
  761. savefile->ReadInt( (int &)particleModelDefHandle );
  762. savefile->ReadInt( (int &)lightDefHandle );
  763. savefile->ReadRenderEntity( particleRenderEntity );
  764. savefile->ReadRenderLight( light );
  765. savefile->ReadInt( particleTime );
  766. savefile->ReadInt( lightTime );
  767. savefile->ReadFloat( time );
  768. #ifdef _D3XP
  769. savefile->ReadBool( isStable );
  770. if ( lightDefHandle != -1 ) {
  771. lightDefHandle = gameRenderWorld->AddLightDef( &light );
  772. }
  773. if ( particleModelDefHandle != -1 ) {
  774. particleModelDefHandle = gameRenderWorld->AddEntityDef( &particleRenderEntity );
  775. }
  776. #endif
  777. }
  778. /*
  779. ================
  780. idExplodingBarrel::Spawn
  781. ================
  782. */
  783. void idExplodingBarrel::Spawn( void ) {
  784. health = spawnArgs.GetInt( "health", "5" );
  785. fl.takedamage = true;
  786. #ifdef _D3XP
  787. isStable = true;
  788. fl.networkSync = true;
  789. #endif
  790. spawnOrigin = GetPhysics()->GetOrigin();
  791. spawnAxis = GetPhysics()->GetAxis();
  792. state = NORMAL;
  793. particleModelDefHandle = -1;
  794. lightDefHandle = -1;
  795. lightTime = 0;
  796. particleTime = 0;
  797. time = spawnArgs.GetFloat( "time" );
  798. memset( &particleRenderEntity, 0, sizeof( particleRenderEntity ) );
  799. memset( &light, 0, sizeof( light ) );
  800. }
  801. /*
  802. ================
  803. idExplodingBarrel::Think
  804. ================
  805. */
  806. void idExplodingBarrel::Think( void ) {
  807. idBarrel::BarrelThink();
  808. if ( lightDefHandle >= 0 ){
  809. if ( state == BURNING ) {
  810. // ramp the color up over 250 ms
  811. float pct = (gameLocal.time - lightTime) / 250.f;
  812. if ( pct > 1.0f ) {
  813. pct = 1.0f;
  814. }
  815. light.origin = physicsObj.GetAbsBounds().GetCenter();
  816. light.axis = mat3_identity;
  817. light.shaderParms[ SHADERPARM_RED ] = pct;
  818. light.shaderParms[ SHADERPARM_GREEN ] = pct;
  819. light.shaderParms[ SHADERPARM_BLUE ] = pct;
  820. light.shaderParms[ SHADERPARM_ALPHA ] = pct;
  821. gameRenderWorld->UpdateLightDef( lightDefHandle, &light );
  822. } else {
  823. if ( gameLocal.time - lightTime > 250 ) {
  824. gameRenderWorld->FreeLightDef( lightDefHandle );
  825. lightDefHandle = -1;
  826. }
  827. return;
  828. }
  829. }
  830. if ( !gameLocal.isClient && state != BURNING && state != EXPLODING ) {
  831. BecomeInactive( TH_THINK );
  832. return;
  833. }
  834. if ( particleModelDefHandle >= 0 ){
  835. particleRenderEntity.origin = physicsObj.GetAbsBounds().GetCenter();
  836. particleRenderEntity.axis = mat3_identity;
  837. gameRenderWorld->UpdateEntityDef( particleModelDefHandle, &particleRenderEntity );
  838. }
  839. }
  840. #ifdef _D3XP
  841. /*
  842. ================
  843. idExplodingBarrel::SetStability
  844. ================
  845. */
  846. void idExplodingBarrel::SetStability( bool stability ) {
  847. isStable = stability;
  848. }
  849. /*
  850. ================
  851. idExplodingBarrel::IsStable
  852. ================
  853. */
  854. bool idExplodingBarrel::IsStable( void ) {
  855. return isStable;
  856. }
  857. /*
  858. ================
  859. idExplodingBarrel::StartBurning
  860. ================
  861. */
  862. void idExplodingBarrel::StartBurning( void ) {
  863. state = BURNING;
  864. AddParticles( "barrelfire.prt", true );
  865. }
  866. /*
  867. ================
  868. idExplodingBarrel::StartBurning
  869. ================
  870. */
  871. void idExplodingBarrel::StopBurning( void ) {
  872. state = NORMAL;
  873. if ( particleModelDefHandle >= 0 ){
  874. gameRenderWorld->FreeEntityDef( particleModelDefHandle );
  875. particleModelDefHandle = -1;
  876. particleTime = 0;
  877. memset( &particleRenderEntity, 0, sizeof( particleRenderEntity ) );
  878. }
  879. }
  880. #endif
  881. /*
  882. ================
  883. idExplodingBarrel::AddParticles
  884. ================
  885. */
  886. void idExplodingBarrel::AddParticles( const char *name, bool burn ) {
  887. if ( name && *name ) {
  888. #ifdef _D3XP
  889. int explicitTimeGroup = timeGroup;
  890. SetTimeState explicitTS( explicitTimeGroup );
  891. #endif
  892. if ( particleModelDefHandle >= 0 ){
  893. gameRenderWorld->FreeEntityDef( particleModelDefHandle );
  894. }
  895. memset( &particleRenderEntity, 0, sizeof ( particleRenderEntity ) );
  896. const idDeclModelDef *modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, name ) );
  897. if ( modelDef ) {
  898. particleRenderEntity.origin = physicsObj.GetAbsBounds().GetCenter();
  899. particleRenderEntity.axis = mat3_identity;
  900. particleRenderEntity.hModel = modelDef->ModelHandle();
  901. float rgb = ( burn ) ? 0.0f : 1.0f;
  902. particleRenderEntity.shaderParms[ SHADERPARM_RED ] = rgb;
  903. particleRenderEntity.shaderParms[ SHADERPARM_GREEN ] = rgb;
  904. particleRenderEntity.shaderParms[ SHADERPARM_BLUE ] = rgb;
  905. particleRenderEntity.shaderParms[ SHADERPARM_ALPHA ] = rgb;
  906. particleRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
  907. particleRenderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = ( burn ) ? 1.0f : gameLocal.random.RandomInt( 90 );
  908. #ifdef _D3XP
  909. particleRenderEntity.timeGroup = explicitTimeGroup;
  910. #endif
  911. if ( !particleRenderEntity.hModel ) {
  912. particleRenderEntity.hModel = renderModelManager->FindModel( name );
  913. }
  914. particleModelDefHandle = gameRenderWorld->AddEntityDef( &particleRenderEntity );
  915. if ( burn ) {
  916. BecomeActive( TH_THINK );
  917. }
  918. particleTime = gameLocal.realClientTime;
  919. }
  920. }
  921. }
  922. /*
  923. ================
  924. idExplodingBarrel::AddLight
  925. ================
  926. */
  927. void idExplodingBarrel::AddLight( const char *name, bool burn ) {
  928. if ( lightDefHandle >= 0 ){
  929. gameRenderWorld->FreeLightDef( lightDefHandle );
  930. }
  931. memset( &light, 0, sizeof ( light ) );
  932. light.axis = mat3_identity;
  933. light.lightRadius.x = spawnArgs.GetFloat( "light_radius" );
  934. light.lightRadius.y = light.lightRadius.z = light.lightRadius.x;
  935. light.origin = physicsObj.GetOrigin();
  936. light.origin.z += 128;
  937. light.pointLight = true;
  938. light.shader = declManager->FindMaterial( name );
  939. light.shaderParms[ SHADERPARM_RED ] = 2.0f;
  940. light.shaderParms[ SHADERPARM_GREEN ] = 2.0f;
  941. light.shaderParms[ SHADERPARM_BLUE ] = 2.0f;
  942. light.shaderParms[ SHADERPARM_ALPHA ] = 2.0f;
  943. lightDefHandle = gameRenderWorld->AddLightDef( &light );
  944. lightTime = gameLocal.realClientTime;
  945. BecomeActive( TH_THINK );
  946. }
  947. /*
  948. ================
  949. idExplodingBarrel::ExplodingEffects
  950. ================
  951. */
  952. void idExplodingBarrel::ExplodingEffects( void ) {
  953. const char *temp;
  954. StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
  955. temp = spawnArgs.GetString( "model_damage" );
  956. if ( *temp != '\0' ) {
  957. SetModel( temp );
  958. Show();
  959. }
  960. temp = spawnArgs.GetString( "model_detonate" );
  961. if ( *temp != '\0' ) {
  962. AddParticles( temp, false );
  963. }
  964. temp = spawnArgs.GetString( "mtr_lightexplode" );
  965. if ( *temp != '\0' ) {
  966. AddLight( temp, false );
  967. }
  968. temp = spawnArgs.GetString( "mtr_burnmark" );
  969. if ( *temp != '\0' ) {
  970. gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetGravity(), 128.0f, true, 96.0f, temp );
  971. }
  972. }
  973. /*
  974. ================
  975. idExplodingBarrel::Killed
  976. ================
  977. */
  978. void idExplodingBarrel::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  979. if ( IsHidden() || state == EXPLODING || state == BURNING ) {
  980. return;
  981. }
  982. float f = spawnArgs.GetFloat( "burn" );
  983. if ( f > 0.0f && state == NORMAL ) {
  984. state = BURNING;
  985. PostEventSec( &EV_Explode, f );
  986. StartSound( "snd_burn", SND_CHANNEL_ANY, 0, false, NULL );
  987. AddParticles( spawnArgs.GetString ( "model_burn", "" ), true );
  988. return;
  989. } else {
  990. state = EXPLODING;
  991. if ( gameLocal.isServer ) {
  992. idBitMsg msg;
  993. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  994. msg.Init( msgBuf, sizeof( msgBuf ) );
  995. msg.WriteLong( gameLocal.time );
  996. ServerSendEvent( EVENT_EXPLODE, &msg, false, -1 );
  997. }
  998. }
  999. // do this before applying radius damage so the ent can trace to any damagable ents nearby
  1000. Hide();
  1001. physicsObj.SetContents( 0 );
  1002. const char *splash = spawnArgs.GetString( "def_splash_damage", "damage_explosion" );
  1003. if ( splash && *splash ) {
  1004. gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), this, attacker, this, this, splash );
  1005. }
  1006. ExplodingEffects( );
  1007. //FIXME: need to precache all the debris stuff here and in the projectiles
  1008. const idKeyValue *kv = spawnArgs.MatchPrefix( "def_debris" );
  1009. // bool first = true;
  1010. while ( kv ) {
  1011. const idDict *debris_args = gameLocal.FindEntityDefDict( kv->GetValue(), false );
  1012. if ( debris_args ) {
  1013. idEntity *ent;
  1014. idVec3 dir;
  1015. idDebris *debris;
  1016. //if ( first ) {
  1017. dir = physicsObj.GetAxis()[1];
  1018. // first = false;
  1019. //} else {
  1020. dir.x += gameLocal.random.CRandomFloat() * 4.0f;
  1021. dir.y += gameLocal.random.CRandomFloat() * 4.0f;
  1022. //dir.z = gameLocal.random.RandomFloat() * 8.0f;
  1023. //}
  1024. dir.Normalize();
  1025. gameLocal.SpawnEntityDef( *debris_args, &ent, false );
  1026. if ( !ent || !ent->IsType( idDebris::Type ) ) {
  1027. gameLocal.Error( "'projectile_debris' is not an idDebris" );
  1028. }
  1029. debris = static_cast<idDebris *>(ent);
  1030. debris->Create( this, physicsObj.GetOrigin(), dir.ToMat3() );
  1031. debris->Launch();
  1032. debris->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = ( gameLocal.time + 1500 ) * 0.001f;
  1033. debris->UpdateVisuals();
  1034. }
  1035. kv = spawnArgs.MatchPrefix( "def_debris", kv );
  1036. }
  1037. physicsObj.PutToRest();
  1038. CancelEvents( &EV_Explode );
  1039. CancelEvents( &EV_Activate );
  1040. f = spawnArgs.GetFloat( "respawn" );
  1041. if ( f > 0.0f ) {
  1042. PostEventSec( &EV_Respawn, f );
  1043. } else {
  1044. PostEventMS( &EV_Remove, 5000 );
  1045. }
  1046. if ( spawnArgs.GetBool( "triggerTargets" ) ) {
  1047. ActivateTargets( this );
  1048. }
  1049. }
  1050. /*
  1051. ================
  1052. idExplodingBarrel::Damage
  1053. ================
  1054. */
  1055. void idExplodingBarrel::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
  1056. const char *damageDefName, const float damageScale, const int location ) {
  1057. const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
  1058. if ( !damageDef ) {
  1059. gameLocal.Error( "Unknown damageDef '%s'\n", damageDefName );
  1060. }
  1061. if ( damageDef->FindKey( "radius" ) && GetPhysics()->GetContents() != 0 && GetBindMaster() == NULL ) {
  1062. PostEventMS( &EV_Explode, 400 );
  1063. } else {
  1064. idEntity::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
  1065. }
  1066. }
  1067. /*
  1068. ================
  1069. idExplodingBarrel::Event_TriggerTargets
  1070. ================
  1071. */
  1072. void idExplodingBarrel::Event_TriggerTargets() {
  1073. ActivateTargets( this );
  1074. }
  1075. /*
  1076. ================
  1077. idExplodingBarrel::Event_Explode
  1078. ================
  1079. */
  1080. void idExplodingBarrel::Event_Explode() {
  1081. if ( state == NORMAL || state == BURNING ) {
  1082. state = BURNEXPIRED;
  1083. Killed( NULL, NULL, 0, vec3_zero, 0 );
  1084. }
  1085. }
  1086. /*
  1087. ================
  1088. idExplodingBarrel::Event_Respawn
  1089. ================
  1090. */
  1091. void idExplodingBarrel::Event_Respawn() {
  1092. int i;
  1093. int minRespawnDist = spawnArgs.GetInt( "respawn_range", "256" );
  1094. if ( minRespawnDist ) {
  1095. float minDist = -1;
  1096. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1097. if ( !gameLocal.entities[ i ] || !gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  1098. continue;
  1099. }
  1100. idVec3 v = gameLocal.entities[ i ]->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  1101. float dist = v.Length();
  1102. if ( minDist < 0 || dist < minDist ) {
  1103. minDist = dist;
  1104. }
  1105. }
  1106. if ( minDist < minRespawnDist ) {
  1107. PostEventSec( &EV_Respawn, spawnArgs.GetInt( "respawn_again", "10" ) );
  1108. return;
  1109. }
  1110. }
  1111. const char *temp = spawnArgs.GetString( "model" );
  1112. if ( temp && *temp ) {
  1113. SetModel( temp );
  1114. }
  1115. health = spawnArgs.GetInt( "health", "5" );
  1116. fl.takedamage = true;
  1117. physicsObj.SetOrigin( spawnOrigin );
  1118. physicsObj.SetAxis( spawnAxis );
  1119. physicsObj.SetContents( CONTENTS_SOLID );
  1120. physicsObj.DropToFloor();
  1121. state = NORMAL;
  1122. Show();
  1123. UpdateVisuals();
  1124. }
  1125. /*
  1126. ================
  1127. idMoveable::Event_Activate
  1128. ================
  1129. */
  1130. void idExplodingBarrel::Event_Activate( idEntity *activator ) {
  1131. Killed( activator, activator, 0, vec3_origin, 0 );
  1132. }
  1133. /*
  1134. ================
  1135. idMoveable::WriteToSnapshot
  1136. ================
  1137. */
  1138. void idExplodingBarrel::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1139. idMoveable::WriteToSnapshot( msg );
  1140. msg.WriteBits( IsHidden(), 1 );
  1141. }
  1142. /*
  1143. ================
  1144. idMoveable::ReadFromSnapshot
  1145. ================
  1146. */
  1147. void idExplodingBarrel::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1148. idMoveable::ReadFromSnapshot( msg );
  1149. if ( msg.ReadBits( 1 ) ) {
  1150. Hide();
  1151. } else {
  1152. Show();
  1153. }
  1154. }
  1155. /*
  1156. ================
  1157. idExplodingBarrel::ClientReceiveEvent
  1158. ================
  1159. */
  1160. bool idExplodingBarrel::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  1161. switch( event ) {
  1162. case EVENT_EXPLODE: {
  1163. if ( gameLocal.realClientTime - msg.ReadLong() < spawnArgs.GetInt( "explode_lapse", "1000" ) ) {
  1164. ExplodingEffects( );
  1165. }
  1166. return true;
  1167. }
  1168. default: {
  1169. return idBarrel::ClientReceiveEvent( event, time, msg );
  1170. }
  1171. }
  1172. return false;
  1173. }