Moveable.cpp 39 KB

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