Moveable.cpp 35 KB

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